/* components.css — the component kit (docs/34 §5) and the shell's layout
   (§6), in the look the owner chose: open text over thin lines. A group is
   separated by a hairline, never boxed; a control is a line, and the brass
   part of the line is its level; the one primary action of a screen is
   brass, the rest are quiet outlines or plain text. A filled ground is kept
   for what really floats — sheet, dialog, toast, menu — and nothing casts a
   shadow. Every component has its states here: default, hover, pressed,
   focus, disabled, busy; for a device tile also on / off / not answering /
   blocked on Shabbat / view-only. Phone first (375 px), then the rail at
   900 px. */

/* =====================================================================
   SHELL — the header, the view, the navigation. Phone first: a bar at the
   bottom, five words at most, each a 56 px target. From 900 px: a rail at
   the start of the line (the right, in Hebrew) that also lists the rooms.
   ===================================================================== */
.app {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}
.skip {
  position: absolute;
  inset-inline-start: var(--s-2);
  top: -100px;
  z-index: 80;
  padding: var(--s-2) var(--s-4);
  border-radius: var(--r-pill);
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: var(--fw-medium);
}
.skip:focus { top: var(--s-2); }

/* The header carries the page's own light, so it joins the ground with no
   edge and still covers what moves under it. */
.header {
  position: sticky;
  top: 0;
  z-index: 30;
  min-height: calc(var(--header-h) + var(--safe-t));
  padding-top: var(--safe-t);
  padding-inline: var(--s-2);
  background: var(--canvas) var(--page-glow);
  display: flex;
  align-items: center;
  gap: var(--s-1);
}
.header__title {
  flex: 1 1 auto;
  min-width: 0;
  padding-inline-start: var(--s-3);
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  font-weight: var(--fw-light);
  line-height: 1.2;
}
.header__name { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.header__sub {
  display: block;
  font-size: var(--fs-sm);
  font-weight: var(--fw-regular);
  line-height: 1.4;
  color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.header__actions { display: flex; align-items: center; gap: var(--s-1); flex: none; }

/* The connection: a quiet dot when all is well; the words when it is not.
   Cut off is a hollow ring: the shape says it, not the colour alone. */
.conn {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 32px;
  padding-inline: var(--s-2);
  margin-inline-end: var(--s-2);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  color: var(--ink-2);
  flex: none;
  max-width: 60%;
}
.conn__dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--ok);
  flex: none;
}
.conn__text { display: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.conn--off .conn__text, .conn--wait .conn__text { display: inline; }
.conn--off { color: var(--danger); }
.conn--off .conn__dot { background: transparent; border: 1.5px solid var(--danger); }
.conn--wait .conn__dot { background: var(--warn); animation: pulse 1.6s ease-in-out infinite; }
@keyframes pulse { 50% { opacity: 0.35; } }

.view {
  flex: 1 1 auto;
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding: var(--s-2) var(--gutter) calc(var(--nav-h) + var(--safe-b) + var(--s-6));
}

/* The bottom bar (phone): words only. The open tab is brass, with a short
   brass line on the bar's own hairline. */
.nav {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 30;
  height: calc(var(--nav-h) + var(--safe-b) + var(--line));
  padding-bottom: var(--safe-b);
  padding-inline: var(--s-2);
  background: var(--canvas);
  border-top: var(--line) solid var(--hairline);
  display: flex;
  align-items: stretch;
}
.nav__item {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding-inline: var(--s-1);
  color: var(--ink-2);
  font-size: var(--fs-sm);
  line-height: 1.2;
  text-decoration: none;
  transition: color var(--t-base) var(--ease);
  -webkit-tap-highlight-color: transparent;
}
.nav__item:hover { text-decoration: none; color: var(--ink); }
.nav__item:focus-visible { outline-offset: -6px; }
.nav__item .icon { display: none; }
.nav__pill {
  position: absolute;
  top: calc(var(--line) * -1);
  inset-inline: 30%;
  height: var(--line);
  background: var(--accent);
  opacity: 0;
  transition: opacity var(--t-base) var(--ease);
}
.nav__item[aria-current="page"] { color: var(--accent); font-weight: var(--fw-medium); }
.nav__item[aria-current="page"] .nav__pill { opacity: 1; }
.nav__label { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The rail (wide screens): hidden on a phone, at the side from 900 px. */
.rail {
  display: none;
  flex-direction: column;
  gap: 2px;
  width: var(--rail-w);
  flex: none;
  padding: var(--s-5) var(--s-4) var(--s-3);
  border-inline-end: var(--line) solid var(--hairline);
}
.rail__brand {
  display: flex; align-items: center; gap: var(--s-3);
  min-height: var(--tap);
  font-size: 0.9375rem;
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-brand);
  color: var(--ink);
  padding: 0 var(--s-3) var(--s-4);
  text-decoration: none;
}
.rail__brand:hover { text-decoration: none; }
.rail__logo { display: none; }
.rail__item {
  position: relative;
  display: flex; align-items: center; gap: var(--s-3);
  min-height: var(--tap);
  padding-inline: var(--s-3);
  color: var(--ink-2);
  text-decoration: none;
  transition: color var(--t-base) var(--ease);
}
.rail__item > .icon { display: none; }
.rail__item:hover { color: var(--ink); text-decoration: none; }
.rail__item[aria-current="page"] { color: var(--accent); font-weight: var(--fw-medium); }
.rail__item[aria-current="page"]::after {
  content: "";
  position: absolute;
  inset-inline-start: var(--s-3);
  bottom: 9px;
  width: 28px;
  height: var(--line);
  background: var(--accent);
}
.rail__label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail__item--room { min-height: var(--tap-sm); font-size: var(--fs-sm); }
.rail__item--room[aria-current="page"]::after { bottom: 8px; }
/* How many are on in the room: a lit mark and the number, as on the plan. */
.rail__lit {
  flex: none;
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--accent);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  font-variant-numeric: tabular-nums;
}
.rail__lit::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); }
.rail__scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; margin-inline: calc(var(--s-4) * -1); padding-inline: var(--s-4); }
.rail__foot { flex: none; margin-top: var(--s-2); padding-top: var(--s-2); border-top: var(--line) solid var(--hairline); }
.rail__section {
  font-size: var(--fs-xs);
  color: var(--accent);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-label);
  padding: var(--s-5) var(--s-3) var(--s-1);
}
@media (max-width: 899.98px) {
  .main { display: contents; }
}
@media (min-width: 900px) {
  .app { flex-direction: row; align-items: flex-start; }
  .rail { display: flex; position: sticky; top: 0; height: 100dvh; z-index: 31; }
  .main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; min-height: 100dvh; }
  .nav { display: none; }
  .header { padding-inline: var(--s-5); min-height: 88px; }
  .header__title { font-size: var(--fs-2xl); }
  .view { padding: var(--s-2) var(--s-7) var(--s-7); }
}

/* =====================================================================
   HOUSE PLAN — the home screen is the plan of the house (plan.js). Rows of
   rooms inside one thin wall; the rooms that are outside the house stand
   under it, inside the dashed line of the plot. A room is a button. The
   script gives a room one number — its weight in its row (flex-grow) — and
   everything else is here.
   In a room: its name; a warm pool of light while a lamp in it is on
   (data-glow 0–4, coming up and dying down in --t-glow); a mark per device
   — brass: active, a ring: not, a dashed line: not answering; the
   temperature and the cameras in the top corners; the door on its wall.
   States of a room: chosen (aria-pressed) · lit · about to change
   (--changing, for a plan that shows another hour).
   ===================================================================== */
.plan {
  --plan-gap: 6px;
  --plan-row: 104px;
  position: relative;
}
.plan--plot {
  padding: var(--s-3);
  border: var(--line) dashed var(--hairline-strong);
  border-radius: var(--r-lg);
}
.plan__house {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--plan-gap);
  padding: 3px;
  border: var(--line) solid var(--control-line);
  border-radius: calc(var(--r-sm) + 4px);
}
.plan__yard {
  --plan-row: 72px;
  display: flex;
  flex-direction: column;
  gap: var(--plan-gap);
  margin-top: var(--s-2);
}
.plan__row { display: flex; gap: var(--plan-gap); min-height: var(--plan-row); }
.plan__room {
  position: relative;
  isolation: isolate;
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  /* No side padding: a room's width is its weight's share exactly; the name
     and the marks keep their own distance from the walls. */
  padding: 24px 0 18px;
  border: var(--line) solid var(--control-line);
  border-radius: var(--r-sm);
  background: var(--room);
  color: var(--ink);
  text-align: center;
  transition: border-color var(--t-base) var(--ease);
  -webkit-tap-highlight-color: transparent;
}
/* The light of the room. Words stand in it: everything written in a room is
   --ink (docs/34 §3.4). */
.plan__room::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: var(--glow-room);
  opacity: 0;
  transition: opacity var(--t-glow) var(--ease);
  pointer-events: none;
}
.plan__room[data-glow="1"]::before { opacity: 0.6; }
.plan__room[data-glow="2"]::before { opacity: 0.76; }
.plan__room[data-glow="3"]::before { opacity: 0.9; }
.plan__room[data-glow="4"]::before { opacity: 1; }
.plan__room:hover { border-color: var(--ink-2); }
.plan__room:active { background: var(--wash); }
.plan__room[aria-pressed="true"] { border-color: var(--accent); outline: var(--line) solid var(--accent); outline-offset: -2px; }
/* The rooms touch: the keyboard's ring is drawn inside the room. */
.plan__room:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -4px; }
.plan__room--out { border-style: dashed; border-color: transparent; background: none; }
.plan__room--out:hover { border-color: var(--hairline-strong); }
.plan__room--out[aria-pressed="true"] { border-style: solid; border-color: var(--accent); }
.plan__room--changing { border-style: dashed; border-color: var(--accent); }
.plan__name {
  max-width: 100%;
  padding-inline: var(--s-2);
  font-size: var(--fs-md);
  line-height: 1.3;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.plan__marks { display: flex; align-items: center; justify-content: center; gap: 5px; min-height: 12px; max-width: 100%; padding-inline: var(--s-2); }
.plan__mark {
  width: 7px; height: 7px;
  flex: none;
  border-radius: 50%;
  border: var(--line) solid var(--ink-3);
  transition: background var(--t-slow) var(--ease), border-color var(--t-slow) var(--ease);
}
.plan__mark--on { background: var(--accent); border-color: var(--accent); }
.plan__mark--gone { width: 9px; height: 0; border-radius: 0; border: 0; border-top: var(--line) dashed var(--offline); }
.plan__more { font-size: var(--fs-xs); line-height: 1; font-variant-numeric: tabular-nums; }
.plan__temp { position: absolute; top: 6px; inset-inline-end: 9px; font-size: var(--fs-sm); line-height: 1.2; font-variant-numeric: tabular-nums; }
.plan__cams { position: absolute; top: 6px; inset-inline-start: 9px; display: flex; align-items: center; gap: 4px; color: var(--ink-3); }
.plan__cams .plan__more { color: var(--ink); }
.plan__cam { position: relative; display: block; }
.plan__cam .icon { width: 17px; height: 17px; }
.plan__cam--gone { color: var(--offline); }
.plan__cam--gone::after {
  content: "";
  position: absolute;
  inset-inline: -2px;
  top: 50%;
  height: var(--line);
  background: var(--ink-2);
  transform: rotate(-38deg);
}
/* The door: a thicker piece of the wall between its two jambs, and a lock
   mark in the corner of the room. It stands on an outer wall of its room —
   the bottom one, a side one or the top one — and on the room's own bottom
   wall when the room has none. Released, the leaf turns brass and swings
   open: out of the house on the bottom wall, into the room on the others. */
.plan { --turn: -1; }
[dir="rtl"] .plan { --turn: 1; }
.plan__door {
  position: absolute;
  inset-inline-end: 30px;
  width: 28px; height: 3px;
  background: var(--canvas);
  pointer-events: none;
}
.plan__door--bottom { bottom: -6px; }
.plan__door--top { top: -6px; }
.plan__door--inner { bottom: -2px; }
.plan__door--end, .plan__door--start { bottom: 28px; width: 3px; height: 28px; }
.plan__door--end { inset-inline-end: -6px; }
.plan__door--start { inset-inline: -6px auto; }
.plan__door--none { display: none; }
.plan__door::before, .plan__door::after {
  content: "";
  position: absolute;
  top: -3px;
  width: var(--line); height: 9px;
  background: var(--control-line);
}
.plan__door::before { inset-inline-start: -1px; }
.plan__door::after { inset-inline-end: -1px; }
:is(.plan__door--end, .plan__door--start)::before, :is(.plan__door--end, .plan__door--start)::after { inset-inline: -3px auto; width: 9px; height: var(--line); }
:is(.plan__door--end, .plan__door--start)::before { top: -1px; }
:is(.plan__door--end, .plan__door--start)::after { top: auto; bottom: -1px; }
/* The leaf turns on its hinge: the end nearer the corner of the room; on a
   side wall, the lower end. */
.plan__leaf {
  position: absolute;
  inset: 0;
  border-radius: 2px;
  background: var(--ink-2);
  transform-origin: calc(100% - 1.5px) 50%;
  transition: transform var(--t-glow) var(--ease), background var(--t-base) var(--ease);
}
[dir="rtl"] .plan__leaf { transform-origin: 1.5px 50%; }
:is(.plan__door--end, .plan__door--start) .plan__leaf, [dir="rtl"] :is(.plan__door--end, .plan__door--start) .plan__leaf { transform-origin: 50% calc(100% - 1.5px); }
/* The way the leaf went: a dashed quarter of a circle about the hinge. */
.plan__swing {
  position: absolute;
  inset-inline-end: 1px;
  width: 28px; height: 28px;
  border: 0 dashed var(--accent);
  opacity: 0;
  transition: opacity var(--t-slow) var(--ease);
}
:is(.plan__door--bottom, .plan__door--top) .plan__swing { top: 1px; border-block-end-width: var(--line); border-inline-start-width: var(--line); border-end-start-radius: 28px; }
:is(.plan__door--inner, .plan__door--end) .plan__swing { bottom: 1px; border-block-start-width: var(--line); border-inline-start-width: var(--line); border-start-start-radius: 28px; }
.plan__door--start .plan__swing { bottom: 1px; inset-inline: 1px auto; border-block-start-width: var(--line); border-inline-end-width: var(--line); border-start-end-radius: 28px; }
.plan__door--open .plan__leaf { background: var(--accent); }
.plan__door--open .plan__swing { opacity: 1; }
.plan__door--open:is(.plan__door--bottom, .plan__door--top, .plan__door--end) .plan__leaf { transform: rotate(calc(var(--turn) * 90deg)); }
.plan__door--open:is(.plan__door--inner, .plan__door--start) .plan__leaf { transform: rotate(calc(var(--turn) * -90deg)); }
.plan__door--gone .plan__leaf { background: repeating-linear-gradient(to right, var(--offline) 0 4px, transparent 4px 7px); }
:is(.plan__door--end, .plan__door--start).plan__door--gone .plan__leaf { background: repeating-linear-gradient(to bottom, var(--offline) 0 4px, transparent 4px 7px); }
.plan__lock { position: absolute; bottom: 5px; inset-inline-end: 8px; color: var(--ink-3); transition: color var(--t-base) var(--ease); }
.plan__lock .icon { width: 15px; height: 15px; }
.plan__lock--start { inset-inline: 8px auto; }
.plan__lock--open { color: var(--accent); }
.plan__lock--gone { color: var(--offline); }
.plan--skel .plan__room { cursor: default; }
/* The door of the house when it is in no room: on the bottom wall of the
   house, its lock mark outside under it. */
.plan--door .plan__house { margin-bottom: 26px; }
.plan__door--house { bottom: -3px; inset-inline-end: 34px; }
.plan__lock--house { bottom: -25px; inset-inline-end: 40px; }
/* The cameras in no room: marks outside the wall, each with its name. */
.plan__outside { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: center; gap: var(--s-2) var(--s-5); margin-top: var(--s-3); }
.plan__ocam { display: flex; flex-direction: column; align-items: center; gap: 2px; max-width: 9rem; text-align: center; font-size: var(--fs-xs); line-height: 1.3; color: var(--ink-2); }
.plan__ocam .plan__cam { color: var(--ink-3); }
.plan__ocam-name { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.plan__ocam-state:empty { display: none; }
@media (min-width: 600px) { .plan { --plan-row: 124px; } }
@media (min-width: 900px) {
  .plan { --plan-row: 136px; }
  .plan--plot { padding: var(--s-4) var(--s-5); }
  .plan__yard { --plan-row: 84px; margin-top: var(--s-3); }
  .plan__name { font-size: 1.0625rem; }
}

/* =====================================================================
   HOME — what stands around the plan: the date, the status strip, the room
   that is open (a panel beside the plan on a wide screen), the scenes, the
   lines of today. .home__slot is the place of what is not built yet ("דבר
   אל הבית" above the plan, "ציר היום" under it): empty, it takes no room.
   ===================================================================== */
.view:has(> .home) { max-width: 1480px; }
.home__date { margin: 0 0 var(--s-1); font-size: var(--fs-sm); line-height: 1.5; color: var(--ink-2); }
.home__hint { margin: 0 0 var(--s-2); font-size: var(--fs-sm); color: var(--ink-2); }
.home__slot:empty { display: none; }
.home__rest > section:not(:empty) { margin-top: var(--s-4); }
/* The devices that are in no room: one quiet line under the plan. */
.home__loose {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-height: var(--tap-sm);
  margin: var(--s-2) 0 0;
  font-size: var(--fs-sm);
  color: var(--ink-2);
  text-decoration: none;
  transition: color var(--t-base) var(--ease);
}
a.home__loose:hover { color: var(--accent); text-decoration: none; }
.home__loose .icon { color: var(--ink-3); }
/* While a room's sheet is open on a phone, the page can move far enough for
   the lowest room to stand above the sheet. */
.home--sheet { padding-bottom: 54dvh; }
@media (min-width: 720px) { .home--sheet { padding-bottom: 0; } }

/* The status strip: a small quiet word over its value. No box, no icon. */
.facts { display: flex; flex-wrap: wrap; gap: 0 var(--s-5); margin-bottom: var(--s-2); }
.fact {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: var(--tap-sm);
  max-width: 100%;
  min-height: var(--tap);
  text-align: start;
  color: var(--ink);
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.fact:hover { text-decoration: none; }
.fact__k { font-size: var(--fs-xs); line-height: 1.3; color: var(--ink-2); }
.fact__v { font-size: var(--fs-md); line-height: 1.3; font-variant-numeric: tabular-nums; transition: color var(--t-slow) var(--ease); }
a.fact:hover .fact__v, button.fact:hover .fact__v { color: var(--accent); }
.fact--hot .fact__v { color: var(--accent); font-weight: var(--fw-medium); }
/* Not answering: its name in weight — never red, never "off". */
.fact--gone .fact__v { font-weight: var(--fw-medium); }

/* The clock in the header of the home screen, on a wide screen. */
.clock { display: none; }

/* The open room beside the plan (a wide screen). On a phone it is a sheet. */
.roompanel { display: none; }
.roompanel__sub { margin: 2px 0 var(--s-2); font-size: 0.9375rem; color: var(--ink-2); }
/* In the sheet the line stands right under the room's name, as beside the plan. */
.sheet__body > .roompanel__sub:first-child { margin-top: calc(-1 * var(--s-2)); }

/* A scene on the home screen: its name over a line, a quiet word under it.
   One tap runs it. */
.scene-list { display: grid; gap: 0 var(--s-5); grid-template-columns: repeat(2, minmax(0, 1fr)); }
.scene-line {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
  min-height: 64px;
  padding: var(--s-2) 0;
  border-bottom: var(--line) solid var(--hairline);
  text-align: start;
  color: var(--ink);
  transition: border-color var(--t-slow) var(--ease), background var(--t-fast);
  -webkit-tap-highlight-color: transparent;
}
.scene-line:hover { border-bottom-color: var(--accent); }
.scene-line:active { background: var(--wash); }
.scene-line:disabled { opacity: 0.45; }
.scene-line[aria-busy="true"] { border-bottom-color: var(--accent); }
.scene-line__name {
  max-width: 100%;
  font-size: var(--fs-lg);
  line-height: 1.3;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.scene-line__sub { max-width: 100%; font-size: var(--fs-sm); color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* "היום בבית": the hour in brass, what happened beside it. A line opens
   "למה זה קרה?". */
.day { display: flex; flex-direction: column; }
.day__item {
  display: flex;
  align-items: baseline;
  gap: var(--s-4);
  width: 100%;
  min-height: var(--tap-sm);
  padding: var(--s-2) 0;
  text-align: start;
  color: var(--ink);
  font-size: 0.9375rem;
  -webkit-tap-highlight-color: transparent;
}
.day__time { flex: none; min-width: 3rem; font-size: 1.125rem; line-height: 1.3; color: var(--accent); font-variant-numeric: tabular-nums; }
.day__text { flex: 1 1 auto; min-width: 0; line-height: 1.45; transition: color var(--t-base) var(--ease); }
.day__item:hover .day__text { color: var(--accent); }
.day__item--refused .day__text { color: var(--danger); }

@media (min-width: 900px) {
  .facts { gap: 0 var(--s-7); margin-bottom: var(--s-4); }
  .clock {
    display: block;
    padding-inline: var(--s-4) var(--s-2);
    font-size: var(--fs-3xl);
    font-weight: var(--fw-light);
    line-height: 1;
    letter-spacing: 0.02em;
    font-variant-numeric: tabular-nums;
  }
  .scene-list { grid-template-columns: repeat(auto-fill, minmax(168px, 1fr)); }
  .day { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 0 var(--s-6); }
}
@media (min-width: 1180px) {
  .home__grid { display: grid; grid-template-columns: minmax(0, 1fr) 340px; column-gap: var(--s-7); align-items: start; }
  .home__top { grid-column: 1; grid-row: 1; min-width: 0; }
  .home__rest { grid-column: 1; grid-row: 2; min-width: 0; }
  .roompanel {
    display: block;
    grid-column: 2;
    grid-row: 1 / span 2;
    position: sticky;
    top: calc(88px + var(--s-2));
    max-height: calc(100dvh - 88px - var(--s-5));
    overflow-y: auto;
    padding-block: 6px var(--s-4);
    padding-inline: var(--s-6) 6px;
    border-inline-start: var(--line) solid var(--hairline);
  }
  .roompanel__title { font-size: var(--fs-3xl); font-weight: var(--fw-light); line-height: 1.1; }
  .roompanel__title--md { font-size: var(--fs-2xl); }
  .roompanel__title--sm { font-size: var(--fs-xl); line-height: 1.2; }
}

/* =====================================================================
   BUTTONS — one primary in brass on a screen; the rest are a quiet outline
   or plain text. Every button is a pill.
   ===================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: var(--tap);
  padding: 0 var(--s-5);
  border: var(--line) solid var(--control-line);
  border-radius: var(--r-pill);
  font-weight: var(--fw-regular);
  font-size: var(--fs-md);
  line-height: 1.2;
  background: transparent;
  color: var(--ink);
  transition: border-color var(--t-base) var(--ease), color var(--t-base) var(--ease), background var(--t-base) var(--ease), opacity var(--t-fast);
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
  white-space: nowrap;
  text-decoration: none;
}
.btn:hover { border-color: var(--ink); text-decoration: none; }
.btn:active { background: var(--wash-2); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: 0.45; pointer-events: none; }
.btn--primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); font-weight: var(--fw-medium); }
.btn--primary:hover, .btn--primary:active { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn--secondary { border-color: var(--accent); color: var(--accent); }
.btn--secondary:hover { border-color: var(--accent-hover); color: var(--accent-hover); }
.btn--ghost { border-color: transparent; color: var(--ink-2); }
.btn--ghost:hover { border-color: transparent; background: var(--wash); color: var(--ink); }
.btn--danger { border-color: var(--danger); color: var(--danger); font-weight: var(--fw-medium); }
.btn--danger:hover { border-color: var(--danger); background: var(--wash); }
.btn--danger-soft { border-color: transparent; color: var(--danger); }
.btn--danger-soft:hover { border-color: var(--danger); }
/* What is on: the outline is brass and the light shows through it. */
.btn--on { border-color: var(--accent); color: var(--ink); background: var(--glow); }
.btn--on .icon { color: var(--accent); }
.btn--on:hover { border-color: var(--accent-hover); }
.btn--sm { min-height: var(--tap-sm); padding-inline: var(--s-4); font-size: var(--fs-sm); }
.btn--lg { min-height: 56px; padding-inline: var(--s-6); font-size: 1.0625rem; }
.btn--block { width: 100%; }
.btn--pill { border-radius: var(--r-pill); }
.btn--icon { width: var(--tap); padding: 0; flex: none; }
.btn--icon.btn--sm { width: var(--tap-sm); }
.btn--icon.btn--lg { width: 64px; min-height: 64px; }
/* Busy: a spinner takes the icon's place. */
.btn[aria-busy="true"] { pointer-events: none; }
.btn[aria-busy="true"]::before {
  content: "";
  width: 16px; height: 16px;
  border-radius: 50%;
  border: 1.5px solid color-mix(in srgb, currentColor 30%, transparent);
  border-top-color: currentColor;
  animation: spin 0.8s linear infinite;
  flex: none;
}
.btn-row { display: flex; gap: var(--s-3); }
.btn-row > .btn { flex: 1 1 0; min-width: 0; padding-inline: var(--s-2); }
.btn-row--wrap { flex-wrap: wrap; }
.btn-row--wrap > .btn { flex: 1 1 auto; padding-inline: var(--s-4); }
.btn-row--end { justify-content: flex-end; }
.btn-row--end > .btn { flex: none; padding-inline: var(--s-5); }

@keyframes spin { to { transform: rotate(360deg); } }

.spinner {
  width: 22px; height: 22px;
  border-radius: 50%;
  border: 1.5px solid var(--hairline-strong);
  border-top-color: var(--accent);
  animation: spin 0.8s linear infinite;
  flex: none;
}
.spinner--lg { width: 36px; height: 36px; }

/* =====================================================================
   GROUPS: card, block, note. A card is not a box: it is a group that opens
   with a hairline. Only .card--raised is filled — a layer that floats.
   ===================================================================== */
.card {
  border-top: var(--line) solid var(--hairline);
  padding: var(--s-4) 0;
}
.card--raised { background: var(--raised); border: var(--line) solid var(--float-line); border-radius: var(--r-lg); padding: var(--s-4) var(--s-5); }
.card--inset { border: var(--line) solid var(--hairline-strong); border-radius: var(--r-md); padding: var(--s-4); }
.card--flat { border-top-color: var(--hairline); }
.card--tap { display: block; width: 100%; text-align: start; color: inherit; cursor: pointer; transition: border-color var(--t-base) var(--ease), background var(--t-fast); }
.card--tap:hover { border-top-color: var(--accent); }
.card--tap:active { background: var(--wash); }
.card__title { font-family: var(--font-display); font-size: var(--fs-lg); font-weight: var(--fw-regular); line-height: 1.3; }
.card__sub { color: var(--ink-2); font-size: var(--fs-sm); }
.card--pad-0 { padding: 0; overflow: hidden; }

/* =====================================================================
   DEVICE TILE — the heart of the product. Open text over a line: the name,
   the state, and under them the line. Two targets, no hidden gesture: the
   round button does the everyday thing in one tap; the rest of the tile
   opens the device sheet.
   States: --on (the line turns brass, a warm light rises from it, the
   button fills) · off · --gone (not answering: a dashed line) · --blocked
   (Shabbat: a note, no button) · view only (a note, no button) · --busy
   (a command on its way) · --pending (shown before the bridge confirmed).
   ===================================================================== */
.tile {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  border-bottom: var(--line) solid var(--hairline);
  color: var(--ink);
  transition: border-color var(--t-base) var(--ease);
}
.tile:hover { border-bottom-color: var(--hairline-strong); }
.tile:has(.tile__main:active) { background: var(--wash); }
.tile__main {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  width: 100%;
  min-height: 124px;
  padding: var(--s-3) 0;
  text-align: start;
  color: inherit;
  -webkit-tap-highlight-color: transparent;
}
.tile__main:focus-visible { outline-offset: 2px; }
.tile__icon {
  height: var(--tap);
  display: flex;
  align-items: center;
  color: var(--ink-3);
  margin-bottom: auto;
  transition: color var(--t-base) var(--ease);
}
.tile__name {
  margin-top: var(--s-2);
  font-size: 1.0625rem;
  line-height: 1.3;
  max-width: 100%;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.tile__state {
  font-size: var(--fs-sm);
  color: var(--ink-2);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  transition: color var(--t-base) var(--ease);
}
.tile__room { font-size: var(--fs-xs); color: var(--ink-2); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The quick action: a real, round button at the end of the first line.
   While its device is on it is filled, as a switch that is on. */
.tile__quick {
  position: absolute;
  top: var(--s-3);
  inset-inline-end: 0;
  width: var(--tap); height: var(--tap);
  border-radius: 50%;
  border: var(--line) solid var(--control-line);
  display: grid;
  place-items: center;
  color: var(--ink-2);
  transition: background var(--t-base) var(--ease), border-color var(--t-base) var(--ease), color var(--t-base) var(--ease);
  -webkit-tap-highlight-color: transparent;
}
.tile__quick:hover { border-color: var(--accent); color: var(--accent); }
.tile__quick:active { background: var(--wash-2); }
.tile__quick[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.tile__quick[aria-pressed="true"]:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: var(--accent-ink); }
.tile__foot { position: relative; z-index: 1; padding: 0; }
.tile__note {
  display: flex;
  align-items: center;
  gap: var(--s-1);
  padding: 0 0 var(--s-3);
  font-size: var(--fs-xs);
  color: var(--ink-2);
  line-height: 1.3;
}
.tile__note .icon { color: var(--accent); }
.tile:has(.tile__foot) .tile__main, .tile:has(.tile__note) .tile__main { min-height: 108px; padding-bottom: var(--s-2); }
/* With a slider on the tile, the slider's line is the tile's line. */
.tile:has(.tile__foot) { border-bottom-color: transparent; }

.tile--on { border-bottom-color: var(--accent); background: var(--glow); }
.tile--on:hover { border-bottom-color: var(--accent); }
.tile--on .tile__icon { color: var(--accent); }
.tile--on .tile__state { color: var(--ink); }

.tile--gone { border-bottom-style: dashed; border-bottom-color: var(--offline); }
.tile--gone:hover { border-bottom-color: var(--offline); }
.tile--gone .tile__icon { color: var(--offline); }
.tile--gone .tile__name { color: var(--ink-2); }
.tile--gone .tile__state { color: var(--ink); font-weight: var(--fw-medium); }

.tile--pending .tile__state { opacity: 0.6; }

:is(.tile--busy, .ctl--busy) .tile__quick { pointer-events: none; }
:is(.tile--busy, .ctl--busy) .tile__quick .icon { opacity: 0; }
:is(.tile--busy, .ctl--busy) .tile__quick::after {
  content: "";
  position: absolute;
  inset: 0; margin: auto;
  width: 18px; height: 18px;
  border-radius: 50%;
  border: 1.5px solid color-mix(in srgb, currentColor 30%, transparent);
  border-top-color: currentColor;
  animation: spin 0.8s linear infinite;
}

/* A sensor: the value is the headline — a large, light number — and the
   name is its caption. */
.tile--sensor .tile__main { min-height: 108px; }
.tile__value {
  margin-top: var(--s-1);
  font-family: var(--font-display);
  font-size: var(--fs-2xl);
  font-weight: var(--fw-light);
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
  direction: ltr;
  unicode-bidi: isolate;
  max-width: 100%;
  white-space: nowrap;
}
.tile__value--word { font-size: var(--fs-lg); font-weight: var(--fw-regular); line-height: 1.3; white-space: normal; }
/* At a larger text size a long reading still fits its tile on one line. */
:root[data-size="125"] .tile__value:not(.tile__value--word), :root[data-size="150"] .tile__value:not(.tile__value--word) { font-size: var(--fs-xl); }
.tile__value .unit { font-size: var(--fs-sm); color: var(--ink-2); font-weight: var(--fw-regular); margin-inline-start: 3px; }
.tile--sensor .tile__name { margin-top: 0; font-size: var(--fs-sm); color: var(--ink-2); -webkit-line-clamp: 1; }

/* The grid of tiles: two across at 375, more on wider screens. The rows
   touch: each tile ends in its own line. */
.tiles {
  display: grid;
  gap: 0 var(--s-5);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 560px) { .tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 900px) { .tiles { gap: 0 var(--s-6); grid-template-columns: repeat(auto-fill, minmax(184px, 1fr)); } }

/* =====================================================================
   ROOM CONTROL — a device among the controls of a room of the plan
   (plan.js). One row: the name and the state at the start of the line, the
   device's one number in large light figures, the round switch at its end
   (.tile__quick); under them the line that is the level, or minus and plus
   with the line between them. Parts of the kit, put together as the
   reference draws them.
   States: --on (the number brass, the state ivory) · off · --gone (no
   control, a dashed line) · blocked on Shabbat / view only (a note, no
   control) · --busy. --sm: one of "עוד בחדר".
   ===================================================================== */
.ctl { padding-top: var(--s-1); }
.ctl__row { display: flex; align-items: center; gap: var(--s-3); min-height: 60px; }
.ctl__who {
  flex: 1 1 auto;
  min-width: 0;
  min-height: var(--tap);
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: start;
  color: var(--ink);
  -webkit-tap-highlight-color: transparent;
}
.ctl__name {
  max-width: 100%;
  font-size: var(--fs-lg);
  line-height: 1.3;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  transition: color var(--t-base) var(--ease);
}
.ctl__who:hover .ctl__name { color: var(--accent); }
.ctl__state { font-size: var(--fs-sm); line-height: 1.4; color: var(--ink-2); font-variant-numeric: tabular-nums; transition: color var(--t-slow) var(--ease); }
.ctl__num {
  flex: none;
  font-size: var(--fs-2xl);
  font-weight: var(--fw-light);
  line-height: 1;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  transition: color var(--t-slow) var(--ease);
}
.ctl__num:empty { display: none; }
.ctl--on .ctl__num { color: var(--accent); }
.ctl--on .ctl__state { color: var(--ink); }
.ctl .tile__quick { position: relative; top: auto; inset-inline-end: auto; flex: none; }
.ctl__bar { display: flex; align-items: center; gap: var(--s-3); height: var(--tap-sm); }
.ctl__bar > .slider { flex: 1 1 auto; min-width: 0; }
.ctl__rule { flex: 1 1 auto; height: var(--line); background: var(--hairline); }
/* The bare line stands in the middle of its bar, not at its foot. */
.ctl .slider--compact::before { bottom: 21px; }
.ctl .slider--compact .slider__fill { bottom: 20px; }
.ctl .slider--compact::after { inset: 0; }
.ctl .slider[aria-disabled="true"] .slider__fill { width: 0; }
.ctl__bar > .stepper { flex: 1 1 auto; display: grid; grid-template-columns: var(--tap-sm) minmax(0, 1fr) var(--tap-sm); min-height: 0; }
.ctl--gone .ctl__name { color: var(--ink-2); }
.ctl--gone .ctl__state { color: var(--ink); font-weight: var(--fw-medium); }
.ctl--gone .ctl__rule { height: 0; background: none; border-top: var(--line) dashed var(--offline); }
.ctl__note { display: flex; align-items: center; gap: var(--s-1); padding-bottom: var(--s-2); font-size: var(--fs-xs); line-height: 1.3; color: var(--ink-2); }
.ctl__note .icon { color: var(--accent); }
.ctl--sm .ctl__name { font-size: 1.0625rem; }
.ctl--sm .ctl__num { font-size: var(--fs-xl); }
/* "עוד בחדר · N", and the way to the room's own screen. */
.roomctl__more {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: var(--tap-sm);
  margin-top: var(--s-2);
  font-size: var(--fs-sm);
  color: var(--accent);
  transition: color var(--t-base) var(--ease);
}
.roomctl__more:hover { color: var(--accent-hover); }
.roomctl__more .icon { transition: transform var(--t-slow) var(--ease); }
.roomctl__more[aria-expanded="true"] .icon { transform: rotate(180deg); }
.roomctl__link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  min-height: var(--tap);
  margin-top: var(--s-3);
  border-top: var(--line) solid var(--hairline);
  font-size: var(--fs-sm);
  color: var(--ink-2);
  text-decoration: none;
  transition: color var(--t-base) var(--ease);
}
.roomctl__link:hover { color: var(--accent); text-decoration: none; }
.roomctl__link .icon { color: var(--ink-3); }

/* =====================================================================
   ROOM CARD — the primary navigation: a row that ends in a line. A room
   where something is on is lit from below, as on the plan of the house.
   ===================================================================== */
.room-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--s-3);
  min-height: 88px;
  padding: var(--s-3) 0;
  border-bottom: var(--line) solid var(--hairline);
  text-align: start;
  width: 100%;
  color: var(--ink);
  text-decoration: none;
  transition: border-color var(--t-base) var(--ease);
  -webkit-tap-highlight-color: transparent;
}
.room-card:hover { border-bottom-color: var(--accent); text-decoration: none; }
.room-card:active { background: var(--wash); }
.room-card__icon {
  width: 40px; height: 40px;
  display: grid; place-items: center start;
  color: var(--ink-3);
}
.room-card--lit { background: var(--glow); }
.room-card--lit:active { background: var(--glow); }
.room-card--lit .room-card__icon { color: var(--accent); }
.room-card__text { display: block; min-width: 0; }
.room-card__name { font-family: var(--font-display); font-size: var(--fs-lg); font-weight: var(--fw-regular); line-height: 1.25; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.room-card__meta { display: block; font-size: var(--fs-sm); color: var(--ink-2); margin-top: 2px; }
.room-card__meta .lit { color: var(--ink); font-weight: var(--fw-medium); }
.room-card__chev { color: var(--ink-3); }
.rooms { display: grid; gap: 0 var(--s-6); grid-template-columns: 1fr; }
@media (min-width: 560px) { .rooms { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .rooms { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* A row of outlines that scrolls sideways (the place of a row of chips
   before it is read), and a scroller cuts what leaves it: the padding is the
   room the keyboard's ring needs. */
.scenes {
  display: flex;
  gap: var(--s-2);
  overflow-x: auto;
  padding: 6px;
  margin: -2px -6px 2px;
  scrollbar-width: none;
  scroll-snap-type: x proximity;
  scroll-padding-inline: 6px;
}
.scenes::-webkit-scrollbar { display: none; }

/* =====================================================================
   SWITCH — a thin track and a knob. Off: a hollow knob at the start of the
   line. On: the track's line turns brass and a full brass knob stands at
   the end — the place and the fill say it, not the colour alone.
   ===================================================================== */
.switch {
  appearance: none;
  -webkit-appearance: none;
  width: 52px; height: 30px;
  border: var(--line) solid var(--control-line);
  border-radius: var(--r-pill);
  background: transparent;
  position: relative;
  flex: none;
  cursor: pointer;
  transition: border-color var(--t-base) var(--ease), background var(--t-base) var(--ease);
  margin: 0;
}
/* The finger's target is larger than the switch: 50 px high. */
.switch::before { content: ""; position: absolute; inset: -11px -5px; }
.switch::after {
  content: "";
  position: absolute;
  top: 4px;
  inset-inline-start: 4px;
  width: 20px; height: 20px;
  border-radius: 50%;
  border: var(--line) solid var(--control-line);
  transition: inset-inline-start var(--t-base) var(--ease), background var(--t-base) var(--ease), border-color var(--t-base) var(--ease);
}
.switch:checked { border-color: var(--accent); background: var(--accent-soft); }
.switch:checked::after { inset-inline-start: 26px; background: var(--accent); border-color: var(--accent); }
.switch--on:checked { border-color: var(--accent); }
.switch:disabled { opacity: 0.45; cursor: default; }
.switch--lg { width: 64px; height: 36px; }
.switch--lg::after { width: 26px; height: 26px; }
.switch--lg:checked::after { inset-inline-start: 32px; }

/* =====================================================================
   SLIDER — brightness, position, volume. The line is the level: brass from
   the start of the line up to the value, a thin line beyond it. Min at the
   inline start (right), max at the inline end: the natural Hebrew
   expectation (docs/05 §2). Above the line, the word and — at the end of
   the line — the number. Keyboard: the start-side arrow lowers, the
   end-side arrow raises; Home/End; PageUp/PageDown.
   The knob stays inside the line's ends (ui.js maps a press the same way):
   --frac is the value as a fraction, --value as a percentage.
   ===================================================================== */
.slider {
  --knob: 18px;
  --halo: 6px;
  position: relative;
  height: 64px;
  cursor: pointer;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.slider::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: 21px;
  height: var(--line);
  background: var(--control-line);
}
.slider__fill {
  position: absolute;
  inset-inline-start: 0;
  bottom: 20px;
  height: var(--line-level);
  width: calc(var(--knob) / 2 + (100% - var(--knob)) * var(--frac, 0.5));
  background: var(--accent);
  transition: width var(--t-fast) linear;
}
/* The knob, with its halo: a ring of the same brass, not a shadow. */
.slider__fill::after {
  content: "";
  position: absolute;
  top: 50%;
  inset-inline-end: calc((var(--knob) / 2 + var(--halo)) * -1);
  width: calc(var(--knob) + var(--halo) * 2);
  height: calc(var(--knob) + var(--halo) * 2);
  margin-top: calc((var(--knob) / 2 + var(--halo)) * -1);
  border-radius: 50%;
  border: var(--halo) solid var(--accent-soft);
  background: var(--accent);
  background-clip: padding-box;
}
.slider__label {
  position: absolute;
  inset-inline: 0;
  top: 0;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  font-size: var(--fs-sm);
  line-height: 1.3;
  pointer-events: none;
  color: var(--ink-2);
}
.slider__label .num { font-variant-numeric: tabular-nums; color: var(--ink); }
.slider--on .slider__label .num { color: var(--accent); }
.slider--dragging .slider__fill { transition: none; }
.slider[aria-disabled="true"] { opacity: 0.45; cursor: default; }
.slider[aria-disabled="true"] .slider__fill::after { display: none; }
/* The large one, in a device sheet: the number is the headline. */
.slider--lg { height: 96px; }
.slider--lg .slider__label { font-size: var(--fs-md); align-items: flex-end; }
.slider--lg .slider__label .num { font-size: var(--fs-2xl); font-weight: var(--fw-light); line-height: 1.1; color: var(--ink-2); }
.slider--lg.slider--on .slider__label .num { color: var(--accent); }
/* A vertical slider: max at the top, the words beside the line. */
.slider--v {
  height: 220px;
  width: 132px;
  margin-inline: auto;
}
.slider--v::before {
  inset-inline: auto 29px;
  inset-block: 0;
  width: var(--line);
  height: auto;
}
.slider--v .slider__fill {
  inset-inline: auto 28px;
  bottom: 0;
  width: var(--line-level);
  height: calc(var(--knob) / 2 + (100% - var(--knob)) * var(--frac, 0.5));
  transition: height var(--t-fast) linear;
}
.slider--v .slider__fill::after {
  top: 0;
  inset-inline-end: 50%;
  margin-top: calc((var(--knob) / 2 + var(--halo)) * -1);
  margin-inline-end: calc((var(--knob) / 2 + var(--halo)) * -1);
}
.slider--v .slider__label { inset-inline: 0 64px; flex-direction: column; justify-content: flex-start; align-items: flex-start; gap: 0; }
.slider--v .slider__label .num { font-size: var(--fs-xl); font-weight: var(--fw-light); }

/* =====================================================================
   STEPPER — the climate target: − value +. Two round outlines and a large
   light number between them.
   ===================================================================== */
.stepper {
  display: inline-grid;
  grid-template-columns: var(--tap-sm) 1fr var(--tap-sm);
  align-items: center;
  gap: var(--s-3);
  min-height: var(--tap);
}
.stepper__btn {
  width: var(--tap-sm); height: var(--tap-sm);
  border-radius: 50%;
  border: var(--line) solid var(--control-line);
  display: grid; place-items: center;
  color: var(--ink);
  transition: border-color var(--t-base) var(--ease), color var(--t-base) var(--ease), background var(--t-fast);
}
.stepper__btn:hover { border-color: var(--accent); color: var(--accent); }
.stepper__btn:active { background: var(--wash-2); }
.stepper__btn:disabled { opacity: 0.35; }
.stepper__value {
  text-align: center;
  font-family: var(--font-display);
  font-weight: var(--fw-light);
  font-size: var(--fs-xl);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  direction: ltr;
  unicode-bidi: isolate;
  padding-inline: var(--s-1);
  min-width: 64px;
}
.stepper--lg { grid-template-columns: 56px 1fr 56px; min-height: 64px; }
.stepper--lg .stepper__btn { width: 56px; height: 56px; }
.stepper--lg .stepper__value { font-size: var(--fs-3xl); }

/* =====================================================================
   CHIPS and segmented control. What is chosen is filled brass; the rest
   are quiet outlines.
   ===================================================================== */
.chips { display: flex; gap: var(--s-2); flex-wrap: wrap; }
.chips--scroll { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; padding: 6px; margin: -6px; }
.chips--scroll::-webkit-scrollbar { display: none; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  min-height: var(--tap-sm);
  padding: 0 var(--s-4);
  border: var(--line) solid var(--control-line);
  border-radius: calc(var(--tap-sm) / 2);
  background: transparent;
  color: var(--ink);
  font-size: var(--fs-sm);
  white-space: nowrap;
  text-decoration: none;
  transition: background var(--t-base) var(--ease), color var(--t-base) var(--ease), border-color var(--t-base) var(--ease);
  -webkit-tap-highlight-color: transparent;
}
.chip:hover { border-color: var(--accent); text-decoration: none; }
.chip[aria-pressed="true"], .chip[aria-selected="true"], .chip--on {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}
.chip:disabled { opacity: 0.45; }
/* A chip longer than its row (a long room name at a large text size) goes
   to a second line instead of leaving the screen. */
.chips:not(.chips--scroll) > .chip { max-width: 100%; white-space: normal; text-align: start; padding-block: var(--s-1); }
/* A day: the target is 44 px and the targets touch, so seven stand in one
   row at phone width; the round mark inside is smaller, and filled when the
   day is chosen. */
.chip--day { position: relative; z-index: 0; min-width: var(--tap-sm); justify-content: center; padding: 0; border-color: transparent; background: transparent; }
.chip--day::before {
  content: "";
  position: absolute;
  inset: 3px;
  z-index: -1;
  border: var(--line) solid var(--control-line);
  border-radius: 50%;
  transition: background var(--t-base) var(--ease), border-color var(--t-base) var(--ease);
}
.chip--day:hover { border-color: transparent; }
.chip--day:hover::before { border-color: var(--accent); }
.chip--day[aria-pressed="true"] { background: transparent; border-color: transparent; }
.chip--day[aria-pressed="true"]::before { background: var(--accent); border-color: var(--accent); }
.chips:has(> .chip--day) { gap: 0; }

.seg {
  display: inline-grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 2px;
  padding: 2px;
  border: var(--line) solid var(--control-line);
  border-radius: var(--r-pill);
  width: 100%;
}
.seg__btn {
  min-height: var(--tap-sm);
  padding: 0 var(--s-3);
  border-radius: var(--r-pill);
  font-size: var(--fs-sm);
  color: var(--ink-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background var(--t-base) var(--ease), color var(--t-base) var(--ease);
}
.seg__btn:hover { color: var(--ink); }
.seg__btn:focus-visible { outline-offset: -4px; }
.seg__btn[aria-pressed="true"], .seg__btn[aria-selected="true"] {
  background: var(--accent);
  color: var(--accent-ink);
}

/* =====================================================================
   LISTS — settings rows, people, users, rules. Rows between hairlines; no
   box around them. A row that can be pressed turns its line brass.
   ===================================================================== */
.list {
  border-top: var(--line) solid var(--hairline);
}
.list-item {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-height: 64px;
  padding: var(--s-3) 0;
  width: 100%;
  text-align: start;
  color: var(--ink);
  position: relative;
  border-bottom: var(--line) solid var(--hairline);
  transition: border-color var(--t-base) var(--ease), background var(--t-fast);
  -webkit-tap-highlight-color: transparent;
}
.list-item--tap { cursor: pointer; }
.list-item--tap:hover { border-bottom-color: var(--accent); }
.list-item--tap:active { background: var(--wash); }
.list-item__lead {
  width: 40px; height: 40px;
  display: grid; place-items: center start;
  color: var(--ink-3);
  flex: none;
}
.list-item__lead--accent { color: var(--accent); }
.list-item__lead--on { color: var(--accent); }
.list-item__lead--ok { color: var(--ok); }
.list-item__lead--danger { color: var(--danger); }
.list-item { text-decoration: none; }
.list-item:hover { text-decoration: none; }
.list-item__body { display: block; flex: 1 1 auto; min-width: 0; }
.list-item__title { display: block; font-size: 1.0625rem; line-height: 1.35; }
.list-item__sub { display: block; font-size: var(--fs-sm); color: var(--ink-2); line-height: 1.45; }
.list-item__sub--danger { color: var(--danger); }
.list-item__trail { flex: none; display: flex; align-items: center; gap: var(--s-2); color: var(--ink-3); }
.list-item__trail .chev { color: var(--ink-3); }
/* What is switched off is quieter, and still read. */
.list-item--off .list-item__title { color: var(--ink-2); }
/* At phone width several trailing controls go under the text (AT-20). */
.list-item--wrap { flex-wrap: wrap; }
.list-item--wrap .list-item__body { flex: 1 1 160px; }
.list-item--wrap .list-item__trail { flex: 0 0 auto; margin-inline-start: auto; }
/* One choice at the end of a row (the room of a device): on a wide screen
   the selects stand in one column of one width; at phone width the choice
   goes under the text, as wide as the row. */
.list-item--pick { flex-wrap: wrap; }
.list-item--pick .list-item__body { flex: 1 1 160px; }
.list-item--pick .list-item__trail { flex: 0 1 260px; min-width: 0; }
.list-item--pick .list-item__trail > .select { min-width: 0; }
@media (max-width: 559px) {
  .list-item--pick .list-item__trail { flex-basis: 100%; padding-inline-start: calc(40px + var(--s-3)); }
  .list-item--pick.list-item--no-lead .list-item__trail { padding-inline-start: 0; }
}

/* =====================================================================
   SHEET — a bottom sheet on the phone, a centered panel on desktop. It
   floats, so it is filled: one step lighter than the page, with a thin
   brass edge. Opened by ui.openSheet(); the back button closes it.
   ===================================================================== */
.sheet-host {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: none;
}
.sheet-host[data-open] { display: block; }
.sheet-host__scrim {
  position: absolute;
  inset: 0;
  background: var(--overlay);
  animation: fade var(--t-base) var(--ease);
}
.sheet {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  max-height: calc(100dvh - var(--s-6) - var(--safe-t));
  background: var(--raised);
  border-top: var(--line) solid var(--float-line);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  display: flex;
  flex-direction: column;
  animation: rise var(--t-slow) var(--ease);
  padding-bottom: var(--safe-b);
}
/* Each sheet is its own layer: nothing inside a lower one shows through the one above. */
.sheet-wrap { position: absolute; inset: 0; isolation: isolate; }
.sheet-wrap--closing .sheet { animation: sink var(--t-out) var(--ease) forwards; }
.sheet-wrap--closing .sheet-host__scrim { animation: unfade var(--t-out) var(--ease) forwards; }
/* A sheet above a sheet: the second scrim is lighter, so two do not go black. */
.sheet-wrap + .sheet-wrap .sheet-host__scrim { background: color-mix(in srgb, var(--overlay) 55%, transparent); }
.has-sheet body { overflow: hidden; }
.sheet__handle {
  width: 36px; height: var(--line);
  background: var(--ink-2);
  margin: 10px auto 0;
  flex: none;
}
.sheet__head {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-4) var(--s-2);
  flex: none;
}
/* The way back and the way out: thin round outlines. */
.sheet__head > .btn--icon { width: var(--tap-sm); min-height: var(--tap-sm); border-color: var(--control-line); color: var(--ink-2); }
.sheet__head > .btn--icon:hover { border-color: var(--accent); color: var(--accent); background: transparent; }
.sheet__title {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  font-weight: var(--fw-light);
  line-height: 1.25;
  padding-inline-start: var(--s-1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.sheet__body {
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--s-2) var(--gutter) var(--s-5);
  -webkit-overflow-scrolling: touch;
}
.sheet__foot {
  flex: none;
  padding: var(--s-3) var(--gutter);
  border-top: var(--line) solid var(--hairline);
  background: var(--raised);
  display: flex;
  gap: var(--s-3);
}
.sheet__foot > .btn { flex: 1 1 auto; }
@media (min-width: 720px) {
  .sheet {
    inset-inline: 0;
    bottom: auto;
    top: 50%;
    margin-inline: auto;
    width: min(var(--sheet-max), calc(100% - var(--s-6)));
    max-height: min(86dvh, 820px);
    transform: translateY(-50%);
    border: var(--line) solid var(--float-line);
    border-radius: var(--r-xl);
    animation: pop var(--t-base) var(--ease);
  }
  .sheet-wrap--closing .sheet { animation: unpop var(--t-out) var(--ease) forwards; }
  .sheet__handle { display: none; }
  .sheet__head { padding: var(--s-5) var(--s-5) var(--s-2); }
  .sheet__body { padding-inline: var(--s-6); }
  .sheet__foot { padding-inline: var(--s-6); border-radius: 0 0 var(--r-xl) var(--r-xl); }
  .sheet--wide { width: min(960px, calc(100% - var(--s-6))); }
}
/* A sheet that leaves the screen above it in sight and in reach (a room of
   the plan of the house): on a phone it has no scrim and takes the lower
   part only; a touch above it reaches the page, and the page still moves.
   From 720 px it is a sheet like any other. */
.sheet-host { pointer-events: none; }
.sheet-wrap { pointer-events: auto; }
@media (max-width: 719.98px) {
  .sheet-wrap--peek { pointer-events: none; }
  .sheet-wrap--peek > .sheet-host__scrim { display: none; }
  .sheet-wrap--peek > .sheet { pointer-events: auto; max-height: 52dvh; }
  .has-sheet:has(.sheet-host > .sheet-wrap--peek:only-child) body { overflow: visible; overflow-x: hidden; }
}
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes unfade { from { opacity: 1; } to { opacity: 0; } }
@keyframes rise { from { transform: translateY(40px); opacity: 0.4; } to { transform: none; opacity: 1; } }
@keyframes sink { to { transform: translateY(100%); opacity: 0.7; } }
@keyframes pop { from { transform: translateY(calc(-50% + 14px)); opacity: 0; } to { transform: translateY(-50%); opacity: 1; } }
@keyframes unpop { to { transform: translateY(calc(-50% + 8px)); opacity: 0; } }

/* =====================================================================
   DIALOG — the confirm for a sensitive device. Always centered, always
   with a plain question, the dangerous choice last and never default.
   ===================================================================== */
.dialog-host {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--gutter);
}
.dialog-host[data-open] { display: flex; }
.dialog-host__scrim { position: absolute; inset: 0; background: var(--overlay); animation: fade var(--t-base) var(--ease); }
.dialog {
  position: relative;
  width: min(420px, 100%);
  background: var(--raised);
  border: var(--line) solid var(--float-line);
  border-radius: var(--r-xl);
  padding: var(--s-6) var(--s-5) var(--s-5);
  animation: pop-in var(--t-slow) var(--ease);
  text-align: center;
}
@keyframes pop-in { from { transform: translateY(14px); opacity: 0; } to { transform: none; opacity: 1; } }
.dialog__icon {
  display: grid; place-items: center;
  color: var(--accent);
  margin-bottom: var(--s-3);
}
.dialog--danger .dialog__icon { color: var(--danger); }
.dialog--info .dialog__icon { color: var(--accent); }
.dialog__title { font-family: var(--font-display); font-size: var(--fs-xl); font-weight: var(--fw-light); line-height: 1.25; margin-bottom: var(--s-2); }
.dialog__text { color: var(--ink-2); font-size: 0.9375rem; }
.dialog__device {
  display: inline-flex; align-items: center; gap: var(--s-2);
  margin-top: var(--s-4);
  min-height: 32px;
  padding: 0 var(--s-4);
  border: var(--line) solid var(--hairline-strong);
  border-radius: var(--r-pill);
  font-size: var(--fs-sm);
}
.dialog__device .icon { color: var(--ink-3); }
/* "ביטול" first, the action last: at the end of the line on a wide screen,
   and on top when the two are stacked on a phone. */
.dialog__actions { display: flex; flex-direction: column-reverse; gap: var(--s-3); margin-top: var(--s-5); }
@media (min-width: 420px) {
  .dialog__actions { flex-direction: row; }
  .dialog__actions > .btn { flex: 1 1 0; }
}

/* =====================================================================
   TOAST — on a phone above the navigation, by the thumb ("בטל" is within
   reach); on a wide screen the top corner at the end of the line — the
   left, in Hebrew (docs/05 §2). One action at most. At most three.
   ===================================================================== */
.toast-host {
  position: fixed;
  bottom: calc(var(--nav-h) + var(--safe-b) + var(--s-4));
  inset-inline: var(--gutter);
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-2);
  pointer-events: none;
}
.has-sheet .toast-host { bottom: calc(var(--safe-b) + var(--s-4)); }
@media (min-width: 900px) {
  .toast-host, .has-sheet .toast-host {
    top: var(--s-4);
    bottom: auto;
    inset-inline: auto var(--s-5);
    width: 380px;
    align-items: stretch;
  }
}
.toast {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  max-width: 100%;
  min-height: 48px;
  padding-block: 2px;
  padding-inline: var(--s-5) var(--s-2);
  border: var(--line) solid var(--float-line);
  border-radius: 25px;
  background: var(--raised);
  color: var(--ink);
  font-size: 0.9375rem;
  line-height: 1.4;
  animation: toast-in var(--t-base) var(--ease);
}
.toast--leaving { animation: toast-out var(--t-out) var(--ease) forwards; }
@keyframes toast-in { from { transform: translateY(10px); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes toast-out { to { transform: translateY(10px); opacity: 0; } }
.toast__icon { flex: none; color: var(--ink-2); }
.toast__text { flex: 1 1 auto; min-width: 0; padding-block: var(--s-2); }
.toast__text:last-child { padding-inline-end: var(--s-3); }
.toast__action {
  flex: none;
  min-height: var(--tap-sm);
  min-width: var(--tap-sm);
  padding-inline: var(--s-4);
  border-radius: var(--r-pill);
  font-weight: var(--fw-medium);
  color: var(--accent);
  transition: color var(--t-fast);
}
.toast__action:hover { color: var(--accent-hover); }
.toast__action:focus-visible { outline-offset: -3px; }
.toast--ok .toast__icon { color: var(--ok); }
.toast--warn .toast__icon { color: var(--warn); }
.toast--danger { border-color: var(--danger); }
.toast--danger .toast__icon { color: var(--danger); }

/* =====================================================================
   FORMS — the label small and spaced, the text over a line. No box: the
   line is the field. An address is written left to right.
   ===================================================================== */
.field { display: flex; flex-direction: column; gap: 2px; margin-bottom: var(--s-5); }
.field__label { font-size: var(--fs-xs); font-weight: var(--fw-medium); letter-spacing: 0.12em; line-height: 1.5; color: var(--ink-2); }
.field__label .req { color: var(--danger); margin-inline-start: 4px; letter-spacing: 0; }
.field__hint { font-size: var(--fs-sm); color: var(--ink-2); margin-top: var(--s-1); }
.field__error { font-size: var(--fs-sm); color: var(--danger); display: flex; align-items: center; gap: var(--s-1); margin-top: var(--s-1); }
.input, .select, .textarea {
  width: 100%;
  min-height: var(--tap);
  padding: 0;
  border: 0;
  border-bottom: var(--line) solid var(--control-line);
  border-radius: 0;
  background: transparent;
  color: var(--ink);
  font-size: 1.0625rem;
  transition: border-color var(--t-base) var(--ease);
  text-align: start;
}
.textarea { padding-block: var(--s-2); min-height: 96px; resize: vertical; line-height: 1.5; }
.input::placeholder, .textarea::placeholder { color: var(--ink-2); opacity: 1; }
.input:hover, .select:hover, .textarea:hover { border-bottom-color: var(--ink); }
.input:focus-visible, .select:focus-visible, .textarea:focus-visible { border-bottom-color: var(--accent); outline-offset: 4px; }
.input[aria-invalid="true"], .select[aria-invalid="true"] { border-bottom: var(--line-level) solid var(--danger); }
.input:disabled, .select:disabled { opacity: 0.5; border-bottom-style: dashed; }
.input--ltr { direction: ltr; text-align: left; font-family: var(--font-mono); font-size: var(--fs-md); }
.input--lg { min-height: 56px; font-size: var(--fs-lg); }
/* A number inside a sentence: "וכבה אחרי [5] דקות בלי תנועה". The number
   alone, in the middle: the browser's own little arrows are left out. */
.input--short { width: 4.5rem; flex: none; text-align: center; appearance: textfield; -moz-appearance: textfield; }
.input--short::-webkit-outer-spin-button, .input--short::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.select {
  appearance: none;
  -webkit-appearance: none;
  padding-inline-end: calc(24px + var(--s-2));
  background-image: var(--chevron);
  background-repeat: no-repeat;
  background-position: left 0 center;
  cursor: pointer;
}
[dir="ltr"] .select { background-position: right 0 center; }
.select option, .select optgroup { background: var(--raised); color: var(--ink); font-weight: var(--fw-regular); }
.select optgroup { font-weight: var(--fw-medium); }
/* A door code: typed in a text field (a password field would make the browser
   offer to keep it), with the digits hidden until the person asks to see. */
.input--secret { -webkit-text-security: disc; }
.input-group { display: flex; gap: var(--s-3); align-items: flex-end; }
.input-group > .input { flex: 1 1 auto; min-width: 0; }
.input-group > .btn { flex: none; }
/* Fields side by side when there is room, with their action at the end (the
   recordings of a camera: date, time, length, "הצג"); one under the other on
   a phone. The fields keep one top line, so an error under one of them moves
   nothing beside it. */
.field-row { display: grid; gap: 0 var(--s-5); grid-template-columns: minmax(0, 1fr); }
.field-row > .field { min-width: 0; }
.field-row > .field > .field__label { line-height: 1.5; }
.field-row > .btn { margin-bottom: var(--s-5); }
@media (min-width: 560px) {
  .field-row { display: flex; align-items: flex-start; gap: var(--s-5); }
  .field-row > .field { flex: 1 1 0; }
  .field-row > .btn { flex: none; margin-top: calc(var(--fs-xs) * 1.5 + 2px); }
}
.input-wrap { position: relative; }
.input-wrap > .icon { position: absolute; inset-inline-start: 0; top: 50%; transform: translateY(-50%); color: var(--ink-3); pointer-events: none; }
.input-wrap > .input { padding-inline-start: calc(24px + var(--s-3)); }

.check {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  min-height: var(--tap);
  padding-block: var(--s-3);
  cursor: pointer;
}
.check input[type="checkbox"], .check input[type="radio"] {
  appearance: none;
  -webkit-appearance: none;
  width: 22px; height: 22px;
  flex: none;
  margin-top: 1px;
  border: var(--line) solid var(--control-line);
  border-radius: var(--r-sm);
  background: transparent;
  display: grid; place-items: center;
  transition: background var(--t-base) var(--ease), border-color var(--t-base) var(--ease);
  cursor: pointer;
}
.check input[type="radio"] { border-radius: 50%; }
.check input:checked { background: var(--accent); border-color: var(--accent); }
.check input[type="checkbox"]:checked::after {
  content: "";
  width: 16px; height: 16px;
  background: var(--accent-ink);
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'><path d='M20 6L9 17l-5-5'/></svg>") center / contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'><path d='M20 6L9 17l-5-5'/></svg>") center / contain no-repeat;
}
.check input[type="radio"]:checked { background: transparent; }
.check input[type="radio"]:checked::after { content: ""; width: 10px; height: 10px; border-radius: 50%; background: var(--accent); }
.check input:disabled { opacity: 0.45; }
.check__text { flex: 1 1 auto; min-width: 0; }
.check__sub { display: block; font-size: var(--fs-sm); color: var(--ink-2); }

/* A row with a switch at the end: "מצב שבת נכנס ויוצא לבד". */
.switch-row {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  min-height: var(--tap);
  padding-block: var(--s-2);
  cursor: pointer;
}
.switch-row__text { flex: 1 1 auto; min-width: 0; }
/* "חדרים ושיוך": one room of the order, its two moves and its "outside" switch (docs/34 §10, G1, G23). */
.room-order__head { display: flex; align-items: center; gap: var(--s-3); }
.room-order__name { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.room-order__moves { display: flex; gap: var(--s-1); flex: none; }

/* =====================================================================
   TABLE — rows between hairlines; on phones each row becomes a group with
   the column's name beside each value
   ===================================================================== */
.table-wrap { overflow-x: auto; border-top: var(--line) solid var(--hairline); }
.table { width: 100%; border-collapse: collapse; font-size: 0.9375rem; }
.table th, .table td { text-align: start; padding: var(--s-3) 0; padding-inline-end: var(--s-4); vertical-align: top; }
.table th { font-weight: var(--fw-medium); color: var(--ink-2); font-size: var(--fs-xs); letter-spacing: 0.12em; white-space: nowrap; }
.table tr { border-bottom: var(--line) solid var(--hairline); }
.table tbody tr:hover { background: var(--wash); }
.table td.num, .table th.num { text-align: end; padding-inline-end: 0; font-variant-numeric: tabular-nums; }
@media (max-width: 559.98px) {
  .table--stack thead { display: none; }
  .table--stack tr { display: block; padding: var(--s-2) 0; }
  .table--stack td { display: flex; gap: var(--s-3); padding: var(--s-1) 0; }
  .table--stack td::before { content: attr(data-label); color: var(--ink-2); flex: 0 0 38%; font-size: var(--fs-sm); }
  .table--stack td.num { text-align: start; }
}

/* =====================================================================
   BADGE — a small word in a thin outline. Its kind is its colour and, with
   --dot, a mark before the word; the word itself always says the state.
   ===================================================================== */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 24px;
  padding: 0 10px;
  border: var(--line) solid var(--hairline-strong);
  border-radius: 13px;
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  color: var(--ink-2);
  white-space: nowrap;
  line-height: 1.2;
}
.badge--ok { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 50%, transparent); }
.badge--warn { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 50%, transparent); }
.badge--danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 50%, transparent); }
.badge--on { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 50%, transparent); }
.badge--accent { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 50%, transparent); }
.badge--dot::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }
.badge--lg { min-height: 32px; padding-inline: var(--s-4); font-size: var(--fs-sm); border-radius: 17px; }
/* a badge that holds a sentence: on a narrow screen, or at large text, it takes a second line */
.badge--wrap { white-space: normal; line-height: 1.3; padding-block: 6px; }

/* =====================================================================
   BANNER — a standing notice at the top of a screen: an icon, the words,
   and a line of the notice's colour at the start. Not a filled strip.
   ===================================================================== */
.banner {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  padding-block: var(--s-2);
  padding-inline: var(--s-4) 0;
  border-inline-start: var(--line-level) solid var(--accent);
  color: var(--ink);
  font-size: 0.9375rem;
  margin-bottom: var(--s-4);
}
.banner .icon { flex: none; margin-top: 1px; color: var(--accent); }
.banner__text { flex: 1 1 auto; min-width: 0; }
.banner__title { font-weight: var(--fw-medium); display: block; }
.banner--warn { border-inline-start-color: var(--warn); }
.banner--warn > .icon { color: var(--warn); }
.banner--danger { border-inline-start-color: var(--danger); }
.banner--danger > .icon { color: var(--danger); }
.banner--ok { border-inline-start-color: var(--ok); }
.banner--ok > .icon { color: var(--ok); }
.banner--on { border-inline-start-color: var(--accent); background: var(--glow); }
.banner .btn { flex: none; }

/* =====================================================================
   EMPTY STATE, SKELETON
   ===================================================================== */
.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--s-2);
  padding: var(--s-7) var(--s-4);
  color: var(--ink-2);
}
.empty__icon {
  display: grid; place-items: center;
  color: var(--ink-3);
  margin-bottom: var(--s-2);
}
.empty__title { font-family: var(--font-display); font-size: var(--fs-lg); font-weight: var(--fw-regular); line-height: 1.3; color: var(--ink); }
.empty__text { max-width: 36ch; font-size: 0.9375rem; }
.empty .btn { margin-top: var(--s-4); }
.empty--compact { padding: var(--s-5) var(--s-3); }

.skel {
  background: var(--skeleton);
  background-size: 200% 100%;
  animation: shimmer 1.6s ease-in-out infinite;
  border-radius: 4px;
  display: block;
}
@keyframes shimmer { 0% { background-position: 100% 0; } 100% { background-position: -100% 0; } }
.skel--line { height: 12px; width: 100%; }
.skel--line.w-60 { width: 60%; }
.skel--line.w-40 { width: 40%; }
.skel--title { height: 20px; width: 50%; }
.skel--circle { width: 40px; height: 40px; border-radius: 50%; }
/* What will be a tile, a room or the date line: the place of its two lines
   of words over its hairline — not a block. */
.skel--tile, .skel--room, .skel--strip { position: relative; background: none; animation: none; border-radius: 0; border-bottom: var(--line) solid var(--hairline); }
.skel--tile { min-height: 124px; }
.skel--room { min-height: 88px; }
.skel--tile::before, .skel--tile::after, .skel--room::before, .skel--room::after, .skel--strip::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  height: 10px;
  border-radius: 4px;
  background: var(--skeleton);
  background-size: 200% 100%;
  animation: shimmer 1.6s ease-in-out infinite;
}
.skel--tile::before { bottom: 44px; width: 62%; }
.skel--tile::after { bottom: 20px; width: 38%; }
.skel--room::before { bottom: 48px; width: 46%; }
.skel--room::after { bottom: 26px; width: 30%; }
.skel--strip::before { bottom: 27px; width: 56%; }
/* What will be a scene: the outline of its pill. */
.skel--chip { height: var(--tap); width: 110px; border-radius: var(--r-pill); flex: none; background: none; animation: none; border: var(--line) solid var(--hairline-strong); }
@media (prefers-reduced-motion: reduce) {
  .skel, .skel--tile::before, .skel--tile::after, .skel--room::before, .skel--room::after, .skel--strip::before { animation: none; background: var(--wash-2); }
  .skel--tile, .skel--room, .skel--strip, .skel--chip { background: none; }
}

/* =====================================================================
   TIMELINE — "למה?": what happened, step by step. A thin line down the
   start of the rows; the hour in brass. What was refused is a diamond and
   red words; what went well is a full mark; what is only a note is faint.
   ===================================================================== */
.timeline { display: flex; flex-direction: column; }
.tl-item {
  display: grid;
  grid-template-columns: 12px minmax(0, 1fr) auto;
  align-items: start;
  gap: var(--s-4);
  padding-block: var(--s-3);
  position: relative;
  text-align: start;
  width: 100%;
  color: var(--ink);
}
.tl-item::before {
  content: "";
  position: absolute;
  inset-inline-start: 5.5px;
  top: 0; bottom: 0;
  width: var(--line);
  background: var(--hairline);
}
.tl-item:first-child::before { top: 20px; }
.tl-item:last-child::before { bottom: calc(100% - 20px); }
.tl-item__dot {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--raised);
  border: var(--line) solid var(--accent);
  margin-top: 8px;
  margin-inline-start: 1.5px;
  position: relative;
  z-index: 1;
}
.tl-item--refused .tl-item__dot { border-radius: 0; border-color: var(--danger); transform: rotate(45deg); }
.tl-item--ok .tl-item__dot { background: var(--ok); border-color: var(--ok); }
.tl-item--muted .tl-item__dot { border-color: var(--ink-3); }
.tl-item__body { display: block; min-width: 0; }
.tl-item__time { display: block; font-size: var(--fs-sm); line-height: 1.5; color: var(--accent); font-variant-numeric: tabular-nums; }
.tl-item__text { display: block; line-height: 1.45; }
.tl-item .chev { color: var(--ink-3); margin-top: 6px; }
.tl-item--refused .tl-item__text { color: var(--danger); }
.tl-item--muted .tl-item__text { color: var(--ink-2); }
.tl-item--tap { cursor: pointer; transition: background var(--t-fast); }
.tl-item--tap:hover .tl-item__text { color: var(--accent); }
.tl-item--tap:active { background: var(--wash); }

/* The check lines of a test: ✓ / ✗ */
.check-line { display: flex; gap: var(--s-3); align-items: flex-start; padding-block: var(--s-1); }
.check-line .icon { flex: none; margin-top: 3px; }
.check-line--ok .icon { color: var(--ok); }
.check-line--no .icon { color: var(--danger); }
.check-line--no { color: var(--danger); }
.check-line--wait .icon { color: var(--ink-3); }

/* =====================================================================
   TABS (top of a screen), MENU (popover), KV, AVATAR, CAMERA, MISC
   ===================================================================== */
/* The row scrolls sideways; the keyboard's ring is drawn inside a tab, so
   the row needs no room for it and its hairline is as wide as the page's. */
.tabs {
  display: flex;
  gap: var(--s-2);
  overflow-x: auto;
  scrollbar-width: none;
  border-bottom: var(--line) solid var(--hairline);
  margin-bottom: var(--s-4);
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  flex: none;
  min-height: var(--tap);
  padding: 0 var(--s-3);
  color: var(--ink-2);
  position: relative;
  white-space: nowrap;
  transition: color var(--t-base) var(--ease);
}
.tab:hover { color: var(--ink); }
.tab:focus-visible { outline-offset: -4px; }
.tab[aria-selected="true"] { color: var(--accent); font-weight: var(--fw-medium); }
.tab[aria-selected="true"]::after {
  content: "";
  position: absolute;
  inset-inline: var(--s-3);
  bottom: 0;
  height: var(--line);
  background: var(--accent);
}

.menu {
  position: fixed;
  min-width: 220px;
  max-width: calc(100vw - 16px);
  background: var(--raised);
  border: var(--line) solid var(--float-line);
  border-radius: var(--r-md);
  padding: var(--s-1);
  z-index: 55;
  animation: pop-in var(--t-fast) var(--ease);
}
/* A menu opened inside a sheet stands a step above the sheet. */
.sheet .menu { background: var(--raised-2); }
.menu__item {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  width: 100%;
  min-height: var(--tap);
  padding-inline: var(--s-3);
  border-radius: 10px;
  text-align: start;
  color: var(--ink);
  transition: background var(--t-fast);
}
.menu__item .icon { color: var(--ink-3); }
.menu__item:hover { background: var(--wash); }
.menu__item:focus-visible { outline-offset: -2px; }
.menu__item--danger, .menu__item--danger .icon { color: var(--danger); }
.menu__sep { height: var(--line); background: var(--hairline); margin: var(--s-1) var(--s-3); }

.kv { display: grid; grid-template-columns: auto 1fr; gap: var(--s-2) var(--s-5); font-size: 0.9375rem; }
.kv dt { color: var(--ink-2); }
.kv dd { margin: 0; }

.avatar {
  width: 40px; height: 40px;
  border-radius: 50%;
  display: grid; place-items: center;
  border: var(--line) solid var(--hairline-strong);
  color: var(--ink);
  font-family: var(--font-display);
  flex: none;
}
.avatar--lg { width: 56px; height: 56px; font-size: var(--fs-xl); font-weight: var(--fw-light); }

/* A picture's frame is dark in both themes: what stands on it takes the
   dark set's colours (tokens.css, --camera-*). A frame that plays nothing
   has a dashed edge. */
.camera {
  --ink: var(--camera-ink);
  --ink-2: var(--camera-ink-2);
  --ink-3: var(--camera-ink-3);
  --accent: var(--camera-accent);
  --control-line: var(--camera-line);
  --hairline-strong: var(--camera-line-2);
  --wash-2: var(--camera-wash);
  --focus-ring: var(--camera-ink);
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--r-md);
  background: var(--camera);
  color: var(--camera-ink);
  overflow: hidden;
}
.camera::after { content: ""; position: absolute; inset: 0; border: var(--line) solid var(--hairline); border-radius: inherit; pointer-events: none; }
.camera:has(> .camera__off)::after { border: var(--line) dashed var(--camera-line); }
.camera iframe, .camera video, .camera img { width: 100%; height: 100%; border: 0; object-fit: cover; }
/* A long name stays on one line inside the frame. */
.camera__name {
  position: absolute;
  inset-inline-start: var(--s-2);
  bottom: var(--s-2);
  max-width: calc(100% - 2 * var(--s-2));
  padding: 2px var(--s-3);
  border-radius: var(--r-pill);
  background: var(--camera-veil);
  color: var(--camera-ink);
  font-size: var(--fs-sm);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.camera__live {
  position: absolute;
  inset-inline-end: var(--s-2);
  top: var(--s-2);
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px var(--s-3);
  border-radius: var(--r-pill);
  background: var(--camera-veil);
  color: var(--camera-ink);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
}
.camera__live::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--camera-live); }
.camera__off {
  position: absolute; inset: 0;
  display: grid; place-items: center; align-content: center; gap: var(--s-2);
  color: var(--camera-ink-2);
  font-size: var(--fs-sm);
  line-height: 1.4;
  text-align: center;
  padding: var(--s-4);
}
/* "נסה שוב" inside a frame that could not play. It sits above the link that
   covers a frame of the grid, so the tap reaches it. */
.camera__off .btn {
  position: relative;
  z-index: 2;
  margin-top: var(--s-1);
}

/* The cameras as a grid (screen 16): one across on a phone, two from 560 px,
   three from 1100. */
.cam-grid {
  display: grid;
  gap: var(--s-4);
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 560px) { .cam-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .cam-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* One camera of the grid: the frame, and above it a link that takes the tap
   (a player's frame would swallow it). */
.cam {
  --focus-ring: var(--camera-ink);
  position: relative;
  min-width: 0;
  border-radius: var(--r-md);
}
.cam__open {
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: var(--r-md);
  -webkit-tap-highlight-color: transparent;
}
/* The frame clips what is outside it, so the ring is drawn inside. */
.cam__open:focus-visible { outline-offset: -4px; }
/* What a waiting frame says sits above the name, not under it. */
.cam .camera__off { padding-bottom: var(--s-6); }

/* One camera, large (screen 17): never taller than the screen it is on. */
.camera--stage {
  width: 100%;
  max-width: max(320px, calc((100dvh - 250px) * 16 / 9));
  margin-inline: auto;
  border-radius: var(--r-lg);
}
.camera--stage:fullscreen { max-width: none; border-radius: 0; }
.camera--stage .camera__off { font-size: var(--fs-md); }

/* How full something is: the disk, the memory (screen 31). A line, and the
   brass part of it is how much. The fill grows from the start of the line —
   from the right, in Hebrew. */
.meter {
  display: block;
  height: var(--line-level);
  margin-top: var(--s-2);
  background: var(--hairline-strong);
  overflow: hidden;
}
.meter__fill {
  display: block;
  height: 100%;
  width: var(--value, 0%);
  background: var(--accent);
  transition: width var(--t-base) var(--ease);
}
.meter--warn .meter__fill { background: var(--warn); }
.meter--danger .meter__fill { background: var(--danger); }

/* The top of a device sheet: the device's mark, and its one number in
   large light figures. What is on stands in its own light. */
.hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) 0 var(--s-5);
  text-align: center;
}
.hero__icon {
  width: 96px; height: 96px;
  border-radius: 50%;
  display: grid; place-items: center;
  color: var(--ink-3);
  transition: color var(--t-base) var(--ease);
}
.hero--on .hero__icon { color: var(--accent); background: var(--glow-spot); }
.hero__value { font-family: var(--font-display); font-size: var(--fs-3xl); font-weight: var(--fw-light); font-variant-numeric: tabular-nums; line-height: 1; direction: ltr; unicode-bidi: isolate; }
.hero__value .unit { font-size: var(--fs-lg); color: var(--ink-2); font-weight: var(--fw-regular); }
.hero__state { color: var(--ink-2); }

/* A line of key figures under a heading: "12 מכשירים · 3 דולקים" */
.meta { font-size: var(--fs-sm); color: var(--ink-2); }
.meta .sep { margin-inline: 6px; color: var(--ink-3); }

/* A note with a thin brass mark at its start (the Shabbat halachic note) */
.note {
  padding-block: var(--s-1);
  padding-inline: var(--s-4) 0;
  font-size: var(--fs-sm);
  color: var(--ink-2);
  border-inline-start: var(--line) solid var(--accent);
}

/* Gate overlay for the login screen (docs/30 §11.1): above the whole app */
/* A short form sits in the middle; a tall one (the first owner, on a phone
   with its keyboard up) scrolls inside the layer instead of being cut at the
   top — hence the card's own margins, not the layer's centring. */
.gate {
  position: fixed;
  inset: 0;
  z-index: 70;
  background: var(--canvas) var(--page-glow);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  overflow-y: auto;
  padding: var(--gutter);
}
.gate__card { width: min(420px, 100%); margin: auto; }
.gate__brand { display: flex; flex-direction: column; align-items: center; gap: var(--s-3); margin-bottom: var(--s-6); }
.gate__brand .icon { color: var(--accent); }

/* Pressable row of a settings hub */
.hub { display: grid; gap: 0 var(--s-5); grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 720px) { .hub { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.hub__item {
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-2);
  min-height: 104px;
  padding: var(--s-4) 0;
  border-bottom: var(--line) solid var(--hairline);
  color: var(--ink);
  text-align: start;
  transition: border-color var(--t-base) var(--ease);
}
.hub__item:hover { border-bottom-color: var(--accent); }
.hub__item:active { background: var(--wash); }
.hub__item .icon { color: var(--accent); }
.hub__title { font-size: 1.0625rem; }
.hub__sub { font-size: var(--fs-sm); color: var(--ink-2); }
.hub__item--soon { opacity: 0.6; }

/* =====================================================================
   SLIDER on a tile, LOADING ROW, STEPS, TAB COUNT, NOTICES
   ===================================================================== */
/* Colour temperature: the line itself is the scale, warm at the start, cool
   at the end; the knob says where on it. */
.slider--temp::before {
  bottom: 18px;
  height: 6px;
  border: var(--line) solid var(--control-line);
  border-radius: 3px;
  background: linear-gradient(to left, var(--temp-warm), var(--temp-mid) 55%, var(--temp-cool));
}
[dir="ltr"] .slider--temp::before { background: linear-gradient(to right, var(--temp-warm), var(--temp-mid) 55%, var(--temp-cool)); }
.slider--temp .slider__fill { background: transparent; }
.slider--temp .slider__fill::after { background-color: var(--ink); border-color: var(--wash-2); }
.slider--lg.slider--temp .slider__label .num { font-size: var(--fs-xl); color: var(--ink); }
/* On a tile: the line alone, and it lies exactly where the tile's own
   hairline would, so the lines of one row stay level. The knob hangs half
   under the line; the slider answers a finger there too (::after). The
   tile's own words say the value. */
.slider--compact { height: var(--tap-sm); }
.slider--compact::before { bottom: calc(var(--line) * -1); }
.slider--compact .slider__fill { bottom: calc(var(--line) * -1); }
.slider--compact::after { content: ""; position: absolute; inset: 0 0 -16px; }
.slider--compact .slider__label { display: none; }

.loading-row { display: flex; align-items: center; gap: var(--s-3); padding: var(--s-3) 0; color: var(--ink-2); font-size: var(--fs-sm); }

/* Where a wizard stands: a line over each step. Done and now are brass. */
.steps { display: flex; gap: var(--s-2); margin-bottom: var(--s-5); }
.steps__item { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: var(--s-2); font-size: var(--fs-xs); color: var(--ink-2); }
.steps__item::before { content: ""; height: var(--line); background: var(--hairline-strong); }
.steps__dot { display: none; }
.steps__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.steps__item--done::before { background: var(--accent); }
.steps__item--now { color: var(--ink); font-weight: var(--fw-medium); }
.steps__item--now::before { height: var(--line-level); margin-bottom: calc(var(--line) * -1); background: var(--accent); }

.tab__count { margin-inline-start: 6px; font-size: var(--fs-xs); font-weight: var(--fw-regular); color: var(--ink-2); font-variant-numeric: tabular-nums; }

/* The shell's standing notices, above every screen: the storage banner of
   docs/27 §7.4, "login is off" of docs/30 §8.3. */
.notices { display: flex; flex-direction: column; }
.notices:empty { display: none; }
.notices .banner { margin: var(--s-3) var(--gutter) 0; }
@media (min-width: 900px) { .notices .banner { margin-inline: var(--s-7); } }

/* A block of a sheet or a screen with its own heading: "כאשר", "רק אם",
   "אז". A group under a hairline; its heading is the small spaced label. */
.block { border-top: var(--line) solid var(--hairline); padding: var(--s-3) 0 var(--s-4); }
/* In a column or a grid that keeps its own gaps, a card, a block and a rule
   add none: the space between them is the column's. */
:is(.stack, .stack-2, .stack-5, .grid-auto, .grid-auto-lg, .grid-2) > :is(.card, .block, hr) { margin: 0; }
.block__title { font-family: var(--font-display); font-size: var(--fs-xs); font-weight: var(--fw-medium); letter-spacing: var(--ls-label); color: var(--accent); margin-bottom: var(--s-2); }

.skel--strip { height: 64px; margin-bottom: var(--s-3); }
.stepper--block { display: grid; width: 100%; }
.center-x { justify-content: center; }
.field__label.center { text-align: center; }

/* Setup menu choices and maker-app QR: touch targets and a quiet zone at
   phone width, with no third-party request for the login payload. */
.flow-choices { display: grid; gap: var(--s-3); }
.flow-choices > .btn { justify-content: flex-start; text-align: start; white-space: normal; overflow-wrap: anywhere; min-height: 48px; }
.flow-qr canvas { display: block; max-width: 100%; height: auto; image-rendering: pixelated; }
