/* --------------------------------------------------------------------------
   TTOP School — application styles
   Tokens are the ones settled in docs/COLORS.md. Do not invent new ones here.
   Assistant carries all interface text; TTOPNum (Heebo) carries digits in
   aligned columns only — never on an element that also contains words.
   -------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
   NO SCREEN SLIDES SIDEWAYS. Menachem, 9 August 2026, on a phone: *"I need to
   scroll out of the page in order to reach certain things that are beyond the
   borders of the phone."*

   Anything genuinely wider than a phone — the marking grid, a sheet of Letter
   paper — already slides INSIDE its own box (`.gridscroll`, `#sheet`). This is
   the backstop for everything else: one long unbreakable line anywhere on any
   screen used to widen the body, and then every button on the page travelled
   off the right edge with it.

   `clip` AND NOT `hidden`. `hidden` makes the element a scroll container, which
   silently breaks `position: sticky` — the top bar, the grid's frozen name
   column. `clip` stops the sliding and leaves sticky alone.
   -------------------------------------------------------------------------- */
/* THE CLIP IS OFF AGAIN, deliberately. It stopped the page sliding and by doing
   so put everything that was out of reach permanently out of reach, which is
   strictly worse than a page that slides. Menachem could at least GET to the
   buttons before. Until the real cause is found — with a picture of the phone,
   rather than four rounds of guessing — the page is allowed to slide. */
/* THE PAGE DOES NOT SLIDE. Menachem: *"it slides out of focus and away from the
   top banner — the green part slides out until the beige part."* That is the
   BODY moving, and it takes the top bar and the menu with it.

   This is safe to do ONLY because of the rule below: the boxes have their own
   sliders now. The first time this line existed they did not, and it turned
   things that were out of reach into things that could never be reached. Both
   halves have to be on together — that was the whole mistake, twice. */
html, body { max-width: 100%; }
/* ⚠ THE CLIP IS OFF. Seven attempts, none of them right, all of them blind —
   see the note in STATE.md. A page that slides is a nuisance; a page that is
   pinned while the thing you need is off the edge is a wall. Until somebody has
   LOOKED at the phone, the nuisance is the safer of the two. Do not put this
   back without a screenshot in front of you. */

/* EACH BOX SLIDES ON ITS OWN. Menachem: *"is there any way you can figure out
   how to get the boxes to move?"*

   The two earlier attempts both put the scroller in the wrong place. On `main`
   it was one big scroller wrapping everything, so the whole screen slid — which
   is what he already had. On `body` it was `clip`, which stopped the sliding by
   putting the content permanently out of reach, which was worse.

   The scroller belongs on THE BOX. Every direct child of the page's `main` — a
   card, a section, the grid's frame — gets its own horizontal scroller, so
   whichever one is too wide is the one that moves, and the headings and buttons
   around it stay put.

   `overflow-x: auto` forces the other axis to `auto` as well; that is harmless
   here only because these boxes are sized BY their content and so never have
   anything to scroll vertically. Do not put this on a box with a fixed height.

   THE BODY IS DELIBERATELY LEFT ALONE. If something still overflows despite
   this, the page slides — which is a nuisance, and reachable. Nothing is ever
   locked away again. */
/* `#m-grid` is EXCLUDED because it already holds `.gridscroll`, which is the
   marking grid's own slider. Two nested horizontal scrollers on a touch screen
   means the outer takes the drag and the inner never moves — that was the
   Android failure. One scroller, on the box that is actually too wide. */
/* `#s-grid` is excluded for exactly the reason `#m-grid` is: it holds its own
   `.gridscroll`, and nesting a second horizontal scroller around it is the
   Android failure above. Results is the widest screen in the app — 15 boys by
   14 skills in 4th grade — so it is the one that would have shown it. */
.mrkwrap > *:not(#m-grid), .bldwrap > *, .tstwrap > *,
.stdwrap > *:not(#s-grid), .prgwrap > * {
  overflow-x: auto;
  overscroll-behavior-x: contain;
}
#m-grid, #s-grid { overflow: visible; }
.gridscroll, #sheet, .bpbody { overscroll-behavior-x: contain; }

/* ---- a boy's face, or his initials ---------------------------------------
   HERE AND NOT IN records.css. Moved 9 August 2026: `avatar()` is a shared
   helper in people.js, so every screen that calls it needs these rules, and
   only the records screen loaded the file they were in.
   One set of sizes, because there is one helper — people.js → avatar(). The
   initial version is in the yeshiva's own colour rather than grey: two letters in
   a warm circle say "we have not taken his picture yet", a grey silhouette says
   "missing data" about a child.
   ----------------------------------------------------------------------- */
.avatar{display:inline-grid;place-items:center;border-radius:50%;overflow:hidden;
  flex:none;background:var(--brand-soft);color:var(--brand);
  font-weight:700;letter-spacing:.5px;line-height:1;user-select:none}
.avatar img{width:100%;height:100%;object-fit:cover;display:block}
.avatar.noface{border:1px solid rgba(20,82,79,.14)}
.av-sm{width:30px;height:30px;font-size:11.5px}
.av-md{width:40px;height:40px;font-size:14px}
.av-lg{width:62px;height:62px;font-size:21px}
/* A Hebrew initial is a wider, taller letter than a Latin one and sits low in the
   line — without this it looks a size too big and slightly sunk. */
.avatar{font-family:'Assistant',system-ui,sans-serif}
@media (max-width:640px){ .av-lg{width:52px;height:52px;font-size:18px} }

/* Said to a screen reader and to nobody else. Used where a colour or a glyph
   carries meaning the eye can see and a reader cannot — an unmarked cell's
   em-dash, an N/A chip, the letter on a cell button.

   ⚠ IT NEEDS A POSITIONED ANCESTOR, and the first version did not have one. An
   absolutely positioned box with no positioned parent lays out against the page
   itself, so inside the marking grid — which is 924px wide inside a 354px
   scroller — every hidden label landed at its static offset out at 900px and
   stretched the DOCUMENT to 883px. The page began sliding sideways on a phone:
   the exact fault seven CSS attempts were spent on in August, reintroduced by an
   accessibility fix. `.sr-host` is the containing block that stops it, and
   anything carrying a `.sr` needs it. */
.sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}
.sr-host{position:relative}

:root{
  --brand:#14524f; --brand-2:#0f423f; --brand-soft:#e6efee;
  --bg:#f7f6f2; --panel:#ffffff; --line:#e3e1d8; --line-2:#eeece5;
  --ink:#171a21; --ink-2:#52514e; --ink-3:#898781;
  --sand-soft:#f3efe4; --sand-line:#ded6c2; --sand-ink:#5c5124;
  --crit:#d03b3b; --warn:#fab219; --good:#0ca30c;
  --present-soft:#f2f9f2; --present-line:#dfeedf;

  /* The tint, border and text that go WITH each status colour. Every screen in
     design/ already used these exact values; they had simply never been written
     down, so each new page was re-deriving them. Recorded in docs/COLORS.md.
     The three status colours above stay fixed and are never themed. */
  --crit-soft:#fbecec; --crit-line:#f2cfcf; --crit-ink:#96201f;
  --warn-soft:#fdf4e0; --warn-line:#f0dcb4; --warn-ink:#7a5305;
  --good-soft:#eaf6ea; --good-line:#cfe7cf; --good-ink:#006300;
  /* ⚠ GOING HOME EARLY HAS ITS OWN COLOUR SINCE 8 SEPTEMBER 2026, and it used
     to borrow the data blue. Menachem: *"the color for 'left early' should be a
     different color from the sets and P3 color. Right now, it's blue. Doesn't
     make sense that it's the same color."* He is right twice over — the blue is
     documented in docs/COLORS.md as what PROVISION A BOY RECEIVES looks like,
     and going home early is not provision; and `.rrow .mnynote` three hundred
     lines down already made this exact argument for the minyan note ("two
     facts wearing one colour is worse than no colour at all") while the gone
     chip quietly wore the blue anyway.
     Purple, because status owns red, amber and green, sand says "wants care",
     and blue is taken — blue is what he objected to, since it made going home
     read as a provision the boy receives. 8.36:1 on its own tint, and ΔE 16.4
     from `--data-ink`, which is the pair that matters: the two chips now sit
     side by side on a conduct row. The first try, #6a3d80, was ΔE 12.8 from
     the data blue and under the floor of 15 — computed, not eyeballed, which
     is the rule at the head of docs/COLORS.md, and the reason that was caught
     before it shipped. `node tools/colour_check.cjs` does the arithmetic. */
  --gone-soft:#f4edf7; --gone-line:#e0cde8; --gone-ink:#6b1f8a;
  /* WHITE ON `--good` IS 3.35:1 AND FAILS AA at the sizes the M chip uses.
     `--good-deep` is 5.32:1 with white and is what every M chip and every
     chosen M button is drawn on, so the letter M looks the same on the marking
     grid, on Results and on a talmid's page. `--good` itself is unchanged and
     still carries the non-text uses. Audit of 9 August, docs/ACADEMICS-AUDIT.md. */
  --good-deep:#0a7d0a;

  /* Sequential ramp for a count in a table cell — one hue, more is darker.
     Never a second hue: two colours in one series say two things. */
  --data:#2a78d6;
  --seq-0:#e3e1d8; --seq-1:#86b6ef; --seq-2:#3987e5; --seq-3:#1c5cab;

  /* The tint, border and text that go WITH the data blue — the same three-part
     shape the status colours have, and added for the same reason they were:
     `records.css` had these as literals on the P3 chip, which is exactly the
     position status was in before COLORS.md was written, and two of THOSE had
     already drifted by the time anybody looked. One user today; a token before
     there are two. Values unchanged — this is a rename, not a re-colour.
     Found on 13 August 2026 by mirroring the app into the design system. */
  --data-soft:#eaf1fb; --data-line:#cfe0f6; --data-ink:#1c5aa8;

  /* ---- five steps of the brand, for telling one box from the next ----------
     9 September 2026. Menachem, on the services scheduler: *"can each one of
     the boxes have a different shade of the website colors so it's easier to
     differentiate?"*

     ⚠ ALL FIVE ARE THE BRAND TEAL AND NOTHING ELSE, and that is a rule rather
     than a shortage. Red, amber and green mean *overdue*, *needs attention
     today* and *handled*; sand means *a special day*; the data blue means
     *provision a boy receives*. Every other hue in this file is spoken for, so
     tinting a box with one merely to separate it from its neighbour would make
     the screen say something it does not mean — the same argument this file
     makes about the birthday card and `--warn`.

     Depth carries the only meaning they have: the palest is the thing you READ,
     the deepest is the thing you TYPE INTO. A box still names itself in words,
     so the shade never carries the meaning alone.

     Recorded in docs/COLORS.md in the same commit as this line. */
  --teal-1:#f3f7f6; --teal-2:#eaf2f1; --teal-3:#e0ecea;
  --teal-4:#d3e3e1; --teal-5:#c6dad7;
  --teal-edge-1:#e2ebe9; --teal-edge-2:#d5e3e1; --teal-edge-3:#c4d8d5;
  --teal-edge-4:#b3ccc8; --teal-edge-5:#a2c0bc;
}
*{box-sizing:border-box}
html,body{height:100%}
body{
  margin:0; background:var(--bg); color:var(--ink);
  font:16px/1.55 'Assistant',system-ui,-apple-system,'Segoe UI',sans-serif;
  -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
}
.num{font-family:'TTOPNum','Assistant',sans-serif;font-feature-settings:'tnum' 1}
[hidden]{display:none !important}

/* --------------------------------------------------------------------------
   The preview bar.

   Sand, not red. A preview is not a warning and nothing is wrong — sand is the
   register for "this screen is in a special state", which is exactly what it is.
   Sticky, because scrolling out of sight is the whole failure mode.

   Built by mountNav, so it appears on every screen including ones not written
   yet. See nav.js.
   -------------------------------------------------------------------------- */
#previewbar{position:sticky;top:0;z-index:25;display:flex;align-items:center;gap:14px;
  background:var(--sand-soft);border-bottom:1px solid var(--sand-line);
  color:var(--sand-ink);padding:9px 18px;padding-top:calc(9px + env(safe-area-inset-top))}
#previewbar .pw{min-width:0;line-height:1.4}
/* ⚠ `> b` AND `> span`, NOT BARE. Until 4 September 2026 these were descendant
   selectors, and the sentence nav.js writes has a `<b>his</b>` INSIDE its
   span — so that one word was given `display:block` and `font-size:14px` too,
   and the bar came out as:
       Previewing as Rabbi Z. Rebbi
       Teacher. You are seeing his screens with
       his
       data — what you can see here is what he can see.
   The bar has said this since the preview was rebuilt on the 3rd, so it has
   looked like that on every preview since. Nothing could catch it: the markup
   is correct, the words are correct, and the fault is entirely in how it draws
   — found by putting the design copy INTO a preview and looking at the
   picture. The same one-character mistake was made and caught in the settings
   mock-up the same afternoon. */
#previewbar .pw > b{display:block;font-size:14px;font-weight:700}
#previewbar .pw > span{display:block;font-size:12.5px;opacity:.85}
#previewbar .pw > span b{display:inline;font-size:inherit;font-weight:700}
#previewbar button{margin-left:auto;flex:none;background:var(--sand-ink);border:0;color:#fff;
  border-radius:8px;padding:8px 15px;font:700 13.5px 'Assistant',sans-serif;cursor:pointer}
#previewbar button:hover{background:var(--ink)}
#previewbar button[disabled]{opacity:.6;cursor:default}
/* Two things stuck to top:0 would sit on top of each other. The preview bar is
   the one that must not scroll away, so the page's own bar gives up sticking
   for as long as a preview is on. */
body.previewing .topbar,
body.previewing .abar{position:static}
@media (max-width: 720px){
  #previewbar{padding:8px 13px;padding-top:calc(8px + env(safe-area-inset-top));gap:10px}
  /* Direct children here too, for the same reason as above — otherwise the
     phone width quietly re-introduces half of it. */
  #previewbar .pw > b{font-size:13px}
  #previewbar .pw > span{font-size:11.5px}
  #previewbar button{padding:7px 12px;font-size:12.5px}
}

/* ---- boot ---------------------------------------------------------------- */
.screen{min-height:100dvh}
#boot{display:grid;place-items:center}
.spinner{width:26px;height:26px;border:2.5px solid var(--line);
  border-top-color:var(--brand);border-radius:50%;animation:spin .7s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}

/* ---- the gate (sign in / not linked) ------------------------------------- */
#signin,#unlinked{display:grid;place-items:center;padding:28px}
.gate{max-width:400px;width:100%;text-align:center}
.gate .seal{width:76px;height:76px;margin-bottom:18px}
.wordmark{font-family:'FrankRuhl','Assistant',serif;font-size:34px;font-weight:500;
  margin:0;letter-spacing:.5px;color:var(--brand)}
.plain{font-size:24px;font-weight:700;margin:0 0 8px;letter-spacing:-.3px}
.gate .sub{color:var(--ink-2);margin:4px 0 26px;font-size:15px}
/* Scoped on purpose. An unscoped .sub also matched the second line of every
   name in the attendance roster and put 26px of air under each one. */
.fine{color:var(--ink-3);font-size:13.5px;line-height:1.6;margin:18px 0 0}
.fine b{color:var(--ink-2)}
/* The crit tokens — this rule carried three literals six values away from the
   tokens declared above it, which is exactly the drift they exist to stop. */
.err{margin:16px 0 0;padding:11px 14px;border-radius:9px;font-size:14px;
  background:var(--crit-soft);border:1px solid var(--crit-line);color:var(--crit-ink);text-align:left}

.gbtn{width:100%;display:flex;align-items:center;justify-content:center;gap:11px;
  background:var(--panel);color:var(--ink);border:1px solid var(--line);
  border-radius:10px;padding:14px 18px;font:600 15.5px 'Assistant',sans-serif;
  cursor:pointer;box-shadow:0 1px 2px rgba(23,26,33,.05);transition:.14s}
.gbtn:hover{border-color:var(--brand);box-shadow:0 2px 12px rgba(20,82,79,.11)}
.gbtn:active{transform:translateY(1px)}
.gbtn[disabled]{opacity:.55;cursor:default;transform:none}
/* "Email me a sign-in link" — 19 October 2026, design/sign-in-link.html */
.gate .or{display:flex;align-items:center;gap:10px;color:var(--ink-3);font-size:13px;margin:16px 0}
.gate .or::before,.gate .or::after{content:'';flex:1;height:1px;background:var(--line)}
.lbtn{width:100%;display:block;background:transparent;color:var(--brand);border:1.5px solid var(--brand);
  border-radius:10px;padding:13px 18px;font:600 15.5px 'Assistant',sans-serif;cursor:pointer}
.sin{width:100%;box-sizing:border-box;border:1.5px solid var(--line);border-radius:10px;background:var(--panel);
  padding:13px 14px;color:var(--ink);font:16px 'Assistant',sans-serif;margin:0 0 10px}
.sgo{width:100%;display:block;background:var(--brand);color:#fff;border:0;border-radius:10px;padding:14px 18px;
  font:700 15.5px 'Assistant',sans-serif;cursor:pointer}
.sgo[disabled]{opacity:.6;cursor:default}
.sok{background:var(--good-soft);border:1px solid var(--good-line);color:var(--good-ink);border-radius:10px;
  padding:12px 14px;text-align:left;font-size:14.5px;line-height:1.5;margin:22px 0 0}
.link{background:none;border:0;color:var(--ink-3);font:14px 'Assistant',sans-serif;
  text-decoration:underline;cursor:pointer;margin-top:22px;padding:6px}
.link:hover{color:var(--ink-2)}

/* ---- signed in ----------------------------------------------------------- */
.app{display:flex;flex-direction:column}
.bar{background:var(--brand);color:#fff;display:flex;align-items:center;gap:13px;
  padding:0 18px;height:60px;padding-top:env(safe-area-inset-top);
  height:calc(60px + env(safe-area-inset-top))}
.bar .mark{width:32px;height:32px;flex:none}
.bar .who{display:flex;flex-direction:column;line-height:1.25;min-width:0}
.bar .who b{font-size:15.5px;font-weight:600;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
.bar .who span{font-size:12px;opacity:.68;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
.out{margin-left:auto;background:rgba(255,255,255,.13);border:0;color:#fff;
  border-radius:8px;padding:8px 13px;font:600 13.5px 'Assistant',sans-serif;
  cursor:pointer}
.out:hover{background:rgba(255,255,255,.22)}

.wrap{width:100%;max-width:640px;margin:0 auto;padding:20px 18px 60px}

/* The content column a shell screen sits in — 28 Aug 2026, the spacing pass.
   The six screens built 25–27 August shipped without the one thing every
   settled screen has (`.offwrap`, `.cdwrap`, `.prgwrap`, …): a centred column
   carrying the app's gutters, so their content sat hard against the glass on
   a phone and against the menu on a desktop. This is that column ONCE, not a
   seventh copy — the gutters are the app's, only the width is a screen's own
   fact, set through its body class (`.bl .pgwrap{max-width:900px}`).
   width:100% because `.work` is a flex column and `margin:0 auto` turns off
   the stretch — the same note conduct.css and seating.css carry.
   In print it gets out of the way entirely: the bus checklist and the bus
   lists were designed to let @page own the margins, and a wrap that padded
   the paper would shrink a sheet held at a kerb. */
.pgwrap{width:100%;max-width:640px;margin:0 auto;padding:20px 18px 60px}
@media print{ .pgwrap{max-width:none;margin:0;padding:0} }

.golinks{margin:0 0 14px}
a.go{display:flex;align-items:baseline;gap:10px;text-decoration:none;background:var(--brand);
  color:#fff;border-radius:12px;padding:16px 20px;margin-bottom:9px;
  box-shadow:0 2px 10px rgba(20,82,79,.18)}
a.go b{font-size:17px;font-weight:700}
a.go span{font-size:14px;opacity:.78;margin-left:auto}
a.go:active{background:var(--brand-2)}
a.go.quiet{background:var(--panel);color:var(--ink);border:1px solid var(--line);
  box-shadow:0 1px 2px rgba(23,26,33,.04)}
a.go.quiet span{color:var(--ink-3);opacity:1}
a.go.quiet:hover{border-color:var(--brand)}
a.go.quiet:active{background:var(--bg)}
.golinks .fine{margin:6px 2px 0}

/* ---- the secretary's desk ------------------------------------------------
   A greeting, then two boxes. Only the office and hanhala see this; a rebbi's
   front door is his one button and the day, and it should stay that.
   ----------------------------------------------------------------------- */
.greet{margin:0 0 14px;font-size:25px;font-weight:700;letter-spacing:-.5px;
  color:var(--ink);line-height:1.2}

/* Two real columns rather than grid auto-placement. The boxes are wildly
   different heights and auto-placement would hang the inbox off the bottom of
   whatever the left column happened to be that morning. */
.deskgrid{display:grid;gap:12px;grid-template-columns:1fr;margin:0 0 16px}
.dcol{display:flex;flex-direction:column;gap:12px;min-width:0}
/* The day card was written to stand on its own; inside a column it takes the
   column's spacing like everything else. */
.dcol .today{margin:0}
@media (min-width: 900px){ .deskgrid{grid-template-columns:1.15fr 1fr} }

.dbox{background:var(--panel);border:1px solid var(--line);border-radius:12px;
  padding:16px 18px 15px;box-shadow:0 1px 2px rgba(23,26,33,.04)}
.dbox h3{margin:0 0 11px;font-size:12px;text-transform:uppercase;letter-spacing:.9px;
  color:var(--ink-3);font-weight:700}

.dlist{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}
.dlist a{display:block;text-decoration:none;color:inherit;border-radius:9px;
  padding:10px 12px;border:1px solid var(--line);background:var(--bg)}
.dlist a:hover{border-color:var(--brand);box-shadow:0 1px 8px rgba(20,82,79,.08)}
.dlist b{display:block;font-size:15px;font-weight:600;line-height:1.3}
.dlist span{display:block;font-size:12.5px;color:var(--ink-3);line-height:1.5;margin-top:2px}
/* The two that mean somebody is unaccounted for read louder than the rest, and
   the colour is never the only thing saying so — the words do. */
.dlist li.crit a{background:var(--crit-soft);border-color:var(--crit-line)}
.dlist li.crit b{color:var(--crit-ink)}
.dlist li.warn a{background:var(--warn-soft);border-color:var(--warn-line)}
.dlist li.warn b{color:var(--warn-ink)}
/* A boy sent from the bus — 15 October 2026. School-bus yellow, which is a
   picture of a bus and not a status (design-system.md), and a band with a bus
   in it across the top of the card. */
.dlist li.bus a{background:var(--warn-soft);border-color:var(--warn);padding-top:0;overflow:hidden}
.dlist li.bus b{color:var(--warn-ink)}
.dlist .dbus{display:flex;align-items:center;gap:7px;margin:0 -12px 8px;padding:6px 12px;
  background:var(--warn);color:#3d2a02;font-size:12.5px;font-weight:700}
.dlist .dbus svg{flex:none}
.dlist .dbus em{font-style:normal}
.dlist .dbus i{margin-left:auto;font-style:normal;font-weight:600}

.dnone{margin:0;font-size:13.5px;line-height:1.65;color:var(--ink-2)}

/* ---- the Hayom Yom card, first in his left column -------------------------
   A `.dbox` like the rest of the desk; only the date line and the button
   underneath it are new. The Hebrew date leads because that is what the entry
   is keyed to and what he is looking for; the English date sits at the end of
   the line, quiet, the way the day card sets its own Hebrew.
   ----------------------------------------------------------------------- */
/* ⚠ THE HEADING IS HEBREW, SO IT LOSES THE LABEL LETTER-SPACING. `.dbox h3`
   tracks its label out by .9px, which is what makes a small uppercase Latin
   heading read as a label — Hebrew has no case for `text-transform` to act on
   and tracking it out just breaks the word up. Seen in the first screenshot,
   which is the only place it was ever going to be seen. */
.hy h3{letter-spacing:0}
.hy .hydate{margin:0 0 13px;display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
.hy .hydate .he{font-family:'TTOPNum','Assistant',sans-serif;font-size:19px;
  font-weight:600;color:var(--ink)}
.hy .hydate .en{font-size:13.5px;color:var(--ink-3);margin-left:auto}
/* `a.go` carries its own margin-bottom for a stack of buttons; in a card it is
   the last thing and the card's padding is the space. */
.hy a.go{margin-bottom:0}

/* ---- the inbox, under what needs you -------------------------------------
   Separated by a rule rather than a heading: it is the same question — what is
   waiting — coming from somewhere else. The subject leads and the sender sits
   under it, because in a list of five she is looking for the matter, not the name.
   ----------------------------------------------------------------------- */
/* The account the box is reading. Quiet, and always there — see whose() in app.js. */
.dwho{margin:-4px 0 9px;font-size:11px;color:var(--ink-3);letter-spacing:.2px}
.dmh{margin:0 0 8px;font-size:12.5px;color:var(--ink-3)}
.dmh b{color:var(--ink-2);font-weight:700}
/* From staff (app.js → paintFromStaff, 25 September 2026): a dot on a note
   not yet opened; and outside the desk it spans the grid like #t-week. */
.dlist li.fsnew b::before{content:'';display:inline-block;width:8px;height:8px;border-radius:50%;
  background:var(--brand);margin-right:7px;vertical-align:1px}
main.wrap > .fromstaff{grid-column:1 / -1}
/* You are covering (covercard.js, 7 October 2026) — on every front door, so it
   spans the grid like the notes from staff, and its rows are not links. */
main.wrap > .covering{grid-column:1 / -1}
.covering .dlist a:hover{border-color:var(--line);box-shadow:none}
.cvtag,.dlist .cvtag{display:inline-block;margin-top:0;margin-right:8px;padding:1px 6px;border-radius:5px;vertical-align:1px;
  font:700 10.5px 'Assistant',sans-serif;letter-spacing:.06em;background:var(--line-2);color:var(--ink-2)}
.cvtag.now{background:var(--brand);color:#fff}
.dlist li.mail a{background:var(--panel)}
.dlist li.mail b{font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dsoon a{color:var(--brand);text-decoration:none;font-weight:600}
.dsoon a:hover{text-decoration:underline}

/* Her day. The time leads, because on a page you glance at, "when" is the thing
   you are looking for and the title is what you read once you have found it. */
.cday{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:9px}
.cday li{display:flex;gap:11px;align-items:baseline}
.cday .ct{flex:0 0 72px;font-family:Heebo,'Assistant',sans-serif;font-size:12.5px;
  font-weight:600;color:var(--brand);white-space:nowrap}
.cday .cw{min-width:0}
.cday .cw b{display:block;font-size:14px;font-weight:600;line-height:1.35}
.cday .cw span{display:block;font-size:12px;color:var(--ink-3);line-height:1.45}
/* Not built yet, and it should look like it — quiet, and honest about why. */
.dsoon{margin:11px 0 0;padding-top:11px;border-top:1px solid var(--line-2);
  font-size:12px;line-height:1.6;color:var(--ink-3)}
.dsoon b{color:var(--ink-2);font-weight:600}

/* --------------------------------------------------------------------------
   THE MORNING BUSES — 31 August 2026, and this block exists because the box
   had none.

   ⚠ IT SHIPPED WITH THREE CLASSES THAT ARE NOT ON THIS PAGE. `.cardnote`,
   `.choices` and `.pvopen` all live in `records.css`; `index.html` loads
   fonts, style, nav and welcome, and nothing else. So the only screen that
   ever draws this box drew three default grey browser buttons, touching each
   other, under an unstyled paragraph — on the Principal's front door, which is
   the one screen in the app he looks at every single morning.

   `_test-desk.html` loads the same four stylesheets, so the design copy was
   showing exactly the same thing. It was not hidden. Nobody had looked.

   The button below is sized for what it actually is: one tap, taken quickly,
   by somebody holding a telephone in the other hand and watching a bus pull
   in. Teal fill, because this is the rare thing on the desk that is an ACTION
   rather than a count or something owed — the same brand fill the send button
   carries on a rebbi's attendance list.
   -------------------------------------------------------------------------- */
.busnote{margin:0 0 12px;font-size:13px;line-height:1.6;color:var(--ink-2)}

/* Wraps rather than scrolls, for the same reason the birthday card does: a
   horizontal scroller hides the last item from anybody who does not drag it,
   and here the hidden one would be a bus. */
.busrow{display:flex;flex-wrap:wrap;gap:10px}
.busrow[hidden]{display:none}
/* `max-width` so the last bus left in the row does not become one enormous
   button the width of the card — they share the row while there are several
   and keep their size when there is one. */
.busb{flex:1 1 92px;max-width:200px;min-height:62px;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:1px;
  background:var(--brand);border:1px solid var(--brand);border-radius:12px;
  color:#fff;cursor:pointer;font-family:'Assistant',system-ui,sans-serif;
  box-shadow:0 1px 3px rgba(20,82,79,.18);-webkit-tap-highlight-color:transparent}
.busb .busw{font-size:10.5px;font-weight:700;letter-spacing:.12em;
  text-transform:uppercase;opacity:.8}
.busb .busn{font-size:23px;font-weight:700;line-height:1.05;letter-spacing:-.4px;
  font-variant-numeric:tabular-nums}
.busb:hover{background:var(--brand-2);border-color:var(--brand-2)}
.busb:active{transform:translateY(1px)}
.busb:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.busb[disabled]{opacity:.5;cursor:default;transform:none}

/* The receipt for a press, one line per bus, each holding its own Undo. Three
   tones and the colour is never the only thing saying which: `ok` the bus is
   in, `note` something was taken back or nobody was told, `bad` the record
   itself did not happen. */
.busdone{list-style:none;margin:12px 0 0;padding:0;display:flex;
  flex-direction:column;gap:7px}
.busdone li{display:flex;align-items:center;gap:9px;padding:8px 9px 8px 11px;
  border-radius:10px;font-size:12.5px;line-height:1.5;color:var(--ink-2);
  background:var(--good-soft);border:1px solid var(--good-line)}
.busdone li b{color:var(--good-ink);font-weight:700}
.busdone li.note{background:var(--sand-soft);border-color:var(--sand-line)}
.busdone li.note b{color:var(--sand-ink)}
.busdone li.bad{background:var(--crit-soft);border-color:var(--crit-line)}
.busdone li.bad b{color:var(--crit-ink)}
.busdone .bustx{flex:1 1 auto;min-width:0}

/* Quiet on purpose. Undo is the right thing to be able to do and the wrong
   thing to be drawn towards — pressing a bus again after taking it back sends
   a second notification to every telephone. */
.busundo{flex:0 0 auto;background:var(--panel);border:1px solid var(--line);
  border-radius:8px;padding:5px 11px;font:600 12.5px 'Assistant',sans-serif;
  color:var(--ink-2);cursor:pointer;white-space:nowrap}
.busundo:hover{border-color:var(--ink-3);color:var(--ink)}
.busundo:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.busundo[disabled]{opacity:.5;cursor:default}

/* ---- allowed to come to the office — 2 September 2026 ----------------------
   ⚠ IN style.css AND NOT IN A SCREEN'S OWN SHEET, which is the bus box's scar
   of 31 August paid forward: `index.html` loads fonts, style, nav and welcome,
   and nothing else. A class borrowed from records.css would draw grey default
   buttons on the only page that ever shows this box.
   ------------------------------------------------------------------------- */
.passnote{margin:0 0 10px;font-size:12.5px;color:var(--ink-2);line-height:1.5}
.passbad{margin:0;padding:9px 11px;border-radius:9px;font-size:13px;
  line-height:1.55;background:var(--crit-soft);border:1px solid var(--crit-line);
  color:var(--crit-ink)}
.passlist{list-style:none;margin:0;padding:0;display:flex;
  flex-direction:column;gap:8px}
.passlist li{display:flex;align-items:center;gap:10px;padding:9px 10px 9px 12px;
  border-radius:10px;background:var(--bg);border:1px solid var(--line)}
.passwho{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}
.passwho b{font-size:14.5px;color:var(--ink)}
.passfrom{font-size:12px;color:var(--ink-2)}
/* His own words, if the mark carried any. Its own line and its own weight,
   the way the office's print card draws an instruction — a sentence tucked in
   beside a name is a sentence she skims past. */
.passwhy{font-size:12.5px;color:var(--ink);font-weight:600}
.passb{flex:0 0 auto;min-height:44px;padding:6px 13px;border-radius:9px;
  border:1px solid var(--brand);background:var(--panel);color:var(--brand-2);
  font:600 13px 'Assistant',sans-serif;cursor:pointer;white-space:nowrap}
.passb:hover{background:var(--brand-soft)}
.passb:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.passb[disabled]{opacity:.6;cursor:default}

@media (max-width:430px){
  /* The name and the button stop competing for one line: on a phone the button
     goes full width under him, where a thumb already is. */
  .passlist li{flex-wrap:wrap}
  .passb{flex:1 1 100%}
}

@media (max-width:430px){
  .busb{min-height:56px;flex:1 1 78px}
  .busb .busn{font-size:21px}
}

/* --------------------------------------------------------------------------
   THE DEAN'S FRONT DOOR — 31 August 2026.

   Three lists and a search box. ONE COLUMN, not the desk's two, and the type is
   a size up on the names: Menachem's reason for the whole screen was *"he isnt
   so tech savvy so i dont want to overwhelm him with information"*, and two
   columns of small print is the shape of being overwhelmed.

   Each row is NAME on the left and the one fact about him on the right — why he
   is absent, when he walked in, when he is going. That is the whole vocabulary
   of this screen, repeated three times, so there is one thing to learn.
   -------------------------------------------------------------------------- */
#t-dean{display:flex;flex-direction:column;gap:12px;margin:0 0 16px}
#t-dean .today{margin:0}

.dnbox h3{display:flex;align-items:baseline;gap:8px}
/* The count sits IN the heading rather than at the end of the list, because on
   a phone the end of the list is off the screen. */
.dncount{font-size:11px;font-weight:700;letter-spacing:0;padding:1px 7px;
  border-radius:99px;background:var(--sand-soft);color:var(--sand-ink)}

.dnlist{list-style:none;margin:0;padding:0}
.dnlist li{display:flex;justify-content:space-between;gap:14px;align-items:baseline;
  padding:10px 0;border-top:1px solid var(--line-2)}
.dnlist li:first-child{border-top:0;padding-top:2px}
.dnwho{min-width:0}
.dnname{font-size:15.5px;font-weight:600;color:var(--ink);text-decoration:none;
  line-height:1.3}
.dnname:hover,.dnname:focus-visible{color:var(--brand);text-decoration:underline}
.dnclass{display:block;font-size:12px;color:var(--ink-3);line-height:1.45}
.dnwhat{flex:0 0 auto;font-size:13px;color:var(--ink-2);text-align:right;line-height:1.45}
/* The one thing on this screen that is a job rather than a fact: an absence
   nobody has explained. It is said in words as well — the colour is never
   carrying it alone. */
.dnopen{color:var(--crit-ink);font-weight:600}

.dnnote{margin:0;font-size:13.5px;line-height:1.65;color:var(--ink-2)}
.dnnote.warn{color:var(--crit-ink);font-weight:600}

/* ⚠ `.dnq` / `.dnfind` / `.dnhits` ARE GONE — 2 September 2026. The Dean's own
   search box was replaced by the office's, faces and all, on his instruction:
   *"remove his current student search and replace it with the same student
   search component used by the secretary, including the student pictures."*
   The rules that dressed the old one went with it rather than being left to rot
   under a selector nothing emits. `deskfind.js` and `.dfind` below draw it now.

   ---- WHICH DAY HE IS LOOKING AT --------------------------------------------
   *"add the ability to go backward in time and select a previous date … in case
   he forgot to mark attendance for a student on a particular day."*

   It sits directly above the three lists because it governs them, and it says
   the day in words when it is not today — a date input alone reads as a filter
   nobody has touched, and the whole hazard here is him reading Friday's
   absences as this morning's. */
.dnday{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.dndaypick{display:flex;align-items:center;gap:8px;font-size:13.5px;font-weight:600;
  color:var(--ink-2)}
.dnday input[type=date]{padding:9px 11px;border-radius:9px;border:1px solid var(--line);
  background:var(--bg);color:var(--ink);font:14.5px/1.2 'Assistant',system-ui,sans-serif;
  min-height:40px}
.dnday input[type=date]:focus{outline:2px solid var(--brand);outline-offset:1px}
.dndaynote{font-size:13px;color:var(--ink-3)}
/* Not today: said in the sand tones the rest of the app uses for "this is not
   the ordinary state", so a glance is enough to know the lists are historic. */
.dndaynote.back{background:var(--sand-soft);border:1px solid var(--sand-line);
  color:var(--sand-ink);border-radius:8px;padding:5px 10px;font-weight:600}
.dnbacktoday{background:none;border:0;padding:0;cursor:pointer;font:inherit;
  color:var(--brand);font-weight:700;text-decoration:underline}
.dnfix{margin-left:auto;font-size:13.5px;font-weight:600;color:var(--brand);
  text-decoration:none;padding:8px 0}
.dnfix:hover{text-decoration:underline}
@media (max-width:820px){
  .dnday{gap:9px}
  .dnfix{margin-left:0;flex:1 1 100%}
}

/* ---- WHO IS THIS BOY — the office's search, with faces ---------------------
   Menachem, 31 August 2026: *"many students come to her and she doesn't know
   who they are … it should pop up their photo in the search."*

   ⚠ THE FACE IS THE ANSWER AND IT IS SIZED LIKE ONE. The Dean's box above is a
   list of names because he already knows which boy he means; hers runs the
   other way round — a child is at the desk and the photograph is what tells her
   whether she has the right one. `av-lg` is 62px: big enough to recognise a
   nine-year-old across a counter, which a 32px roster thumbnail is not. */
.dfq{width:100%;box-sizing:border-box;padding:12px 14px;border-radius:10px;
  border:1px solid var(--line);background:var(--bg);color:var(--ink);
  font:16px/1.4 'Assistant',system-ui,sans-serif}
.dfq:focus{outline:2px solid var(--brand);outline-offset:1px;border-color:var(--brand)}
.dfind .dnone{margin-top:9px}
.dfhits{list-style:none;margin:10px 0 0;padding:0;display:flex;flex-direction:column;gap:7px}
.dfhit{display:flex;align-items:center;gap:12px;padding:9px 12px;
  border:1px solid var(--line);border-radius:11px;background:var(--bg);
  text-decoration:none;color:var(--ink)}
.dfhit:hover,.dfhit:focus-visible{border-color:var(--brand);
  box-shadow:0 1px 8px rgba(20,82,79,.08)}
.dfhit .avatar{flex:0 0 auto}
.dfwho{min-width:0;display:flex;flex-direction:column;gap:2px}
.dfwho b{font-size:16px;font-weight:700;line-height:1.25}
.dfclass{font-size:13px;color:var(--ink-2)}
/* ⚠ SAID IN WORDS, not left as a circle of initials. 61 of 186 enrolled boys
   have no photograph on file, so about one search in three cannot answer her
   real question — and a screen that quietly shows letters instead reads as a
   design choice rather than a gap somebody could close. */
.dfnoface{font-size:12px;color:var(--sand-ink);font-weight:600}

@media (max-width:430px){
  .dnlist li{gap:10px}
  .dnwhat{font-size:12.5px}
}

.today{background:var(--panel);border:1px solid var(--line);border-radius:12px;
  padding:18px 20px;box-shadow:0 1px 2px rgba(23,26,33,.04)}
.today .hdr{display:flex;align-items:baseline;gap:14px;flex-wrap:wrap}
.today h2{margin:0;font-size:20px;font-weight:700;letter-spacing:-.3px}
.today .heb{font-size:17px;color:var(--ink-2);margin-left:auto}
.tags{display:flex;flex-wrap:wrap;gap:7px;margin-top:12px}
.tag{font-size:12.5px;font-weight:700;letter-spacing:.3px;border-radius:6px;
  padding:4px 9px;background:var(--brand-soft);color:var(--brand)}
.tag.off{background:var(--sand-soft);color:var(--sand-ink);border:1px solid var(--sand-line)}
.tag.ws{background:#fff;color:var(--brand);border:1px solid var(--brand);
  box-shadow:inset 3px 0 0 var(--brand)}
.hours{margin-top:15px;border-top:1px solid var(--line-2);padding-top:13px;
  display:grid;grid-template-columns:auto 1fr;gap:6px 18px;font-size:15px}
.hours dt{color:var(--ink-2)}
.hours dd{margin:0}
.hours .num{font-family:'TTOPNum','Assistant',sans-serif;font-feature-settings:'tnum' 1}
.hours .ampm{font-family:'Assistant',sans-serif;color:var(--ink-2);font-size:13.5px}
.hours .chg{font-family:'Assistant',sans-serif;color:var(--brand);font-size:13px;
  font-weight:600;margin-left:8px}
.event{margin:11px 0 0;font-size:15px;font-weight:600;color:var(--brand);line-height:1.45}
.tnote{margin:13px 0 0;font-size:14px;color:var(--ink-2);line-height:1.6}

/* `.panel` and `.checks` were the "What is working" box on the front door and
   nothing else. Removed with it on 24 August 2026. Do not bring the names back
   for something unrelated — a `.panel` that is not that panel is how a
   stylesheet stops meaning anything. */

/* --------------------------------------------------------------------------
   Desktop. Not a stretched phone: the two panels sit beside each other, and
   the way in to attendance stays the first thing on the page.
   -------------------------------------------------------------------------- */
@media (min-width: 900px) {
  .bar{padding:0 26px;height:calc(64px + env(safe-area-inset-top))}
  .bar .mark{width:34px;height:34px}
  .bar .who b{font-size:16.5px}
  .bar .who span{font-size:12.5px}

  .wrap{max-width:980px;padding:26px 26px 70px;
    display:grid;grid-template-columns:1fr 1fr;gap:16px;align-items:start}
  .golinks{grid-column:1 / -1;margin:0}
  /* The greeting and the two boxes are the width of the page, not a column of it
     — the wrap becomes a two-column grid here and without this the greeting took
     one cell and both boxes were squeezed into the other. */
  .greet{grid-column:1 / -1;margin:0}
  .deskgrid{grid-column:1 / -1;margin:0}
  /* ⚠ AND THE BIRTHDAY CARD TOO — missed when it was added on 26 August, and
     it is the same fault the comment above describes. Without this the card
     takes ONE CELL: half a card on the desk with an empty half beside it, and
     on a rebbi's door it takes the left column and pushes his whole day, his
     tiles and his class list into the right one. It has been like that on
     every birthday since. */
  #t-birthdays{grid-column:1 / -1}
  /* ⚠ THE 720px CAP IS GONE — his instruction, 14 September 2026: *"rework his
     homepage to have multiple screens, multiple boxes instead of having one
     long row … multiple lists side by side."* It read "The Dean's door is one
     column by design", and that was right when the door held three lists and a
     search; it now holds five boxes and a man reading it at two o'clock was
     scrolling past four of them.

     The span stays — the door still takes the whole of the wrap's two columns.
     What changed is that it no longer stops at 720px, so `.dncols` below has
     the width to put the boxes beside each other. */
  #t-dean{grid-column:1 / -1}
  /* ⚠⚠ AND THE WEEK AND "LEFT THE YESHIVA" — found 24 September 2026, a day
     after the week card was added above every door. It took ONE cell of this
     grid, and its seven day tiles are wider than half the page, so its column
     grew past the edge (Monday cut off) and squeezed the other column to
     ~190px. The provider's whole day and a rebbi's door sat in that strip. The
     birthday card's scar above, a third time: a card above the doors spans. */
  #t-week,#t-departures{grid-column:1 / -1}
  a.go{padding:18px 24px;margin:0}
  a.go b{font-size:18px}
  .today{margin:0;padding:20px 24px}
  .today h2{font-size:22px}
  .today .heb{font-size:18px}
  .hours{font-size:15.5px}

  .gate{max-width:420px}
  .gbtn:hover{transform:translateY(-1px)}
}


/* ---- the legend to the four letters ---------------------------------------
   I · NI · S · M, said in words, drawn by marksview.js -> legend().

   IT LIVES HERE AND NOT IN A SCREEN'S STYLESHEET — moved out of standing.css on
   11 August 2026. Any screen may import the module, and on that day a third one
   did: the marking grid loads tests.css, never loaded standing.css, and drew the
   key as a bare row of letters. A shared module whose CSS sits in one screen's
   sheet is a shared module that works on one screen.

   The M chip is on `--good-deep` and not `--good`. White on `--good` is 3.35:1
   and fails AA at this size; see the note on the token.
   -------------------------------------------------------------------------- */
.mvlegend{display:flex;flex-wrap:wrap;gap:6px 14px;align-items:center;
  margin:0 0 14px;padding:9px 12px;border-radius:10px;
  border:1px solid var(--line-2);background:var(--panel)}
.mvlegend .lg{display:inline-flex;gap:6px;align-items:center}
.mvlegend .lg b{display:inline-block;min-width:26px;text-align:center;
  padding:2px 6px;border-radius:6px;font-size:11.5px;font-weight:700}
.mvlegend .lg i{font-style:normal;font-size:11.5px;color:var(--ink-2)}
.mvlegend .lg.l-I  b{background:var(--crit-soft);color:var(--crit-ink)}
.mvlegend .lg.l-NI b{background:var(--warn-soft);color:var(--warn-ink)}
.mvlegend .lg.l-S  b{background:var(--good-soft);color:var(--good-ink)}
.mvlegend .lg.l-M  b{background:var(--good-deep);color:#fff}
.mvlegend .lg.na   b{background:var(--line-2);color:var(--ink-2);font-size:10.5px}
.mvlegend .lg.none b{background:none;color:var(--ink-2)}


/* ---- a quiet control in a screen's own header ------------------------------
   ⚠ MOVED HERE FROM `seating.css` ON 12 AUGUST, for the same reason `.mvlegend`
   moved: a second screen needed it. Conduct's list now carries "See it as a
   room" in its header the way the room carries "See it as a list" in its own,
   and a rule that lives in one screen's stylesheet is a rule that works on one
   screen.

   It is used on both a <button> and an <a>, so it has to reset both — the
   underline is why the room's own "See it as a list" had one until now.

   ⚠ THIS IS NOT YET THE ONLY `.tbtn`. `tests.css` and `marksheet.css` each still
   declare their own, identical to each other and a shade larger than this one,
   and `build.css` leans on the `tests.css` copy. They win over this rule for
   every property they name, so those three screens are unchanged — but they now
   inherit whatever this rule adds that they do not name. That is why there is
   nothing here about wrapping or sizing: a `white-space` or a `padding` set here
   would reach four screens, and three of them have not been rendered today.
   Folding those three into this one is a separate job, and it needs measuring,
   not assuming. */
.tbtn{display:inline-block;background:var(--panel);border:1px solid var(--line);
  border-radius:9px;padding:8px 13px;font:600 13.5px 'Assistant',sans-serif;
  color:var(--ink-2);text-decoration:none;cursor:pointer}
.tbtn:hover{border-color:var(--ink-3);color:var(--ink)}
.tbtn:focus-visible{outline:2px solid var(--brand);outline-offset:2px}

/* ==========================================================================
   A BOY'S NAME IS THE WAY TO HIS RECORD — Menachem, 13 August 2026: "any time
   the boys name is in the app barring attendance ... clicking a boys name takes
   you to his profile."

   It inherits its colour and carries no underline at rest, on purpose. These
   names sit in grid headers, worklists and caseload columns where a page of
   blue underlined text would read as a page of warnings; the underline arrives
   on hover and on keyboard focus, which is where a person is asking "can I
   click this?".

   ⚠ IT SETS NO `display`, DELIBERATELY. The first version used
   `display: contents`, which is tidy for a link wrapping two stacked name spans
   and is a known accessibility hazard on interactive elements in older
   browsers. It is also unnecessary: `standing.css` was ALREADY styling
   `th.nm a` as a block — the stylesheet had been written expecting this link
   before it existed — and every other screen wraps either plain text or one
   block span, where an inline anchor is exactly right. Leaving `display` alone
   lets each screen's own rule win instead of fighting it.
   ========================================================================== */
.tlink { color: inherit; text-decoration: none; }
.tlink:hover, .tlink:focus-visible { text-decoration: underline; cursor: pointer; }

/* --------------------------------------------------------------------------
   THE VISUAL PASS, stage one — 16 August 2026, on Menachem's word
   ("lets update the design"). Three things and no more, nine days before
   school: the icon class the sprite renders through, the focus standard from
   docs/DESIGN-SYSTEM-PROPOSAL.md §3, and nothing that moves a pixel anybody
   has memorised. The control consolidation is stage two and goes screen by
   screen through the test pages first.

   THE FOCUS STANDARD. One ring for the whole app: 2px brand, 2px off. On the
   dark surfaces — the teal nav, the top bars — the same ring in white,
   because a teal ring on a teal bar is invisible, which is the old .navfold
   rule generalised. The four places that used to suppress the outline now
   suppress it for MOUSE focus only (:focus:not(:focus-visible)), so the
   keyboard ring survives everywhere. This closes the audit finding that only
   seven controls in the whole app had any keyboard focus at all.
   -------------------------------------------------------------------------- */
.icn{width:20px;height:20px;display:block;stroke:currentColor;fill:none;
  stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;pointer-events:none}
/* Any square button that now holds an icon centres it, whatever its old
   text-glyph sizing was. */
#burger,.abburger,.rbar .burger,.navfold,#sc-close,.scmx,.stdclose,.bpx,.brm{
  display:inline-flex;align-items:center;justify-content:center}
button:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible,
textarea:focus-visible, [tabindex]:focus-visible{
  outline:2px solid var(--brand); outline-offset:2px}
.topbar :focus-visible, .abar :focus-visible, #nav :focus-visible,
.rbar :focus-visible, .navtop :focus-visible{outline-color:#fff}

/* ---- the rebbi's front door ----------------------------------------------
   Menachem's ruling of 17 August: a rebbi opens onto his class, not onto the
   office's morning. Two arrangements were drawn and he chose his boys.

   These rules are the READING half of the conduct list's look — the same row,
   the same blue, the same tone colours, with every control taken off. They are
   written here rather than in conduct.css because the front page loads only
   style.css, and they are named apart (`.rrow`, not `.cdrow`) so that neither
   screen can quietly restyle the other.

   ⚠ THE TONE COLOURS BELOW ARE THE THIRD COPY IN THIS REPO — conduct.css and
   seating.css hold the other two. They agree today. The right fix is one set of
   `--tone-*` tokens that all three read, and it is on the list; three copies of
   a colour is how the status colours drifted before COLORS.md was written.
   ------------------------------------------------------------------------ */
.rstrip{display:flex;gap:7px;margin:0 0 14px;flex-wrap:wrap}
.rtile{flex:1 1 0;min-width:96px;text-align:center;text-decoration:none;
  border-radius:10px;padding:10px 6px;border:1px solid var(--line);
  background:var(--panel);color:inherit;min-height:44px}
a.rtile:hover{border-color:var(--brand)}
.rtile .n{display:block;font-family:'TTOPNum','Assistant',sans-serif;
  font-feature-settings:'tnum' 1;font-weight:700;font-size:19px;line-height:1.15;
  color:var(--ink)}
.rtile .l{display:block;font-size:11.5px;color:var(--ink-3);margin-top:2px}
.rtile.due{background:var(--crit-soft);border-color:var(--crit-line)}
.rtile.due .n,.rtile.due .l{color:var(--crit-ink)}
.rtile.ok{background:var(--good-soft);border-color:var(--good-line)}
.rtile.ok .n,.rtile.ok .l{color:var(--good-ink)}

.rclass{margin:0 0 16px}
.rhead{display:flex;align-items:baseline;gap:10px;margin:0 0 9px}
.rhead h2{font-size:17px;font-weight:700;margin:0;letter-spacing:-.2px}
.rmore{margin-left:auto;font-size:13px;font-weight:600;color:var(--brand);
  text-decoration:none}
.rmore:hover{text-decoration:underline}

.rroster{list-style:none;margin:0;padding:0;border:1px solid var(--line);
  border-radius:12px;background:var(--panel);overflow:hidden}
.rroster li{border-bottom:1px solid var(--line-2)}
.rroster li:last-child{border-bottom:0}

.rrow{display:flex;align-items:center;gap:9px;width:100%;padding:11px 13px;
  text-decoration:none;color:inherit;min-height:44px}
.rrow:hover{background:var(--brand-soft)}
.rrow:focus-visible{outline:2px solid var(--brand);outline-offset:-2px}
.rrow .who{flex:1;min-width:0}
.rrow .en{display:block;font-weight:600;font-size:15px}
.rrow .he{display:block;font-family:FrankRuhl,'Assistant',serif;
  font-size:13px;color:var(--ink-3);direction:rtl}

/* The blue, to the letter of the conduct list's rule. */
.rrow .svc{font-size:9.5px;background:var(--data-soft);color:var(--data-ink);
  font-weight:700;border-radius:4px;padding:1px 5px;white-space:nowrap;
  display:inline-block;margin-top:2px}
.rrow.hassvc .who .he{color:var(--data-ink)}

/* Missed minyan, on the rebbi's front door — 26 August 2026. Sand, not the
   services blue, because blue already means "he is out at services" on this
   exact row and two facts wearing one colour is worse than no colour at all.
   Not red either: a boy who missed minyan is not absent, and red on an
   attendance row means a letter is going to his family. It is a NOTE and never
   a control — the words carry it on their own, which is rule 1. */
.rrow .mnynote{font-size:9.5px;background:var(--sand-soft);color:var(--sand-ink);
  font-weight:700;border-radius:4px;padding:1px 5px;white-space:nowrap;
  display:inline-block;margin-top:2px;margin-left:4px}

/* GOING HOME EARLY, on all three of this screen's lists — 8 September 2026,
   and it was on none of them before. Plum by the argument the minyan note
   makes directly above: blue on this row already means "out at services", and
   a boy leaving at one o'clock is a different fact. `.gone` is toggled by the
   ticker, never redrawn, so the words swap from "leaves at" to "left at"
   without disturbing an open mark panel. */
.rrow .gonote{font-size:9.5px;background:var(--gone-soft);color:var(--gone-ink);
  font-weight:700;border-radius:4px;padding:1px 5px;white-space:nowrap;
  display:inline-block;margin-top:2px;margin-left:4px}
.rrow .gonote .post{display:none}
.rrow .gonote.gone .pre{display:none}
.rrow .gonote.gone .post{display:inline}
.rrow.outnow,.rrow.outnow:hover{background:var(--data-soft)}
.rrow.outnow .who .en{color:var(--data-ink)}

.rpill{flex:0 0 auto;font-size:11.5px;font-weight:700;border-radius:999px;
  padding:2px 8px;white-space:nowrap}
.rpill.abs{background:var(--crit-soft);color:var(--crit-ink);
  border:1px solid var(--crit-line)}
.rpill.late{background:var(--warn-soft);color:var(--warn-ink);
  border:1px solid var(--warn-line)}

/* His week, in his own symbols. No count, ever — see rebbihome.js. */
.rmks{display:flex;flex-wrap:wrap;gap:3px;justify-content:flex-end;
  flex:0 0 auto;max-width:46%}
.rmk{width:22px;height:22px;border-radius:6px;display:grid;place-items:center;
  font:700 12.5px 'Assistant',sans-serif;font-style:normal;line-height:1;
  background:var(--line-2);color:var(--ink-2);border:1px solid transparent}
.rmk.good{background:var(--good-deep);color:#fff;border-color:var(--good-deep)}
.rmk.concern{background:var(--crit-soft);color:var(--crit-ink);
  border-color:var(--crit-line)}
.rmk.serious{background:var(--crit-ink);color:#fff;border-color:var(--crit-ink)}
.rmk.watch{background:var(--warn-soft);color:var(--warn-ink);
  border-color:var(--warn-line)}
.rmk.principal{background:var(--brand-soft);color:var(--brand-2);
  border-color:var(--brand)}
/* ⚠ 'pass' IS THE ONLY GREY ONE, AND THAT IS THE WHOLE DESIGN — 2 September
   2026, with the office pass. The other five colours are judgements about a
   boy; being allowed to walk to the secretary is not one, so it wears the
   ink of a form rather than the red of a concern. See
   sql/a_boy_is_allowed_to_go_to_the_office.sql §3. */
.rmk.pass{background:var(--bg);color:var(--ink-2);border-color:var(--ink-3)}

.rempty{padding:16px 13px;color:var(--ink-2);font-size:14px}
.rsoft{margin:0 0 14px;padding:9px 12px;border-radius:9px;
  background:var(--crit-soft);border:1px solid var(--crit-line);
  color:var(--crit-ink);font-size:13px}

/* ---- the front door's switch, and the three lists it opens ----------------
   Menachem, 17 August: "I like the concept of perhaps having a drop-down menu
   which can select which list the teacher's looking at on the dashboard."
   One switch for the whole screen — he thinks "what am I doing right now", not
   "what am I doing to Nursery" — and the rows change SHAPE with it, not only
   behaviour. See rebbihome.js.
   ------------------------------------------------------------------------ */
.rpick{display:flex;align-items:center;gap:9px;margin:0 0 13px}
.rpick label{font-size:12px;font-weight:700;letter-spacing:.08em;
  text-transform:uppercase;color:var(--ink-3)}
.rpick select{flex:1;min-height:44px;border:1px solid var(--line);border-radius:9px;
  background:var(--panel);color:var(--ink);font:600 15px 'Assistant',sans-serif;
  padding:0 12px}
.rpick select:hover{border-color:var(--brand)}

.rchev{flex:0 0 auto;color:var(--ink-3);font-size:19px;line-height:1}
.rwho2{flex:0 0 auto;font-size:12.5px;color:var(--ink-3);max-width:38%;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rphone{flex:0 0 auto;font:700 13px 'TTOPNum','Assistant',sans-serif;
  font-feature-settings:'tnum' 1;color:var(--brand);text-decoration:none;
  padding:6px 8px;border-radius:8px;min-height:44px;display:flex;align-items:center}
.rphone:hover{background:var(--brand-soft);text-decoration:underline}

/* A row that marks is a button, and says so by being one. */
button.rrow{background:none;border:0;text-align:left;font:inherit;color:inherit;
  cursor:pointer}
button.rrow[aria-expanded="true"]{background:var(--brand-soft)}

.rpanel{padding:10px 13px 13px;background:var(--brand-soft);
  border-top:1px solid var(--line-2)}
.rbs{display:flex;flex-wrap:wrap;gap:7px}
.rb{display:inline-flex;align-items:center;gap:7px;min-height:44px;
  padding:7px 12px;border-radius:10px;border:1px solid var(--line);
  background:var(--panel);color:var(--ink);font:600 14px 'Assistant',sans-serif;
  cursor:pointer}
.rb:hover{border-color:var(--brand)}
.rb[disabled]{opacity:.5}
.rb .s{font-weight:700;font-size:15px}
.rb .n{font:700 12px 'TTOPNum','Assistant',sans-serif;background:var(--ink);
  color:var(--panel);border-radius:999px;padding:1px 7px}
/* The same tones the chips wear, so a button and the mark it makes match. */
.rb.good{border-color:var(--good-deep);color:var(--good-ink)}
.rb.good:hover{background:var(--good-deep);color:#fff}
.rb.concern{border-color:var(--crit-line);color:var(--crit-ink)}
.rb.concern:hover{background:var(--crit-soft)}
.rb.serious{border-color:var(--crit-ink);color:var(--crit-ink)}
.rb.serious:hover{background:var(--crit-ink);color:#fff}
.rb.watch{border-color:var(--warn-line);color:var(--warn-ink)}
.rb.watch:hover{background:var(--warn-soft)}
.rb.principal{border-color:var(--brand);color:var(--brand-2)}
.rb.principal:hover{background:var(--panel)}
.rb.pass{border-color:var(--ink-3);color:var(--ink-2)}
.rb.pass:hover{background:var(--bg);border-color:var(--ink-2)}

.rtakes{display:flex;flex-wrap:wrap;align-items:center;gap:7px;margin-top:10px}
.rtl{font-size:12.5px;color:var(--ink-2)}
.rtake{min-height:44px;padding:6px 12px;border-radius:9px;
  border:1px dashed var(--ink-3);background:transparent;color:var(--ink-2);
  font:700 13px 'Assistant',sans-serif;cursor:pointer}
.rtake:hover{border-style:solid;border-color:var(--crit-ink);color:var(--crit-ink)}
.rnone{margin:0;font-size:13.5px;color:var(--ink-2)}

/* What just saved. Quiet, and gone on its own — the marks are the receipt. */
/* He is back in class — the top card on a rebbi's front door, 28 August 2026.
   Brand-soft rather than green: the good/amber/red family is reserved for
   attendance meaning (rule 2), and this is news, not a state. */
.rback{background:var(--brand-soft);border:1px solid var(--brand);
  border-radius:12px;padding:13px 15px;margin:0 0 14px}
.rback h3{margin:0 0 7px;font:700 14px/1.2 'Assistant',sans-serif;
  color:var(--brand-2);letter-spacing:.01em}
.rback ul{list-style:none;margin:0;padding:0;display:flex;
  flex-direction:column;gap:7px}
.rback li{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 9px}
.rback b{font-size:15px;color:var(--ink)}
.rbwhen{font-size:12px;color:var(--ink-2)}
.rbnote{flex:1 1 100%;font-size:13px;color:var(--ink);line-height:1.45}

/* Why is he being sent up — the front door's port of the conduct list's
   question, as a small dialog. 28 August 2026. */
/* WHICH CLASS AM I MARKING — the one picker, shared. Lived in conduct.css
   until 30 August 2026, when the davening remote needed the same control and
   did not load that file. A rebbi never sees it (one class); hanhala uses it
   to move around the yeshiva. */
.cdclass{font:600 14px/1 'Assistant',system-ui,sans-serif;color:var(--ink);
  background:var(--panel);border:1px solid var(--line);border-radius:8px;
  padding:9px 30px 9px 11px;min-height:40px;max-width:100%;cursor:pointer;
  appearance:none;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6'><path d='M1 1l4 4 4-4' fill='none' stroke='%2352514e' stroke-width='1.6' stroke-linecap='round'/></svg>");
  background-repeat:no-repeat;background-position:right 11px center}
.cdclass:hover{border-color:var(--brand)}
.cdclass:focus-visible{outline:2px solid var(--brand);outline-offset:2px}

.rwhyveil{position:fixed;inset:0;z-index:40;background:rgba(23,26,33,.45);
  display:grid;place-items:center;padding:18px}
.rwhy{width:100%;max-width:420px;background:var(--panel);border-radius:14px;
  padding:16px 16px 14px;box-shadow:0 12px 40px rgba(23,26,33,.25)}
.rwhy h3{margin:0 0 4px;font-size:16px}
.rwhy p{margin:0 0 12px;font-size:13px;color:var(--ink-2)}
.rwhybs{display:flex;flex-wrap:wrap;gap:7px;margin:0 0 12px}
.rwhyb{border:1px solid var(--line);border-radius:10px;background:var(--panel);
  color:var(--ink);font:600 13.5px 'Assistant',sans-serif;
  padding:8px 12px;min-height:44px;cursor:pointer}
.rwhyb:hover{border-color:var(--brand);background:var(--brand-soft)}
.rwhy textarea{width:100%;padding:9px 11px;border:1px solid var(--line);
  border-radius:9px;font:400 14px 'Assistant',sans-serif;color:var(--ink);
  background:var(--panel);resize:vertical;margin:0 0 10px}
.rwhy textarea:focus{outline:2px solid var(--brand);outline-offset:1px;
  border-color:var(--brand)}
.rwhyrow{display:flex;gap:9px;flex-wrap:wrap}

/* ---- taking recess, on the rebbi's own front door — 2 September 2026 -------
   His: *"the missing recess option doesn't show up on the conduct list that
   lives in the home page."* Styled HERE and not borrowed from conduct.css,
   which index.html does not load — the bus-arrivals scar of 31 August, where a
   class taken from records.css drew default grey buttons on this very screen.
   ------------------------------------------------------------------------- */
.rrec{display:flex;align-items:stretch;margin-top:10px;
  padding-top:10px;border-top:1px solid var(--line-2)}
.rrecbtn{display:flex;align-items:center;gap:9px;flex:1;min-width:0;min-height:44px;
  padding:8px 12px;border:1px solid var(--line);border-radius:10px;
  background:var(--panel);color:var(--ink);
  font:600 14px 'Assistant',sans-serif;cursor:pointer}
.rrecbtn:hover{border-color:var(--brand)}
.rrecbtn:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.rrecbtn b{font-weight:700}
/* What he already owes, pushed to the far end — the figure a rebbi needs before
   he takes five more minutes off a boy already carrying twelve. */
.rrecbtn .n{margin-left:auto;font:700 12.5px 'TTOPNum','Assistant',sans-serif;
  color:var(--warn-ink);background:var(--warn-soft);border:1px solid var(--warn-line);
  border-radius:999px;padding:2px 8px;white-space:nowrap}
/* Joined to the button, the way the marks and their minus are. */
.rrec:has(.rrecminus) .rrecbtn{border-top-right-radius:0;border-bottom-right-radius:0;
  border-right-width:0}
.rrecminus{flex:0 0 auto;width:44px;min-height:44px;
  border:1px solid var(--line);border-top-left-radius:0;border-bottom-left-radius:0;
  border-top-right-radius:10px;border-bottom-right-radius:10px;
  background:var(--panel);color:var(--ink-2);
  font:700 17px 'Assistant',sans-serif;cursor:pointer}
.rrecminus:hover{border-color:var(--crit-line);background:var(--crit-soft);
  color:var(--crit-ink)}
.rrecminus:focus-visible{outline:2px solid var(--brand);outline-offset:2px}

.rrecpick{display:flex;gap:8px;margin:0 0 10px;flex-wrap:wrap}
.rrecpick .tbtn{flex:1;min-width:88px}
.rrecfld{display:flex;align-items:center;gap:9px;margin:0 0 10px;
  font-size:13px;color:var(--ink-2)}
.rrecfld input{flex:1;min-width:0;font:400 14px 'Assistant',sans-serif;
  color:var(--ink);background:var(--panel);min-height:44px;
  padding:8px 11px;border:1px solid var(--line);border-radius:9px}
.rrecfld input:focus{outline:2px solid var(--brand);outline-offset:1px;
  border-color:var(--brand)}

/* ⚠⚠ THIS WAS CALLED `.rbar` AND IT COLLIDED WITH THE RECORD SCREEN'S GREEN
   HEADER — 2 September 2026. `records.css` has had `.rbar` since August for the
   sticky green bar across the top of `talmidim.html`; this toast, added
   yesterday with the rest of the `.rrec*` family, took the same name in a
   stylesheet every screen loads.

   Both are specificity (0,1,0), so the cascade merged them property by
   property: the record bar kept its own `background` and `top:0` because it
   declares those, and silently inherited this one's `margin:12px 0 0`,
   `border-radius:10px`, `bottom:10px` and box-shadow, because it declares
   none of them. That is the gap Menachem photographed twice — a strip of page
   background above a rounded green bar: *"there shouldn't be any space between
   the top of the phone or the top of the screen and the green bar."*

   ⚠ IT COST TWO ROUNDS BECAUSE IT DOES NOT LOOK LIKE A CSS BUG. Both earlier
   reports were read as the shell injecting something above the header, and
   both times something really was being injected — so the injected thing was
   fixed and the gap stayed. A name collision leaks only the properties the
   other rule forgot to set, which is why the bar stayed the right colour and
   the right height and was simply in the wrong place.

   Renamed to sit with its own family (`.rrecpick`, `.rrecfld`, `.rrecminus`)
   rather than the record's. */
.rrecbar{position:sticky;bottom:10px;margin:12px 0 0;padding:10px 14px;
  border-radius:10px;background:var(--ink);color:var(--panel);
  font:600 14px 'Assistant',sans-serif;box-shadow:0 4px 14px rgba(23,26,33,.24)}
.rrecbar.bad{background:var(--crit-ink)}
/* The details row is a plain row holding two links (see rebbihome.js), so the
   name takes the space the row itself takes in the other two modes. */
.rrow .rlink{flex:1;min-width:0;text-decoration:none;color:inherit;
  display:block;min-height:44px;display:flex;align-items:center}
a.rchev{text-decoration:none;padding:0 4px;min-height:44px;display:flex;
  align-items:center}
/* The switch lives beside the class name, where the Full sheet link used to be
   (Menachem, 17 August). `.rpick` is gone with it. */
.rhead .rmode{margin-left:auto;min-height:44px;max-width:62%;
  border:1px solid var(--line);border-radius:9px;background:var(--panel);
  color:var(--ink);font:600 14px 'Assistant',sans-serif;padding:0 10px}
.rhead .rmode:hover{border-color:var(--brand)}

/* ---- marking attendance on the front door --------------------------------
   Menachem, 17 August: the list itself becomes the thing he marks on. The row
   states borrow attendance.css's meaning exactly — absent is the red one, late
   is the office's and is locked — so a rebbi reads the same colours on both
   screens. Named apart (`.rrow.isabs`, not `.row.abs`) so neither screen can
   restyle the other.
   ------------------------------------------------------------------------ */
.rrow .rmark{flex:0 0 auto;font-size:12.5px;font-weight:700;color:var(--ink-3);
  text-align:right;line-height:1.25}
.rrow .rmark small{display:block;font-size:10.5px;font-weight:600;opacity:.85}

.rrow.isabs{background:var(--crit-soft)}
.rrow.isabs .en{color:var(--crit-ink)}
.rrow.isabs .rmark{color:var(--crit-ink)}
.rrow.islate{background:var(--warn-soft)}
.rrow.islate .rmark{color:var(--warn-ink)}
/* Locked is the office's mark. It reads as settled, not as broken. */
.rrow.locked{cursor:default}
.rrow.locked:hover{background:var(--warn-soft)}
button.rrow[disabled]{opacity:1}

/* What is about to be sent, then the button. */
.rsend{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin:11px 0 0}
.rcount{margin:0;font-size:13.5px;color:var(--ink-2);
  font-family:'TTOPNum','Assistant',sans-serif;font-feature-settings:'tnum' 1}
.rcount b{color:var(--good-ink)}
.rsendbtn{margin-left:auto;min-height:44px;padding:0 20px;border:0;
  border-radius:10px;background:var(--brand);color:#fff;cursor:pointer;
  font:700 15px 'Assistant',sans-serif;
  box-shadow:0 2px 10px rgba(20,82,79,.18)}
.rsendbtn:hover{background:var(--brand-2)}
.rsendbtn[disabled]{opacity:.6;cursor:default}
.rpaper{flex-basis:100%;font-size:13px;color:var(--brand);text-decoration:none}
.rpaper:hover{text-decoration:underline}

/* ---- his day, at the top of his front door -------------------------------
   Menachem, 17 August: the daily schedule sits right under the greeting, and
   it is HIS schedule — the whole-school hours came off the screen entirely.
   ------------------------------------------------------------------------ */
.rday{background:var(--panel);border:1px solid var(--line);border-radius:13px;
  padding:14px 15px;margin:0 0 14px;box-shadow:0 1px 2px rgba(23,26,33,.04)}
.rdhead{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
.rdhead h2{margin:0;font-size:17px;font-weight:700;letter-spacing:-.2px}
.rheb{margin-left:auto;font-family:'FrankRuhl','Assistant',serif;
  font-size:15px;color:var(--brand)}

.rtags{display:flex;flex-wrap:wrap;gap:6px;margin:9px 0 0}
.rtag{font-size:11.5px;font-weight:700;border-radius:999px;padding:2px 9px;
  background:var(--brand-soft);color:var(--brand-2)}
.rtag.off{background:var(--crit-soft);color:var(--crit-ink);
  border:1px solid var(--crit-line)}
.rtag.ws{background:var(--panel);color:var(--ink-2);border:1px solid var(--line)}

.rev{margin:9px 0 0;font-size:14px;font-weight:600;color:var(--ink)}
.rnote2{margin:5px 0 0;font-size:13px;color:var(--ink-2)}
.rgap{margin:10px 0 0;font-size:13.5px;color:var(--ink-3);line-height:1.5}

/* His periods. A break and a bookend are quieter than a lesson — the eye
   should land on what he teaches. */
.rhours{list-style:none;margin:11px 0 0;padding:0}
.rhr{display:flex;gap:11px;padding:8px 0;border-bottom:1px solid var(--line-2)}
.rhr:last-child{border-bottom:0}
.rhr .rt{flex:0 0 96px;font-family:'TTOPNum','Assistant',sans-serif;
  font-feature-settings:'tnum' 1;font-size:12.5px;color:var(--ink-2);
  padding-top:2px;white-space:nowrap}
.rhr .rw{min-width:0}
.rhr .rw b{display:block;font-size:14.5px;font-weight:700}
.rhr .rw small{display:block;font-size:12.5px;color:var(--ink-3)}
.rhr .rw .rcls{color:var(--brand);font-weight:600}
/* Outside today's changed hours (dayof.js, 25 September 2026). */
.rhr.nottoday{opacity:.45}
.rhr .rw .rnot{display:inline-block;margin-top:3px;font-size:10.5px;font-weight:800;letter-spacing:.05em;
  text-transform:uppercase;color:var(--ink-2);background:var(--line-2);border-radius:999px;padding:2px 8px}
.rdaynote{margin:0 0 8px;padding:7px 10px;border-left:4px solid var(--warn);
  background:var(--warn-soft);border-radius:6px;font-size:13.5px;color:var(--ink-2)}
.rhr.break .rw b,.rhr.transition .rw b,.rhr.bookend .rw b{
  font-weight:600;color:var(--ink-2)}
.rhr.break .rt,.rhr.transition .rt,.rhr.bookend .rt{color:var(--ink-3)}
/* An hour a rebbi teaches in somebody else's class. Menachem, 17 August: the
   schedule tracks the TEACHER, so the hour that leaves his own room has to say
   so — otherwise a day that is six-sevenths his own class reads as his class's
   timetable, which is the thing this list is not. */
.rhr .rcls.away{color:var(--data-ink);background:var(--data-soft);
  border:1px solid var(--data-line);border-radius:5px;padding:1px 6px;
  display:inline-block;margin-top:2px;font-weight:700}
/* A lesson he is COVERING — 6 October 2026, cover by the period. Whose it is,
   under the room, in the green the cover screens use for "covered". */
.rhr .rw .rcov{display:block;margin-top:2px;font-size:11.5px;font-weight:700;
  color:var(--good-ink)}

/* ---- where he is standing right now ---------------------------------------
   The card above his class blocks, on the front door, while a lesson of his is
   actually running. Teal when the room is his own, sand when it is somebody
   else's — the sand is the app's existing "this is not the ordinary case"
   colour and it is doing the same job here as on the preview bar. */
.rnow{background:var(--brand);color:#fff;border-radius:12px;
  padding:15px 17px;margin:0 0 13px}
.rnow.away{background:var(--sand-soft);color:var(--sand-ink);
  border:1px solid var(--sand-line)}
.rnow .nwlab{margin:0 0 5px;font-size:11.5px;font-weight:700;letter-spacing:.09em;
  text-transform:uppercase;opacity:.72}
.rnow .nwwhat{margin:0;font-size:17px;line-height:1.35}
.rnow .nwwhat b{font-weight:700}
.rnow .nwmine,.rnow .nwaway{display:inline-block;font-size:15px;opacity:.9}
.rnow .nwaway::before{content:'in '}
.rnow .nwfine{margin:9px 0 0;font-size:13px;line-height:1.55}
.rnow .nwgo{display:inline-flex;align-items:center;min-height:40px;margin-top:12px;
  padding:0 16px;border-radius:9px;text-decoration:none;
  font:700 14.5px/1 'Assistant',system-ui,sans-serif;
  background:rgba(255,255,255,.15);color:#fff;border:1px solid rgba(255,255,255,.42)}
.rnow .nwgo:hover{background:rgba(255,255,255,.24);border-color:#fff}
.rnow.away .nwgo{background:var(--panel);color:var(--brand);border-color:var(--sand-line)}
.rnow.away .nwgo:hover{border-color:var(--brand)}
/* The room he is standing in, at the top of his classes while the lesson runs
   (rebbihome.js, awayBlock) — 12 October 2026. The Now card's sand, because it
   is the same "not your class" fact, framed so it cannot be taken for his own. */
.rclass.raway{background:var(--sand-soft);border:2px solid var(--sand-ink);
  border-radius:12px;padding:12px 12px 4px}
.raway .rhead{align-items:center}
.raway .rawtag{margin-left:auto;font-size:11.5px;font-weight:700;letter-spacing:.08em;
  text-transform:uppercase;color:var(--sand-ink);white-space:nowrap}
.raway .rawnote{margin:0 0 10px;font-size:13.5px;line-height:1.5;color:var(--ink-2)}

/* --------------------------------------------------------------------------
   WHOSE BIRTHDAY IT IS TODAY — 26 August 2026.

   One card, both front doors. It is the only warm thing on a screen otherwise
   made of counts and things that are owed, and it is deliberately the only
   place in this app that uses the sand palette as a FILL rather than as a chip:
   a birthday is not a job, an alert or a deadline, and it should not look like
   one sitting between "3 classes still need attendance" and the day's hours.

   No `--crit`, no `--warn`, no `--good` — those three own meaning across every
   other screen and borrowing one here would make a birthday read as a state to
   be resolved.
   -------------------------------------------------------------------------- */
/* ⚠ 31 August 2026 — *"the happy birthday card looks really boring, make it
   look a little more birthdayish."* Confetti, a cake, a warm wash and a gold
   ring round the face. What did NOT change is the paragraph above: the three
   festive colours below are DECORATION AND CARRY NO MEANING, which is exactly
   why they had to be added rather than borrowed. --warn is a perfectly good
   party gold and it is not available: it means "somebody is unaccounted for"
   on every other screen in this app, and a birthday must never be able to read
   as a thing to be resolved.

   They are declared on the card and not on `:root` so they cannot be reached
   by accident from anywhere else. */
#t-birthdays{margin:0 0 18px}
.bdaycard{--bday-gold:#d9a63a; --bday-rose:#c9788f; --bday-plum:#8b74ad;
  --bday-gold-ink:#a8741a;
  position:relative;overflow:hidden;
  background:linear-gradient(152deg,#fdfaf1 0%,var(--sand-soft) 52%,#f4ecd9 100%);
  border:1px solid var(--sand-line);border-radius:16px;padding:14px 16px 15px;
  box-shadow:0 1px 2px rgba(23,26,33,.04)}

/* Behind everything, and the content is lifted over it — a positioned element
   paints above the static content that follows it, so without the z-index the
   dots would sit ON the boys' names. Low opacity because this is a school's
   front door and the card still has to be read at a glance. */
.bdayconf{position:absolute;inset:0;width:100%;height:100%;z-index:0;
  opacity:.55;pointer-events:none}
.bdayconf .cdot circle{fill:var(--bday-gold)}
.bdayconf .cdot circle:nth-child(3n+2){fill:var(--bday-rose)}
.bdayconf .cdot circle:nth-child(3n){fill:var(--brand)}
.bdayconf .cdash path{stroke:var(--bday-plum)}
.bdayconf .cdash path:nth-child(2n){stroke:var(--bday-gold)}

/* --------------------------------------------------------------------------
   AND IT FALLS IN WHEN THE CARD ARRIVES — 31 August 2026, his *"can the bday
   confetti be animated?"*

   ⚠ ONCE, NOT FOREVER, and that is the whole judgement. This card sits on a
   front door the office keeps open all day, next to "2 boys need your
   attention". Confetti that never stops moving is a thing in the corner of the
   eye of somebody trying to read a worklist — the same instinct that made the
   davening board still rather than dark. So it drops in over about a second and
   a half and then it is a picture.

   The pieces fall in the order they were drawn, left to right, from `--i` on
   each one: a sweep across the card rather than fifty things landing at once.
   `backwards` keeps a piece invisible until its own delay, or they would all
   sit in place for a moment first and then jump up to fall.

   `transform-box:fill-box` is what makes `rotate` turn a piece about its own
   middle; without it every piece rotates about the SVG's origin and they fly
   across the card. -------------------------------------------------------- */
/* ⚠ IT FALLS FOR THIRTY SECONDS NOW, not once — his, 31 August 2026: *"id like
   for the confetti animation to keep going for 30 seconds once going on the
   page."* The one-drop version is gone; what replaced it is a loop that stops
   itself.

   ⚠ BOTH ENDS OF THE LOOP ARE INVISIBLE, and that is the whole trick. A piece
   fades in above its resting place, drifts down through it, and fades out
   below — so the jump back to the top between cycles happens at opacity 0 and
   cannot be seen. Without that the card would pulse: fifty pieces visibly
   snapping upward every four seconds.

   The delays are NEGATIVE, which starts every piece part-way through its own
   cycle. At `-0.13s` a time the pieces are spread right across the four
   seconds, so the card is already full of confetti in the first frame rather
   than opening on an empty band and filling up. */
@keyframes bdayrain{
  0%  {opacity:0;transform:translateY(-34px) rotate(-40deg)}
  15% {opacity:1}
  85% {opacity:1}
  100%{opacity:0;transform:translateY(34px) rotate(35deg)}
}

/* And how it ENDS. After thirty seconds `birthdays.js` adds `.settled` and
   every piece lands on the spot it was drawn at — the same jittered scatter
   the card had before today, which is a picture rather than a stopped
   animation. Staggered by the same `--i`, so they settle in a sweep across the
   card exactly as they used to arrive, instead of fifty things stopping at
   once. */
@keyframes bdaysettle{
  from{opacity:0;transform:translateY(-12px) rotate(-18deg)}
  to  {opacity:1;transform:translateY(0) rotate(0)}
}

.bdayconf .cdot circle,.bdayconf .cdash path{
  transform-box:fill-box;transform-origin:50% 50%;
  animation:bdayrain 4s linear infinite;
  animation-delay:calc(var(--i,0) * -0.13s)}
.bdayconf.settled .cdot circle,.bdayconf.settled .cdash path{
  animation:bdaysettle .7s cubic-bezier(.2,.85,.3,1) both;
  animation-delay:calc(var(--i,0) * 8ms)}

/* ⚠ AND IT STOPS DEAD FOR ANYBODY WHO HAS ASKED IT TO. `prefers-reduced-motion`
   is a real setting real people turn on — for migraine, for vestibular
   disorders, for concentration — and a decoration is the first thing that
   should honour it, not the last. The card is complete without the movement:
   every piece is exactly where it was going to land. */
@media (prefers-reduced-motion:reduce){
  .bdayconf .cdot circle,.bdayconf .cdash path,
  .bdayconf.settled .cdot circle,.bdayconf.settled .cdash path{animation:none}
}
.bdayhead,.bdaylist{position:relative;z-index:1}

/* The greeting leads and the count follows it to the right margin. It was the
   other way round until today: a 12px uppercase "A BIRTHDAY TODAY" is a section
   label, and this card is the one thing on the screen that is addressed to
   somebody rather than about him. */
.bdayhead{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin:0 0 12px;
  font-weight:400}
/* ⚠ THE DEEPER GOLD, NOT THE CONFETTI ONE, AND IT WAS MEASURED. #d9a63a on this
   card's darkest ground (#f4ecd9) is 1.9:1 — fine for a dot in the corner,
   invisible for a 19-pixel line drawing. #a8741a is 3.45:1, which clears the
   3:1 that non-text graphics ask for. The cake is decorative and the words say
   the same thing, so it did not have to clear it; it looked feeble, which is a
   better reason. */
.bdaycake{flex:0 0 auto;width:19px;height:19px;color:var(--bday-gold-ink)}
.bdaywish{font-size:16.5px;font-weight:700;letter-spacing:-.2px;color:var(--sand-ink)}
.bdaywhen{font-size:10.5px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;
  color:var(--ink-3);margin-inline-start:auto}

/* Wraps rather than scrolls: on the rare morning with four birthdays this is a
   second row, not a thing to swipe. A horizontal scroller on a front door hides
   the last boy from anybody who does not think to drag it. */
.bdaylist{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:14px 22px}
.bdayboy{display:flex;align-items:center;gap:12px;min-width:0}
/* A gold ring with the card's own cream between it and the face, so it reads as
   a frame put round him today rather than as a border the avatar always has. */
.bdayboy .avatar{flex:0 0 auto;
  box-shadow:0 0 0 2px #fdfaf1,0 0 0 3.5px var(--bday-gold)}
/* The initials circle is the ORDINARY case here, not a failure: 61 of 186 boys
   have no photograph and 47 of those are the four youngest classes, who were not
   at the yeshiva when the camera came round. So it is given the card's own
   colours and looks intended. */
.bdayboy .avatar.noface{background:var(--panel);border-color:var(--sand-line);
  color:var(--sand-ink);font-weight:700}
.bnm{display:flex;flex-direction:column;min-width:0}
.bnm .bhe{font-size:15px;font-weight:600;color:var(--ink);line-height:1.25;
  font-family:FrankRuhl,'Assistant',serif;unicode-bidi:isolate}
.bnm .ben{font-size:11.5px;color:var(--ink-3);line-height:1.3;unicode-bidi:isolate}
.bnm .bkita{font-size:9.5px;font-weight:700;letter-spacing:.4px;text-transform:uppercase;
  color:var(--sand-ink);margin-top:2px}

@media (max-width:430px){
  .bdaylist{gap:12px 16px}
  .bdaycard{padding:12px 13px}
}

/* ---------------------------------------------------------------------------
   THE IEP SUMMARY — 1 September 2026
   ---------------------------------------------------------------------------
   Menachem: *"most people won't be opening up the IEP; they're going to be just
   reading the summary."* So it is typeset to be READ — a real paragraph at full
   line height, and headings that let a rebbi find *what helps him* without
   reading the rest. It is not a caption under a table of numbers.

   ⚠ IT IS HERE RATHER THAN IN `ieps.css` because two screens draw it: the IEP
   screen at the moment of upload, and the boy's own record, which does not load
   that file. A second copy would drift, and the drift would be a rebbi and the
   office reading one document in two different shapes.
   --------------------------------------------------------------------------- */
.iepsum{margin:0 0 14px;padding:0 0 12px;border-bottom:1px solid var(--line)}
.iepsum .lede{margin:0 0 10px;font-size:1rem;line-height:1.6;color:var(--ink)}
.iepsum h4{margin:10px 0 4px;font-size:.82rem;letter-spacing:.06em;
  text-transform:uppercase;color:var(--ink-3);font-weight:700}
.iepsum ul{margin:0;padding-left:20px}
.iepsum li{margin:0 0 3px;line-height:1.5;color:var(--ink-2)}
/* Where it came from, small and last. A summary with no provenance is a claim;
   this one says it was read off the document and where the document is. */
.iepsum .prov{margin:10px 0 0;font-size:.8rem;color:var(--ink-3)}

/* ⚠ THE CLINICAL ONE IS MARKED AS THE NARROWER ONE. Two summaries of one boy,
   side by side, in the same typeface, and only one of them may be repeated in
   front of his rebbi — so the difference has to be visible without reading
   both. A left rule and a line of words, not a colour: the palette's meanings
   are spoken for (docs/COLORS.md), and "who may see this" is not a warning. */
.iepsum.clinical{border-left:3px solid var(--line);padding-left:12px;
  border-bottom:1px solid var(--line)}
.iepsum.clinical .who{margin:0 0 6px;color:var(--ink-3);font-weight:700}
.iepsum.clinical .lede{font-size:.95rem}
.iepsum .dates{margin:10px 0 0;font-size:.85rem;color:var(--ink-2);
  font-variant-numeric:tabular-nums}

/* ---------------------------------------------------------------------------
   WHICH SERVICE — 1 September 2026
   ---------------------------------------------------------------------------
   Menachem: *"build out the other services in the scheduler."* Until tonight the
   caseload board and the scheduler each opened with `const TYPE = 'p3_setts'`
   and could not be pointed anywhere else.

   ⚠ HERE RATHER THAN IN `services.css`, because the SCHEDULER draws it too and
   does not load that file — the same reason `.iepsum` lives here.

   ⚠ AND `svcpick`/`svcp`, NOT `svtabs`/`svtab`: the board already owns those
   for its PROVIDER tabs, one letter away. Two controls a letter apart in one
   stylesheet is a collision waiting for whoever edits either of them next.

   Deliberately a size smaller and quieter than the provider tabs it sits above.
   It changes what the whole screen is ABOUT, so it has to be findable — but a
   second row of big pills would compete with the control somebody is actually
   working in. */
.svcpick{display:flex;gap:6px;flex-wrap:wrap;margin:14px 0 0}
.svcp{box-sizing:border-box;text-decoration:none;
  border:1px solid var(--line);border-radius:999px;
  min-height:34px;padding:6px 12px;font-size:13px;font-weight:600;
  background:var(--bg);color:var(--ink-2);
  display:inline-flex;align-items:center}
.svcp:hover{background:var(--brand-soft);border-color:var(--brand)}
.svcp.on{background:var(--brand);border-color:var(--brand);color:#fff}
/* How many of that service nobody has been assigned to — his 6 September fix
   for a total that could not be checked. Appended, and nothing else names
   `.svcn`. It carries the warn tint on an unselected chip because the number is
   work outstanding; on the selected chip it goes plain white-on-brand, since a
   warn pill inside a solid brand pill is two loud things in one control. */
.svcn{margin-left:7px;padding:1px 7px;border-radius:999px;font-size:11.5px;
  line-height:1.5;background:var(--warn-soft);color:var(--warn-ink);
  border:1px solid var(--warn-line)}
.svcp.on .svcn{background:rgba(255,255,255,.22);color:#fff;border-color:transparent}

/* ---- the shell's own back link ------------------------------------------
   Injected by `shell.js` on every screen that did not come with one, so that
   thirty-three pages stop being dead ends. It matches the ten hand-written
   `.back` links (13px, 600, brand) because those are what people already know;
   the difference is only that this one is placed by the shell.

   ⚠⚠ IT MOVED TO THE FOOT ON 2 SEPTEMBER 2026, and the reason is a fault this
   file had TWICE. Both shell controls used to be inserted into `#page` — after
   `.topbar` where a page had one, `page.prepend` otherwise. Three screens keep
   their burger somewhere else (`.rbar` on Classes, `.abar` on Attendance and
   Middos), so on those the control was prepended above the page's own green
   header: a stray "← Class list" floating over the top of the screen, with the
   header pushed down under it. Menachem, on his phone: *"The formatting at the
   top is still disastrous. the top needs to be the green bar and you need a
   foot, the refresh button and the back button some were under there."*

   ⚠ 44px of tap target on a phone. It is the control he reaches for one-handed
   while standing, and 13px of text is a 17px-high target on its own.
   -------------------------------------------------------------------------- */

/* THE SHELL'S FOOT. It cannot push a screen down, which is the whole point:
   appended LAST, so on a flex column like `.rshell` `margin-top:auto` pins it
   to the bottom of the window, and on an ordinary page it follows the content.
   Built only when something goes in it — an empty bar with a rule across it is
   a thing people ask about. */
.shellfoot{display:flex;align-items:center;justify-content:space-between;
  gap:12px;flex-wrap:wrap;margin-top:auto;
  padding:10px 16px calc(10px + env(safe-area-inset-bottom));
  border-top:1px solid var(--line);background:var(--panel)}
@media print{ .shellfoot{display:none !important} }

.shellback{display:inline-flex;align-items:center;min-height:34px;
  font-size:13.5px;font-weight:600;color:var(--brand);text-decoration:none}
.shellback:hover{text-decoration:underline}
@media (max-width:820px){ .shellback{min-height:44px} }

/* ---- Refresh, drawn only in an installed window — 2 September 2026 ---------
   ⚠ IT MOVED INTO THE MENU THE SAME DAY IT SHIPPED, on a photograph of the
   installed app pushed down its own window: *"fix this formatting issue."*

   It used to be a right-aligned ROW inside `#page`, which is a page's own
   layout container — and on the three screens that keep their burger somewhere
   other than a `.topbar` (`.rbar` on Classes, `.abar` on Attendance and Middos)
   it was prepended as the first flex child of a `min-height:100dvh` column and
   shoved the entire screen down by its own height. A control that belongs to
   every screen cannot live inside any of them.

   It went to the sidebar first, which fixed the layout fault — chrome cannot
   disturb a page — and was still the wrong home: on a phone the sidebar is
   behind the burger, and the phone is where he was standing when he asked for
   *"a foot, the refresh button and the back button some were under there."*
   So it shares `.shellfoot` with the back link, on the right, because the back
   link owns the left — the same two sides they would have on any window chrome.
   ------------------------------------------------------------------------- */
.shellrefresh{display:inline-flex;align-items:center;gap:6px;min-height:34px;
  padding:6px 12px;border:1px solid var(--line);border-radius:9px;
  background:var(--bg);color:var(--ink-2);
  font:600 13px 'Assistant',sans-serif;cursor:pointer}
.shellrefresh:hover{border-color:var(--brand);color:var(--brand-2)}
.shellrefresh:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.shellrefresh[disabled]{opacity:.6;cursor:default}
/* A thumb needs the full target on a phone; a mouse does not. */
@media (max-width:820px){ .shellrefresh{min-height:44px;padding:9px 14px} }
@media print{ .shellrefresh{display:none !important} }

/* ---- THE STRIP — 4 September 2026 ------------------------------------------
   His name for it, chosen the day it shipped. The selector stays `.areatabs`
   because renaming a live one buys nothing; the WORD is "the strip".

   Under the green bar on every child of an area. See areatabs.js for what it
   replaces (the hub of buttons, his own rule of 7 August) and shell.js for why
   it is injected rather than written into twenty pages.

   ⚠ IT SLIDES INSIDE ITSELF AND THE PAGE DOES NOT. Attendance has eight tabs
   and they will never fit 390px. The house rule from style.css's own opening
   note is that the BOX slides, never the body — a page that slides takes the
   top bar and the menu with it. `overscroll-behavior-x: contain` so reaching
   the end of the strip does not start dragging the screen behind it.

   ⚠ NOT `overflow-x: hidden`. Same note, learned over seven attempts: `hidden`
   makes an element a scroll container and silently kills `position: sticky`
   above it. `auto` is what is wanted here anyway — the strip must scroll. */
.areatabs{
  display:flex; gap:2px; overflow-x:auto; overscroll-behavior-x:contain;
  background:var(--brand-2); padding:0 8px;
  scrollbar-width:none; -webkit-overflow-scrolling:touch;
}
.areatabs::-webkit-scrollbar{ display:none }
.areatabs .atab{
  flex:none; white-space:nowrap; text-decoration:none;
  color:#cfe0de; font:600 13px 'Assistant',system-ui,sans-serif;
  padding:11px 12px 9px; border-bottom:3px solid transparent;
}
.areatabs .atab:hover{ color:#fff }
/* `aria-current="page"` carries the same fact for a screen reader, set in
   areatabs.js — the underline is not the only thing saying where you are. */
.areatabs .atab.on{ color:#fff; border-bottom-color:#fff }
/* An unbuilt child still gets a tab, because the menu already offers it and a
   map with holes admitted is better than one with holes hidden. It is dimmed
   so a thumb knows before it lands. */
.areatabs .atab.soon{ color:#8fada9 }
/* ⚠ THE NUMBER ON A TAB — 5 September 2026, his ask. Drawn only when there is
   something waiting; see badgeTabs in areatabs.js for why zero gets no badge.
   Crit red on the dark green bar, which is the one place in the app where a
   status colour has to hold its own against a brand ground — so it is the
   SOLID red with white on it rather than the soft tint the panels use. */
.areatabs .atab .badge{ display:inline-block; margin-inline-start:6px;
  min-width:17px; padding:1px 5px; border-radius:9px;
  background:var(--crit); color:#fff; font-style:normal;
  font:700 11px/1.45 'Assistant',system-ui,sans-serif; text-align:center;
  vertical-align:1px }
.areatabs .atab:focus-visible{ outline:2px solid #fff; outline-offset:-3px }
/* A strip printed on a sheet of paper is a row of dead words. */
@media print{ .areatabs{ display:none !important } }

/* ---- AN AREA'S OVERVIEW — 4 September 2026 ---------------------------------
   The dashboard the word in the menu now opens onto, in place of a hub of
   buttons. See ov-attendance.js for what it draws and why there are two of it.

   ⚠ EVERY COLOUR HERE IS FROM docs/COLORS.md AND NONE IS DECORATION. This
   file's own opening line is "Tokens are the ones settled in docs/COLORS.md.
   Do not invent new ones here", and the first draft of the mock-up ignored it
   and drew charts in `--brand`, which is the top bar's colour. The data family
   has existed since 13 August: `--data` for a single series and for a meter
   fill, `--seq-*` for magnitude — one hue, more is darker.

   The HERO is tinted by what it says, not to be lively: the app already tints
   rows by state everywhere (`--present-soft` for a boy who is in, `--crit-*`
   for absent, `--warn-*` for late), so a headline saying something is wrong,
   in the same red as the rows beneath it, is the app being consistent. */
.ovwrap{ margin-top:4px }

.ovhero{ background:var(--panel); border:1px solid var(--line); border-radius:12px;
  padding:14px 15px; margin:0 0 10px }
.ovhero .n{ display:block; font:700 30px/1 'Assistant',system-ui,sans-serif;
  letter-spacing:-.8px; color:var(--data-ink) }
.ovhero .n small{ font-size:16px; font-weight:600; color:var(--ink-3); letter-spacing:0 }
.ovhero .l{ display:block; font-size:13.5px; color:var(--ink-2); margin-top:5px;
  line-height:1.5 }
.ovhero .l b{ color:inherit; font-weight:700 }
.ovhero.calm{ background:var(--good-soft); border-color:var(--good-line) }
.ovhero.calm .n, .ovhero.calm .l{ color:var(--good-ink) }
.ovhero.warn{ background:var(--sand-soft); border-color:var(--sand-line) }
.ovhero.warn .n, .ovhero.warn .l{ color:var(--sand-ink) }
.ovhero.bad{ background:var(--crit-soft); border-color:var(--crit-line) }
.ovhero.bad .n, .ovhero.bad .l{ color:var(--crit-ink) }
/* ⚠ `data` WAS BEING ASKED FOR AND DID NOT EXIST. ov-services.js has called
   hero('data', …) since it was written and got the plain panel — no tint, and
   nothing wrong-looking enough to catch in the markup. A count that is neither
   good news nor bad — "18 services this year" — is what it is for, and
   docs/COLORS.md names --data as the colour of a plain quantity. */
.ovhero.data{ background:var(--data-soft); border-color:var(--data-line) }
.ovhero.data .n, .ovhero.data .l{ color:var(--data-ink) }

.ovtiles{ display:grid; grid-template-columns:1fr 1fr; gap:9px; margin:0 0 10px }
.ovtiles.one{ grid-template-columns:1fr }
.ovtile{ background:var(--panel); border:1px solid var(--line); border-radius:11px;
  padding:11px 12px }
.ovtile .n{ display:block; font:700 23px/1.1 'Assistant',system-ui,sans-serif;
  letter-spacing:-.4px }
.ovtile .l{ display:block; font-size:12.5px; color:var(--ink-2); margin-top:3px;
  line-height:1.35 }
.ovtile.act{ background:var(--sand-soft); border-color:var(--sand-line) }
.ovtile.act .n, .ovtile.act .l{ color:var(--sand-ink) }
.ovtile.good{ background:var(--good-soft); border-color:var(--good-line) }
.ovtile.good .n, .ovtile.good .l{ color:var(--good-ink) }
.ovtile.bad{ background:var(--crit-soft); border-color:var(--crit-line) }
.ovtile.bad .n, .ovtile.bad .l{ color:var(--crit-ink) }

.ovcard{ background:var(--panel); border:1px solid var(--line); border-radius:12px;
  padding:12px 14px; margin:0 0 10px }
.ovcard > b.h{ display:block; font-size:13px; font-weight:700; margin-bottom:9px }

/* The 100% bar. A 2px gap between segments is the house mark spec, and it is
   what keeps a 3% sliver from bleeding into the one beside it. */
.ovbar{ display:flex; gap:2px; height:26px; border-radius:7px; overflow:hidden;
  margin-bottom:9px }
.ovbar i{ display:block; height:100% }
.ovkey{ display:flex; flex-wrap:wrap; gap:12px }
.ovkey span{ display:flex; align-items:center; gap:6px; font-size:12.5px;
  color:var(--ink-2) }
/* The swatch never carries the meaning alone — its word is beside it, which is
   the rule the four status colours ship under. */
.ovkey i{ width:11px; height:11px; border-radius:3px; flex:none }
.ovkey b{ color:var(--ink); font-weight:700 }

.ovrow{ display:flex; align-items:center; gap:9px; font-size:13.5px;
  color:var(--ink-2); padding:8px 0; border-top:1px solid var(--line-2) }
.ovrow:first-of-type{ border-top:0 }
.ovrow .nm{ flex:1; min-width:0 }
.ovrow .st{ flex:none; font-weight:700; font-size:12.5px }
.ovdot{ width:9px; height:9px; border-radius:50%; flex:none }
.ovdot.s-good{ background:var(--good-deep) }
.ovdot.s-warn{ background:var(--warn) }
.ovdot.s-crit{ background:var(--crit) }
.ovrow .t-good{ color:var(--good-ink) }
.ovrow .t-warn{ color:var(--warn-ink) }
.ovrow .t-crit{ color:var(--crit-ink) }
/* A class that has not sent in has no count to show, and a coloured dot beside
   the words "not sent in" would be asserting something about boys nobody has
   marked yet. Hollow says "no answer here", which is the truth. */
.ovdot.s-none{ background:transparent; border:1.5px solid var(--ink-3) }
/* A row that is the way in — see goRow() in ovkit.js. */
a.ovrow.tap{ text-decoration:none; color:var(--ink-2) }
a.ovrow.tap:hover{ color:var(--ink) }
a.ovrow.tap:hover .ch{ color:var(--brand) }
.ovrow .ch{ flex:none; font-weight:700; font-size:16px; color:var(--ink-3);
  font-style:normal; line-height:1 }
/* Who owns the row, under its name — the man to ask about a class that has not
   sent in. Muted and small: it answers a second question, not the first one. */
.ovrow .nm small{ display:block; font-size:11px; color:var(--ink-3);
  margin-top:1px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }

/* ---- the horizontal bars and the meter -------------------------------------
   ⚠ THESE WERE MISSING FOR AN HOUR AND THE DASHBOARDS LOOKED FINE WITHOUT
   THEM. `bar()` and `meter()` went into ovkit.js with the Attendance Overview's
   classes but not their own, so Maintenance drew "Room 011-3  6 days" as three
   words of plain text — no track, no fill, nothing wrong-looking enough to
   notice in the markup. Found by looking at the picture, which is the only way
   it could have been found. */
.ovhbs{ display:grid; gap:8px }
.ovhb{ display:grid; grid-template-columns:minmax(58px,auto) 1fr auto;
  align-items:center; gap:10px; font-size:12.5px; color:var(--ink-2) }
.ovhb .k{ min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.ovhb .track{ height:10px; background:var(--seq-0); border-radius:5px;
  overflow:hidden; display:block }
/* Sequential by default — more is darker. The step is the VALUE's and never
   the row's position, so re-sorting a list cannot repaint it. */
.ovhb .fill{ display:block; height:100%; background:var(--seq-2); border-radius:5px }
.ovhb .fill.lo{ background:var(--seq-1) }
.ovhb .fill.hi{ background:var(--seq-3) }
/* Where the bar carries a STATE rather than a magnitude — days a job has
   waited, hours against what somebody will work — status owns it and the ramp
   stays out. Those are different questions and the palette keeps them apart. */
.ovhb .fill.s-good{ background:var(--good-deep) }
.ovhb .fill.s-warn{ background:var(--warn) }
.ovhb .fill.s-crit{ background:var(--crit) }
.ovhb .v{ font-weight:700; color:var(--ink); white-space:nowrap }

/* One ratio against a limit. `--data` is what COLORS.md names as the meter
   fill, in those words. */
.ovmeter .track{ display:block; height:13px; background:var(--seq-0);
  border-radius:7px; overflow:hidden }
.ovmeter .fill{ display:block; height:100%; background:var(--data); border-radius:7px }
.ovmeter .lab{ display:flex; justify-content:space-between; gap:12px;
  font-size:12.5px; color:var(--ink-2); margin-top:7px }
.ovmeter .lab b{ color:var(--ink) }

.ovgo{ display:block; text-align:center; background:var(--brand); color:#fff;
  text-decoration:none; border-radius:10px; padding:12px;
  font:700 14px 'Assistant',system-ui,sans-serif; margin-top:2px }
.ovgo:hover{ background:var(--brand-2) }

/* ⚠ THE ONE THING THAT CANNOT WAIT, above the headline — 5 September 2026:
   *"Surface unresolved attendance immediately. This is the most urgent
   information."* Crit, and solid rather than the soft tint the panels wear,
   because it has to win against a hero sitting directly under it. It is a LINK
   and not a notice: a banner naming a problem with no way to act on it is the
   thing he objected to in the first place. */
.ovalert{ display:flex; align-items:center; gap:10px; text-decoration:none;
  background:var(--crit); color:#fff; border-radius:11px; padding:13px 15px;
  font:700 14.5px/1.35 'Assistant',system-ui,sans-serif; margin:0 0 10px }
.ovalert:hover{ filter:brightness(1.07) }
.ovalert .ch{ margin-inline-start:auto; font-style:normal; font-size:17px }

/* ---- the stopwatch — 5 September 2026 --------------------------------------
   Only ever on screen when `?perf` is in the address. Deliberately plain and
   deliberately ugly: it is an instrument, it must be unmistakable, and nobody
   should be able to confuse it with part of the app. Monospace because the
   columns have to line up to be read at a glance, and `pre-wrap` because the
   panel is built as lines of text rather than as markup. */
#perfbar{ position:fixed; left:8px; right:8px; bottom:8px; z-index:9999;
  background:#111; color:#9fe89f; border:1px solid #2c2c2c; border-radius:8px;
  padding:10px 12px; font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;
  white-space:pre-wrap; overflow-x:auto; cursor:pointer;
  box-shadow:0 6px 20px rgba(0,0,0,.35) }
#perfbar[data-copied]{ color:#fff; border-color:#9fe89f }
#perfbar[data-copied]::after{ content:' ✓ copied' }
@media print{ #perfbar{ display:none !important } }

/* WHICH DAY, AND HOW OLD. Replaces the area's blurb once a dashboard has drawn
   — see `stamp()` in area.js. Deliberately much quieter than the 16.5px blurb
   it stands in for (`.soonwrap .blurb` in nav.css): it is a label on the
   numbers, not a sentence to read. */
.soonwrap .blurb.stamp{ font-size:13px; line-height:1.5; color:var(--ink-3);
  margin:0 0 14px; letter-spacing:.1px }
.ovquiet{ margin:0; font-size:13px; color:var(--ink-3) }
.ovnone{ font-size:14px; color:var(--ink-2) }
/* A read that failed says so. A dashboard quietly missing a tile it could not
   compute is a dashboard claiming everything is fine — see morning.js. */
.ovbroke{ margin:10px 0 0; font-size:12.5px; line-height:1.55;
  color:var(--crit-ink); background:var(--crit-soft);
  border:1px solid var(--crit-line); border-radius:9px; padding:9px 12px }

/* The picture of a row's own arithmetic — `split()` in ovkit.js. It does not
   exist on a telephone: the row already says "13 in · 1 late · 1 absent" in
   words, and 110px of bar on a 430px screen would be taking room from the
   words to say the same thing worse. It is one of the things the board buys. */
.ovmini{ display:none }
/* The things a wide screen buys, and which a telephone therefore never draws:
   the strip of figures across the top, and the card that is a way into every
   tab of the area. Menachem's Overviews on a phone are finished and were not
   what he asked to change — *"right now they're all very cell phone friendly
   views"* was a compliment with a but on the end. */
.ovtiles.board, .ovcard.boardonly{ display:none }

/* ===========================================================================
   THE BOARD — what a dashboard becomes on an iPad or a computer.

   Menachem, 7 September 2026, asked what the empty 600px on a laptop should
   buy him: *"I'm thinking for both iPads and computers the dashboard should be
   much more dashboard like squares and grafts. Just make it more interesting."*

   ⚠ THE STANDING RULE THIS SERVES is his of 6 September — *"I want the app to
   work on phone and computer"* — which until today was recorded in exactly one
   place, a comment in servicereport.js.

   ⚠ ONE MARKUP, TWO LAYOUTS, AND THE CHOICE IS CSS'S. Nothing in the six
   dashboards asks how wide the window is. A dashboard that branched on
   `innerWidth` in JavaScript would be wrong the moment somebody turned an iPad
   or dragged a window — the scar this project already carries against deciding
   anything in pixels — so both readings live in the same markup and only this
   block picks between them. It follows that a telephone is UNTOUCHED by
   everything below: 819px and narrower never enters here.

   WHY 820px AND NOT 900. 900 is where the menu becomes a rail, and that is a
   different question from whether the content can be a board. An iPad held
   upright is 820 (Air) or 834 (11" Pro) — under 900, so it keeps the burger,
   and the whole 820 is the content's. That is two comfortable columns, and it
   is precisely the width Menachem described as "very cell phone friendly".

   ⚠ TWO COLUMNS, AND NEVER THREE — MEASURED, NOT CHOSEN. The first draft used
   `auto-fit`, which gave three columns of 376px on a 1440px laptop, and the
   picture showed why that is wrong: an Overview row is a class name, a rebbi,
   a status and a chevron, and at 376px "4th Grade, R.F." broke over three
   lines. The row's content sets the floor, not the page's width. Two columns
   of ~530px is the widest the content actually wants; past that the board is
   capped and centred rather than stretched, for the same reason `.allwrap`
   caps at a printed page.
   =========================================================================== */
@media (min-width: 820px) {

  /* `ovpage` is set by area.js only once a dashboard has really drawn — never
     for the hub or the "not built yet" placeholder, which are a list of buttons
     and a paragraph and want to stay a readable column. */
  body.ovpage .soonwrap{ max-width:1120px; padding:34px 26px 76px }
  body.ovpage .soonwrap h1{ font-size:34px }
  body.ovpage .soonwrap .blurb.stamp{ font-size:14px; margin-bottom:18px }

  /* ⚠ RULE 2 SURVIVES THE BOARD, WHICH IS WHY THE CARDS ARE THE ONLY THING IN
     COLUMNS. The alarm, the headline and the strip of figures stay the width
     of the page: a dashboard whose alarm is a small box in a corner has
     stopped alarming. The way in at the foot spans for the same reason — it is
     the page's verb. `columnise()` in area.js puts the cards, and only the
     cards, into `.ovcols`; everything else is left where the dashboard put it.

     `columns` and not a grid: a grid lays out in rows, and a row is as tall as
     its tallest cell, so a 130px card beside a 430px one leaves 300px of white
     under it. Cards flow. */
  .ovcols{ columns:2; column-gap:12px }
  .ovcols > .ovcard{ break-inside:avoid; margin:0 0 12px }
  /* Safari has honoured `-webkit-column-break-inside` for far longer than the
     unprefixed property, and a card split down the middle of a row of squares
     is the exact failure this is guarding against on the one browser this
     container cannot photograph. */
  .ovcols > .ovcard{ -webkit-column-break-inside:avoid; page-break-inside:avoid }

  /* A card the board draws and a telephone does not (`panel()`, `buses()` in
     ovkit.js), and the row that is its exact opposite: a phone's plain count
     of a fact the board is already showing as a graphic four inches away.
     Together they are how one markup serves two screens without either one
     saying the same thing twice — see the note on `cls` in goRow(). */
  .ovcard.boardonly{ display:block }
  /* ⚠ THE SECOND SELECTOR IS NOT REDUNDANT AND THE FIRST ONE ALONE DID NOT
     WORK. `.ovcard.sq > .ovrow` further down this block sets `display:flex` to
     make a square, and it is (0,2,1) against this rule's (0,2,0) — so the row
     stayed on screen, the board showed "Booked out today 3" as a square AND
     "Scheduled out · 3 booked out of 67" as a ring four inches away, and the
     only thing that caught it was looking at the picture again. */
  .ovrow.phoneonly,
  .ovcard.sq > .ovrow.phoneonly{ display:none }

  /* ---- the strip of figures across the top ------------------------------
     From the mock-up Menachem sent on 7 September 2026: four squares, each an
     icon-less figure with a label and a line of detail under it — *"Present /
     146 / includes 7 late"*. They are the headline itemised, which is rule 5
     (*"Instead of only '8 of 10', show 8 in · 2 absent"*) given the room to be
     read at a glance instead of in a sentence.

     ⚠ 158px AND NOT 190. At 190 an upright iPad fitted three across and hung
     the fourth square on a row of its own with two thirds of a row of white
     beside it — the strip stopped reading as a strip. 158 is the width at
     which 768px of content (an iPad Air, upright, no menu rail) still takes
     four. `auto-fit` collapses the tracks it does not need, so a laptop's
     1068px does not cut the same four squares into six. */
  .ovtiles.board{ display:grid;
    grid-template-columns:repeat(auto-fit, minmax(158px, 1fr)); gap:10px }
  .ovtile{ padding:14px 16px }
  .ovtile .n{ font-size:34px }
  .ovtile .l{ font-size:13.5px; margin-top:2px; font-weight:600; color:inherit }
  .ovtile small{ display:block; font-size:12px; color:var(--ink-3);
    margin-top:3px; line-height:1.35 }
  .ovtile.act small, .ovtile.good small, .ovtile.bad small{ color:inherit;
    opacity:.78 }
  a.ovtile.tap{ position:relative; display:block; text-decoration:none;
    color:inherit }
  a.ovtile.tap .ch{ position:absolute; top:11px; right:12px; font-style:normal;
    font-weight:700; font-size:16px; color:var(--ink-3); line-height:1 }
  a.ovtile.tap:hover{ border-color:var(--brand) }
  a.ovtile.tap:hover .ch{ color:var(--brand) }

  /* ---- a graphic, a number, a caption, and the tile is the way in --------
     ⚠ THIS WAS A CARD LISTING EVERY TAB OF THE AREA AND IS NOT ANY MORE. He
     took it apart tab by tab on 7 September 2026 — marking is reachable by
     clicking a class, To resolve by the red banner above, Minyan does not
     belong — and ruled: *"scheduled out and going home each have their own
     graphs, so I don't want any buttons on the bottom. There are the buttons
     already at the top."* The strip IS the list of tabs. A graphic earns its
     place by its NUMBER, not by a tab existing. See `panel()` in ovkit.js.

     One shape for every tile in a panel — the buses use it too — so a card of
     graphics reads as one row of things rather than as a collection. */
  .ovtilerow{ display:grid; gap:12px;
    grid-template-columns:repeat(auto-fit, minmax(128px, 1fr)) }
  .ovcount{ display:block; text-align:center; text-decoration:none;
    color:inherit; border:1px solid var(--line-2); border-radius:12px;
    padding:12px 10px 11px }
  .ovcount b{ display:block; font:700 24px/1.05 'Assistant',system-ui,sans-serif;
    letter-spacing:-.5px; color:var(--ink) }
  .ovcount span{ display:block; font-size:13px; font-weight:600;
    color:var(--ink); margin-top:3px }
  .ovcount small{ display:block; font-size:12px; color:var(--ink-2);
    margin-top:2px; line-height:1.4 }
  .ovcount:hover{ border-color:var(--brand) }

  /* ---- one mark per boy -------------------------------------------------
     ⚠ REPLACED A RING, AND THE REASON IS ARITHMETIC. Scheduled out and Going
     home are 3 of 67 and 2 of 67 — 4.5% and 3% — and as rings they were two
     almost-empty circles that looked identical to each other. A ring compares
     one quantity against its own whole, which is the wrong question for a
     handful; a mark per boy is a quantity you can see all of, and three
     figures beside two figures is a comparison the eye makes without reading.

     `min-height` so a tile holding two figures and a tile holding nine are the
     same height and the row does not step up and down. `--data` because these
     are plain quantities and not assertions — docs/COLORS.md. */
  .ovfigs{ display:flex; flex-wrap:wrap; justify-content:center; gap:3px;
    min-height:24px; align-items:flex-end; margin-bottom:7px }
  .ovfigs svg{ width:11px; height:18px; fill:var(--data); flex:none }
  .ovfigs.s-warn svg{ fill:var(--warn) }
  .ovfigs.s-crit svg{ fill:var(--crit) }

  /* ---- the buses --------------------------------------------------------
     His, 7 September 2026: *"Let's do something a little fun."* One tile per
     bus that is actually on the run, the number painted on its side and the
     count under it. The whole tile is the way into the bus lists, so rule 3
     holds for a cartoon exactly as it does for a row.

     ⚠ IT IS NOT DRAWN ON A TELEPHONE, like the rest of the board. A 430px
     screen would fit one bus and a half, and the Bus lists tab is one tap away
     on the strip. */
  /* The bus is an `.ovcount` with a drawing where the figures go, so it
     inherits the tile, the number and the caption from the rule above. */
  .ovbus svg{ display:block; width:100%; height:auto; max-width:168px;
    margin:0 auto 6px }
  .ovbus span{ font-weight:400; color:var(--ink-2) }
  /* A bus that leans forward a little when you reach for it. Motion is opt-out
     for anybody who has asked their machine for less of it. */
  .ovbus svg{ transition:transform .16s ease }
  .ovbus:hover svg{ transform:translateX(3px) }
  @media (prefers-reduced-motion: reduce){
    .ovbus svg{ transition:none }
    .ovbus:hover svg{ transform:none }
  }

  /* Bigger, because there is room to be read from further away than a phone is
     ever held — across a desk, or over somebody's shoulder. */
  .ovhero{ padding:22px 24px }
  .ovhero .n{ font-size:52px }
  .ovhero .n small{ font-size:22px }
  .ovhero .l{ font-size:16px; margin-top:8px }
  .ovalert{ padding:16px 20px; font-size:16px }
  .ovcard{ padding:15px 17px }
  .ovcard > b.h{ font-size:14px; margin-bottom:11px }
  .ovrow{ font-size:14.5px; padding:10px 0 }
  .ovrow .st{ font-size:13.5px }
  .ovrow .nm small{ font-size:12px }
  .ovgo{ padding:15px; font-size:15px }
  .ovbar{ height:34px }
  .ovkey span{ font-size:13.5px }

  /* ---- the squares ------------------------------------------------------
     A card marked `sq` holds counts rather than a list of things — "To resolve
     2 of 4 · Still on paper 1 · Davened 18 of 20". On a phone that is rightly
     a list. Given room it is a set of squares, which is what a dashboard is
     for: the numbers readable at a glance, in parallel, instead of one under
     the other.

     ⚠ EVERY SQUARE IS STILL THE WAY IN — rule 3, and the reason the chevron is
     kept and moved to the corner rather than dropped. A hover lift would say
     the same thing to a mouse and nothing at all to a finger on an iPad, which
     is half the screens this block exists for. */
  .ovcard.sq{ display:grid; gap:10px;
    grid-template-columns:repeat(auto-fit, minmax(150px, 1fr)) }
  .ovcard.sq > b.h{ grid-column:1 / -1; margin-bottom:0 }
  .ovcard.sq > .ovrow{ position:relative; display:flex; flex-direction:column;
    align-items:flex-start; justify-content:flex-start; gap:3px;
    border:1px solid var(--line-2); border-radius:11px;
    padding:12px 13px; min-height:94px }
  .ovcard.sq > a.ovrow.tap:hover{ border-color:var(--brand) }
  /* The number first and big — it is the answer; the label is the question. */
  /* 24px and not 30: a square's figure is not always "3". "18 of 20" and "2 of
     4 resolved" are figures too — his ruling of 5 September that To resolve
     reads as a ratio — and at 30px they broke over three lines and made one
     square twice the height of its neighbours. */
  .ovcard.sq > .ovrow .st{ order:-1; font-size:24px; line-height:1.15;
    letter-spacing:-.6px; padding-right:16px }
  .ovcard.sq > .ovrow .st:empty{ display:none }
  .ovcard.sq > .ovrow .nm{ flex:0 1 auto; white-space:normal; line-height:1.35 }
  .ovcard.sq > .ovrow .nm small{ white-space:normal; overflow:visible }
  .ovcard.sq > .ovrow .ch{ position:absolute; top:9px; right:10px }
  /* A dot inside a square would sit above the number in the column flow, which
     is a coloured full stop floating on its own. The squares that carry one
     are the counts, and their status is already the number's colour. */
  .ovcard.sq > .ovrow .ovdot{ position:absolute; bottom:11px; right:11px }

  /* ---- the row's own arithmetic, as a picture ---------------------------
     ⚠ THE CARD DECIDES, NOT THE WINDOW — and this is a container query rather
     than a media query on purpose. Whether a 108px bar fits beside a class
     name depends on how wide THAT CARD is, and one viewport gives two answers:
     the menu rail is 236px and folds to 54px, so pressing the chevron widens
     every card on the page by 182px without the viewport moving at all. Asked
     of the window, the bar would be wrong in one of those two states; asked of
     the card, it is right in both.

     Measured, on the picture that made this rule: at 376px of card, the row is
     a dot, a class name, a rebbi, "11 in · 1 late · 1 absent" and a chevron,
     and a bar on top of that broke "4th Grade, R.F." over three lines. 430 is
     where it stops doing that — so an upright iPad gets the squares and the
     figures and no bar, and a landscape iPad and a laptop get all three.

     ⚠ AND WHERE @container IS NOT UNDERSTOOD, NOTHING HAPPENS — the bar stays
     `display:none` and the row is exactly the row that ships today. That is
     the right way round: the fallback is the proven layout, not a broken one.
     It matters because this container has Chromium only and no WebKit to
     install, so his iPad is the one screen here that cannot be photographed. */
  .ovcard{ container-type:inline-size }

  @container (min-width: 430px) {
    .ovmini{ display:flex; flex:none; width:108px; height:10px; gap:1.5px;
      border-radius:5px; overflow:hidden; margin-inline-start:auto }
    .ovmini i{ display:block; height:100% }
    /* In a square there is no room beside the words, and the bar becomes the
       square's own footer instead — full width, under the label. */
    .ovcard.sq > .ovrow .ovmini{ width:100%; margin:5px 0 0 }
  }
}

/* ===========================================================================
   THE DATE ON A DASHBOARD — one account only. See app/ovdate.js.

   Menachem, 7 September 2026: a date selector on the Attendance and Middos
   boards *"only to my account because I want to show it to people"*.

   ⚠ ALL-NEW CLASS NAMES, APPENDED, AND NOTHING ABOVE IS TOUCHED. `style.css` is
   loaded by every screen in the app, which is a real reason to be careful — and
   the rule earned on 1 September is that MODIFIED or REMOVED selectors can
   reach anywhere while APPENDED ones reach only what names them. Nothing but
   ovdate.js writes `ovdate`, so this block cannot move a pixel on the other
   sixty-four screens.

   ⚠ IT SITS UNDER THE STAMP AND IS DELIBERATELY QUIET. It is a control for
   demonstrating the app, not a fact about the yeshiva, so it must not compete
   with the headline three inches below it — no panel, no border round the
   group, small type, and the one coloured thing on it is nothing at all.
   =========================================================================== */
.ovdate{ display:flex; align-items:center; flex-wrap:wrap; gap:8px;
  margin:0 0 14px; font-size:13px; color:var(--ink-3) }
.ovdate label{ font-weight:600 }
/* `date` inputs are drawn by the browser and ignore most of a font stack, so
   what is set here is the box round it — enough that it reads as part of this
   app and not as a form control dropped on the page. */
.ovdate input[type="date"]{ font:inherit; font-size:13.5px; color:var(--ink);
  background:var(--panel); border:1px solid var(--line); border-radius:8px;
  padding:5px 8px; line-height:1.2 }
.ovdate input[type="date"]:hover{ border-color:var(--brand) }
.ovdate .ovdate-now{ font:inherit; font-size:13px; font-weight:600;
  color:var(--brand); background:none; border:0; border-bottom:1px solid currentColor;
  padding:0 0 1px; cursor:pointer }
/* ⚠ SAYING WHOSE IT IS, ON THE SCREEN. He is going to show this to people, and
   the first question a person asks about a control they cannot find on their
   own screen is whether theirs is broken. One faint line answers it before it
   is asked. */
.ovdate .ovdate-who{ font-size:11.5px; color:var(--ink-3); opacity:.75;
  border:1px solid var(--line-2); border-radius:999px; padding:2px 8px }
@media (min-width: 820px) {
  .ovdate{ font-size:13.5px; margin-bottom:18px }
}

/* ---- MASTERLIST -------------------------------------------------------------
   The Class list area's own screen — every boy in one list, with its board
   behind a switch. `ov-classes.js` carries why it is that way round.

   ⚠ THESE RULES ONLY ADD. Every selector below is a name nothing else in this
   stylesheet or in the app uses, so no existing screen can be reached by them —
   which is the difference CLAUDE.md draws between appending to style.css and
   modifying it, and the reason this lands without a whole-app re-check.
   -------------------------------------------------------------------------- */
.clsheet, .clboard{ background:var(--panel); border:1px solid var(--line);
  border-radius:12px; overflow:hidden; margin:0 0 10px }
.clboard{ background:none; border:0; border-radius:0; overflow:visible }

.cltools{ display:flex; align-items:center; gap:9px; flex-wrap:wrap;
  padding:11px 13px; border-bottom:1px solid var(--line) }
.cltools.bare{ border-bottom:0; padding:0 0 10px; justify-content:flex-end }
.clsearch{ flex:1; min-width:170px; border:1px solid var(--line); border-radius:8px;
  padding:8px 11px; font:14px 'Assistant',system-ui,sans-serif; color:var(--ink);
  background:var(--bg) }
.clsearch:focus{ outline:2px solid var(--brand); outline-offset:1px }
.clbtn{ border:1px solid var(--line); background:var(--panel); color:var(--ink);
  border-radius:8px; padding:7px 11px; font:600 13px 'Assistant',system-ui,sans-serif;
  cursor:pointer; white-space:nowrap }
.clbtn:hover{ border-color:var(--brand) }
.clbtn.solid{ background:var(--brand); color:#fff; border-color:var(--brand) }
.clbtn:focus-visible{ outline:2px solid var(--brand); outline-offset:2px }
.clcount{ font-size:12.5px; color:var(--ink-3); font-variant-numeric:tabular-nums }

/* ⚠ THE TAB BAR IS WHOSE LIST, AND IT IS NOT THE LIST/OVERVIEW SWITCH — two
   axes, two controls, and they must not look like one another or the office
   will read them as one broken thing. So: tabs sit ABOVE, full width, on the
   page ground with the standing one lifted onto the panel; the switch sits
   INSIDE the tools row, small and pill-shaped, hard right. The tab bar borrows
   the strip's shape (`areatabs.js`) on purpose — it is the same idea one floor
   down, and a person who has learned the strip has learned this. Added
   8 September 2026 with the Alumni tab. */
.cltabs{ display:flex; gap:2px; overflow-x:auto; scrollbar-width:none;
  border-bottom:1px solid var(--line); margin:0 0 13px }
.cltabs::-webkit-scrollbar{ display:none }
.cltabs a{ text-decoration:none; color:var(--ink-2); font-size:13.5px;
  font-weight:600; padding:9px 14px 8px; border-bottom:3px solid transparent;
  white-space:nowrap; border-radius:8px 8px 0 0 }
.cltabs a:hover{ color:var(--ink); background:var(--line-2) }
.cltabs a.on{ color:var(--ink); background:var(--panel);
  border-bottom-color:var(--brand) }
/* The count is a fact about the tab, not a badge shouting for attention — same
   register as the class header's count on the row below it. */
.cltabs a .n{ margin-left:6px; font-weight:400; font-size:12.5px;
  color:var(--ink-3); font-variant-numeric:tabular-nums }
.cltabs a.on .n{ color:var(--ink-2) }

/* The fire-drill print, under the list. Quiet, because it is never what
   somebody opened this screen for, and never hidden, because the one morning it
   is wanted nobody will be in a mood to hunt. */
.clprint{ margin:15px 0 0; font-size:13px }
.clprint a{ color:var(--brand); font-weight:600; text-decoration:none }
.clprint a:hover{ text-decoration:underline }
.clprint span{ color:var(--ink-3); margin-left:8px }

/* The column picker. `details` so it opens and closes without a line of script,
   and closes on Escape, which a hand-built menu would have to be taught. */
.clcols{ position:relative }
.clcols > summary{ list-style:none; display:inline-block }
.clcols > summary::-webkit-details-marker{ display:none }
.clcolbox{ position:absolute; z-index:5; top:calc(100% + 5px); left:0;
  background:var(--panel); border:1px solid var(--line); border-radius:10px;
  box-shadow:0 6px 22px rgba(23,26,33,.13); padding:9px 11px; min-width:172px;
  display:grid; gap:5px }
.clcolbox label{ display:flex; align-items:center; gap:7px; font-size:13px;
  color:var(--ink-2); white-space:nowrap; cursor:pointer }

/* The switch. The view you are NOT on is the plain one — the raised tab is
   where you are standing, the same way the strip marks its current tab. */
.clsw{ display:inline-flex; background:var(--bg); border:1px solid var(--line);
  border-radius:9px; padding:2px; gap:2px; margin-left:auto }
.clsw a{ text-decoration:none; color:var(--ink-2); font-size:12.5px; font-weight:600;
  padding:5px 12px; border-radius:7px }
.clsw a:hover{ color:var(--ink) }
.clsw a.on{ background:var(--panel); color:var(--ink); border:1px solid var(--line);
  box-shadow:0 1px 2px rgba(23,26,33,.05) }

/* ⚠ THE TABLE SCROLLS INSIDE ITS OWN BOX AND THE PAGE NEVER DOES. Ten columns
   do not fit a telephone and never will; a body that scrolls sideways takes the
   menu and the heading with it, which is how a list becomes unusable rather
   than merely wide. */
.clscroll{ overflow-x:auto; -webkit-overflow-scrolling:touch }
.cltable{ border-collapse:collapse; width:100%; font-size:13px; white-space:nowrap }
.cltable thead th{ position:sticky; top:0; z-index:2; background:var(--panel);
  text-align:left; font-size:11px; font-weight:700; letter-spacing:.4px;
  text-transform:uppercase; color:var(--ink-3); padding:8px 11px;
  border-bottom:1px solid var(--line) }
.cltable tbody td{ padding:7px 11px; border-bottom:1px solid var(--line-2);
  color:var(--ink-2) }
.cltable tbody tr:hover td{ background:var(--bg) }
.cltable td.clnm{ color:var(--ink); font-weight:600 }
.cltable td.clnm a{ color:inherit; text-decoration:none }
.cltable td.clnm a:hover{ color:var(--brand); text-decoration:underline }
.cltable td.clheb{ font-size:14px }
/* One class opened on the Masterlist — the way back and the class, above its sheet. */
.clone{ display:flex; align-items:baseline; gap:12px; flex-wrap:wrap; margin:0 0 10px; font-size:14px }
.clone b{ font-size:18px }
.clone span{ color:var(--ink-2) }
.clone a:last-child{ margin-left:auto }
.clone a{ color:var(--brand); font-weight:600; text-decoration:none }
.clone a:hover{ text-decoration:underline }
/* A phone or an email on the Masterlist dials or writes — 23 September 2026. */
.cltable td a.cltel{ color:var(--brand); text-decoration:none; font-variant-numeric:tabular-nums }
.cltable td a.cltel:hover{ text-decoration:underline }
/* The date, the age and the telephone numbers all line up as digits. */
.cltable td{ font-variant-numeric:tabular-nums }

/* A boy nobody can be rung about, said on his own row as well as counted at the
   top — the number tells you there are nine, this tells you which. */
.clno{ display:inline-block; font-style:normal; font-size:10.5px; font-weight:700;
  letter-spacing:.3px; text-transform:uppercase; margin-left:7px; padding:1px 6px;
  border-radius:5px; background:var(--crit-soft); border:1px solid var(--crit-line);
  color:var(--crit-ink) }

/* Her sheet groups by class with the rebbi in the header and the count beside
   it. Same shape, deliberately: it is the part of the spreadsheet that works. */
.cltable tr.clgrp td{ background:var(--brand-soft); color:var(--brand);
  font-weight:700; font-size:12px; letter-spacing:.3px; padding:7px 11px;
  border-bottom:1px solid var(--line); position:sticky; left:0 }
/* ⚠ BESIDE THE TITLE, NOT FLOATED RIGHT — 12 October 2026, his: "in the master
   list by the class title it should have a number of students". It was there,
   floated to the far end of a row as wide as every column, past Address and
   off the screen of anybody who had not scrolled sideways. */
.cltable tr.clgrp td .n{ margin-left:12px; font-weight:600; color:var(--ink-2) }
.cltable tr.clgap td{ padding:0; border:0; height:7px; background:var(--bg) }

/* The add row at the foot of each class. Quiet until it is wanted: it is the
   last thing in a group of twenty names and must not compete with them, so it
   is muted text that comes up to brand on hover. `position:sticky; left:0`
   for the same reason the group header has it — the table scrolls sideways
   through eleven columns and this has to stay where the names are.
   `--ink-2` and not the muted `--ink-3`: this is a 12px LINK, and muted ink is
   3.59:1 on the panel, which is enough for a label nobody clicks and not
   enough for AA on text somebody has to read and hit. --ink-2 is 7.94:1. */
.cltable tr.cladd td{ padding:0; border-bottom:1px solid var(--line);
  position:sticky; left:0; background:var(--panel) }
.cltable tr.cladd a{ display:inline-block; padding:7px 11px; font-size:12px;
  font-weight:600; color:var(--ink-2); text-decoration:none }
.cltable tr.cladd a:hover{ color:var(--brand); text-decoration:underline }
.clempty{ padding:16px 13px }

/* ---- A BOY HAS LEFT ------------------------------------------------------
   The standing notice above all three front doors. Menachem, 9 September 2026:
   *"it should pop up on the front page of their app"*, and it stays until each
   person presses Got it.

   ⚠ SAND, WHICH IS "WANTS CARE" — docs/COLORS.md. Not red: a boy leaving is not
   an emergency by the time it is written down, and a front page that shouts in
   red at a thing that is merely to be noted teaches everybody to ignore red.
   Not the data blue either — that means a provision a boy RECEIVES, and this is
   the opposite of a provision. Every pairing here is one the app already uses
   (`.dndaynote.back`, `.tag.off`), so no new contrast claim is made.
   ------------------------------------------------------------------------- */
#t-departures{ margin:0 0 14px }
.dpbox{ background:var(--sand-soft); border-color:var(--sand-line) }
.dpbox h3{ color:var(--sand-ink); display:flex; align-items:baseline; gap:8px }
.dpcount{ font-size:11px; font-weight:700; letter-spacing:0; padding:1px 7px;
  border-radius:99px; background:var(--sand-ink); color:#fff }

.dplist{ list-style:none; margin:0; padding:0 }
.dplist li{ display:flex; flex-wrap:wrap; align-items:baseline; gap:6px 14px;
  padding:9px 0; border-top:1px solid var(--sand-line) }
.dplist li:first-child{ border-top:0; padding-top:2px }
.dpwho{ min-width:0 }
.dpname{ font-size:15.5px; font-weight:600; color:var(--ink); text-decoration:none;
  line-height:1.3 }
.dpname:hover,.dpname:focus-visible{ color:var(--brand); text-decoration:underline }
.dpclass{ display:block; font-size:12px; color:var(--sand-ink); line-height:1.45 }
.dpwhen{ font-size:12.5px; color:var(--ink-2); line-height:1.45 }
/* ⚠ PUSHED RIGHT BY `margin-left:auto`, NOT BY `justify-content` — the row
   wraps on a telephone, and space-between on a wrapped row strands the button
   on a line of its own at the far edge with nothing beside it. */
.dpgot{ margin-left:auto; flex:none; border:1px solid var(--sand-line);
  background:var(--panel); color:var(--sand-ink); font:inherit; font-size:12.5px;
  font-weight:600; border-radius:8px; padding:5px 12px; cursor:pointer }
.dpgot:hover{ border-color:var(--sand-ink) }
.dpgot:disabled{ opacity:.6; cursor:default }
.dpwarn{ margin:0; font-size:13.5px; line-height:1.65; color:var(--crit-ink);
  font-weight:600 }

/* --------------------------------------------------------------------------
   THE DEAN'S FOURTH LIST — the birthdays coming up. 14 September 2026.

   ⚠ EVERY SELECTOR HERE IS NEW AND NOTHING ABOVE IS TOUCHED. `style.css` is
   loaded by all 34 screens, so a MODIFIED selector reaches every one of them
   and an appended one reaches only what names it. The rows borrow `.dnlist`,
   `.dnwho`, `.dnname`, `.dnclass` and `.dnwhat` unchanged — the vocabulary the
   other three lists already taught him — and add a wrap, a second line in the
   right-hand column, and a note that spans the row.
   -------------------------------------------------------------------------- */
.bdlede{ margin:0 0 8px; font-size:12.5px; color:var(--ink-3) }
/* ⚠ THE WRAP IS ON `.bdrow`, NOT ON `.dnlist li`. Putting `flex-wrap` on the
   shared row would have reached the absent, late and going-home lists too —
   harmlessly today, because those rows hold exactly two children, and that is
   precisely the kind of "harmless today" this file keeps paying for. */
.dnlist li.bdrow{ flex-wrap:wrap }
/* ⚠ THE HEBREW DATE IS THE HEADLINE AND THE ENGLISH DAY SITS UNDER IT — his
   ruling of 14 September 2026, overturning the first build, which had them the
   other way round. The Hebrew date is the birthday; the English day is only
   where it lands this year. So `.bdheb` carries the weight the civil line used
   to, and `.bdcivil` is the small grey one. */
.bdheb{ display:block; font-size:14px; font-weight:600; color:var(--ink);
  line-height:1.4 }
/* Within the week. The day is already there in words — the colour is an
   emphasis on a fact, never the only place the fact is said. */
.bdsoon .bdheb{ color:var(--brand) }
.bdcivil{ display:block; font-size:12px; color:var(--ink-3); line-height:1.5 }
/* Spans the whole row, under both columns: a birthday on a day with no school,
   or a boy the office has held. */
.bdnote{ flex:1 0 100%; margin-top:7px; padding:6px 9px; border-radius:8px;
  background:var(--sand-soft); color:var(--sand-ink); text-align:left;
  font-size:12.5px; line-height:1.5 }

/* ==========================================================================
   THE DEAN'S DOOR AS A BOARD — his instruction, 14 September 2026, and he
   picked this arrangement out of three drawn for him: only the day picker
   spans, and the search and the four lists flow together in two columns from
   the very top.

   ⚠ ONE MARKUP, TWO LAYOUTS, AND THIS STYLESHEET PICKS. `deanhome.js` does not
   ask how wide the window is and never will — the 7 September rule the six
   dashboards were built to, and the reason his telephone is byte-for-byte the
   screen that shipped this morning. A layout decided in JavaScript is wrong the
   moment somebody turns an iPad or drags a window.

   ⚠ 900px, NOT THE DASHBOARDS' 820. The boards start at 820 because an area
   page has the whole width to itself there. This door does not: it lives in
   `.wrap`, which is 640px until 900px, so a board at 820 would be two columns
   of about 300px — and 376px is the measured width that broke "4th Grade, R.F."
   over three lines when the dashboards were built. 900 is where `.wrap` becomes
   980px and the two columns are ~470px, which the rows actually fit in.

   ⚠ AND IT IS `.dncols`, NOT THE DASHBOARDS' `.ovcols`, FOR EXACTLY THAT
   REASON. Reusing the class would have been the tidier-looking thing and would
   have switched this door to two columns at 820 — inside a 640px wrap. A shared
   name is only a saving where the two things share the rule, and here they do
   not.

   ⚠ `columns`, NOT A GRID. A grid lays out in rows and a row is as tall as its
   tallest cell, so the six-row birthday card beside the two-row late list would
   leave a hand's width of white under it. Cards flow.
   ========================================================================== */
@media (min-width: 900px) {
  .dncols{ columns:2; column-gap:14px }
  /* ⚠ THE CARDS ARE `.dbox`, NOT `.ovcard` — the search box the office wrote
     (`deskfind.js`) is a `.dbox dfind` section and the four lists are
     `.dbox dnbox`, so the break rule is keyed on what is actually there rather
     than on the dashboards' class. A card split down the middle of a row is the
     failure this is guarding against. */
  .dncols > .dbox{ break-inside:avoid; -webkit-column-break-inside:avoid;
    page-break-inside:avoid; margin:0 0 14px }
  .dncols > .dbox:last-child{ margin-bottom:0 }
}

/* ==========================================================================
   THE SENTENCE UNDER A BADGE — 17 September 2026.

   Menachem, about the red numbers across the app: *"Right now it says they're
   over 50, but only 3 kids are in my office"*, and *"when I click on it, it
   doesn't take me to everything owed."* Every badged screen now says in words
   what its number counted, at the top of the page the badge landed on.

   ⚠ IT IS GLOBAL BECAUSE IT IS ONE COMPONENT ON SIX SCREENS. `app/says.js`
   draws it; four screens use it tonight and two — Sent to me and Results —
   carry their own copy of these rules from earlier the same evening, noted in
   says.js as a tidy-up rather than done to shipped screens at midnight.

   ⚠ BRAND-SOFT AND NOT RED. This is an explanation; the alarm is the badge
   itself and whatever the page is about. `.bad` is the one case that must look
   wrong — a read that FAILED, which is the opposite fact from nothing owed and
   the identical picture from outside.
   ========================================================================== */
.saysline{margin:0 0 14px;padding:10px 13px;border-radius:10px;
  font-size:13.4px;line-height:1.55;max-width:62ch;
  background:var(--brand-soft);color:var(--brand)}
.saysline b{font-weight:700}
.saysline.bad{background:var(--crit-soft);border:1px solid var(--crit-line);
  color:var(--crit-ink)}

/* ---- the next 7 days — 23 September 2026, week.js -------------------------
   His option A: seven tiles, today green, Shabbos sand, one bar each. */
.w7{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:14px 15px;margin:0 0 14px}
/* Seven tiles to a screen, and the rest a sideways scroll — his, 23 September:
   "a scrollable section that they can see as far ahead as they scroll". */
/* ⚠ FIVE TO A PHONE SCREEN, BIGGER — his, 24 September 2026, on his phone with
   option C live: "I don't think we need 7 days you can make the squares bigger
   and have less days." It was exactly seven, his of 23 September ("fit 7 days
   right now it fits 7 and 1/2"), and still whole days with no half tile — the
   clean edge was the point of that ruling and is kept. Four 6px gaps = 24px. */
.w7strip{display:grid;grid-auto-flow:column;grid-auto-columns:calc((100% - 24px) / 5);gap:6px;
  overflow-x:auto;scroll-snap-type:x mandatory;overscroll-behavior-x:contain;
  padding:2px 2px 8px;margin:12px -2px 0;scrollbar-width:thin}
.w7t{scroll-snap-align:start}
/* A class's schedule change — purple, and only on the strips of the people
   whose class it is (week.js). */
.w7t.clsday{border-color:#c9b4e6}
.w7det p.w7cls{margin:8px 0 0;padding:8px 10px;border-radius:9px;background:#f3ecfb;
  border:1px solid #d9c8f0;color:#4b2a78}
.w7det p.w7cls b{display:inline;font-size:13.5px}
.w7t .m{font-size:9.5px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--brand);min-height:12px}
.w7t.now .m{color:#dfeceb}
.w7t{border:1px solid var(--line);border-radius:10px;padding:7px 2px 6px;text-align:center;background:var(--bg);
  font:inherit;color:var(--ink);cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:1px}
.w7t .d{font-size:11.5px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;color:var(--ink-3)}
.w7t .n{font-size:24px;font-weight:800;line-height:1.2}
.w7t .h{font-size:10.5px;color:var(--ink-3);font-family:'FrankRuhl','Assistant',serif;min-height:13px}
.w7t.shab{background:var(--sand-soft);border-color:var(--sand-line)}
.w7t.now{background:var(--brand);border-color:var(--brand);color:#fff}
.w7t.now .d,.w7t.now .h{color:#dfeceb}
/* The ring on the open day is drawn INSIDE the tile — his photo, 24 September:
   "it's cut slightly at the top and side". An outline outside the box is
   clipped by the strip, which scrolls and so cuts off anything past its edge.
   On today's teal tile the ring is white, or it would vanish into it. */
.w7t.open{outline:2px solid var(--brand);outline-offset:-4px}
.w7t.now.open{outline-color:#fff}

/* ---- OPTION C — 24 September 2026, his "I'm liking c" of four ---------------
   The dates are slim — weekday, number, and DOTS for what is on them — and the
   open day below gets the width: what it is, its Shabbos/Yom Tov times, his
   classes' changes, and whose birthday it is, big, with faces. */
.w7dots{display:flex;justify-content:center;gap:3px;height:7px;margin-top:3px}
.w7dots i{width:7px;height:7px;border-radius:50%}
.w7dots .off,.w7key .off{background:var(--crit)} .w7dots .early,.w7key .early{background:var(--warn)}
.w7dots .ev,.w7key .ev{background:var(--sand-line)} .w7dots .cls,.w7key .cls{background:#7b4fb3}
/* green, his, 24 September — gold sat beside the early-dismissal amber */
.w7dots .bday,.w7key .bday{background:var(--good)}
/* the key to every colour, under the strip — lost with option C, back by his word */
.w7key{display:flex;flex-wrap:wrap;gap:4px 12px;margin:12px 2px 0;font-size:11.5px;color:var(--ink-3)}
.w7key span{white-space:nowrap}
.w7key i{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:5px;vertical-align:0}
.w7t.now .w7dots i{box-shadow:0 0 0 1.5px #fff}
.w7det{margin:0;display:grid;gap:10px}
.w7big{border-radius:12px;background:var(--bg);border:1px solid var(--line);padding:12px 14px}
.w7big .w7day{margin:0;font-size:17px;font-weight:800;line-height:1.3}
/* The English day on its own line under "Today · ‹Hebrew date›" — his, 25 September 2026 */
.w7big .w7en{display:block;font-weight:700;color:var(--ink-2)}
.w7big .w7heb{font-family:'FrankRuhl','Assistant',serif;font-weight:500;color:var(--ink-3);white-space:nowrap}
/* The Hebrew date as the Hayom Yom link (week.js, his alone): the same date,
   in the brand's colour and underlined, so it reads as something to tap. */
.w7big a.w7hy{color:var(--brand);text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:4px}
.w7big p{margin:6px 0 0;font-size:14px;color:var(--ink-2)}
.w7chip{display:inline-block;font-size:12.5px;font-weight:800;border-radius:7px;padding:3px 9px}
.w7chip.off{background:var(--crit-soft);color:var(--crit-ink)}
.w7chip.early{background:#fff1cf;color:#7a5200}
.w7time{display:flex;align-items:center;gap:6px;font-weight:700;color:var(--ink)}
.w7cn{width:15px;height:15px;flex:none}
/* Whose times these are — 2 October 2026 (week.js, placeLine). */
.w7here{margin:2px 0 0 21px;font-size:12.5px;color:var(--ink-3)}
.w7here button{background:none;border:0;padding:0;font:inherit;font-weight:600;color:var(--brand);cursor:pointer}
.w7bd{border-radius:12px;background:#fff8e8;border:1px solid #f0dfb2;padding:12px 14px}
.w7bdl{font-size:11px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:#b0791b}
.w7bdlist{display:flex;flex-wrap:wrap;gap:12px 18px;margin-top:9px}
.w7boy{display:flex;align-items:center;gap:10px;min-width:0}
.w7boy b{display:block;font-size:15px}
.w7boy i{display:block;font-style:normal;font-size:12.5px;color:var(--ink-3)}
/* a computer: two weeks of dates at a glance, and the day beside its birthdays */
@media (min-width: 900px){
  .w7strip{grid-auto-columns:calc((100% - 78px) / 14)}   /* thirteen 6px gaps */
  .w7det{grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);align-items:start}
  .w7det > :only-child{grid-column:1 / -1}
}

/* ---- a provider's front door — 23 September 2026, providerhome.js --------
   New names only. The big card is the rebbi's `.rnow` in shape: brand teal,
   white type, the one thing on the phone to look at first. */
.pvbig{background:var(--brand);color:#fff;border-radius:14px;padding:15px 17px;margin:0 0 13px}
.pvbig.out{background:var(--crit-soft);color:var(--crit-ink);border:1px solid var(--crit-line)}
.pvbig.done{background:var(--panel);color:var(--ink-2);border:1px solid var(--line)}
.pvlab{margin:0 0 8px;font-size:11.5px;font-weight:700;letter-spacing:.09em;
  text-transform:uppercase;opacity:.8}
.pvwhat{margin:0;font-size:16px}
.pvbigwho{display:flex;flex-direction:column;gap:6px}
.pvbig .pvwho b{font-size:20px}
.pvbig .pvwho em{font-style:normal;font-size:14.5px;opacity:.85}
.pvwarn{margin:10px 0 0;font-size:14px;font-weight:600}
.pvwho{display:inline-flex;flex-wrap:wrap;align-items:baseline;gap:4px 7px}
.pvwho em{font-style:normal;color:var(--ink-3);font-size:13px}
.pvwho.gone b{text-decoration:line-through;opacity:.7}
.pvchip{display:inline-block;font-size:11px;font-weight:800;letter-spacing:.04em;
  text-transform:uppercase;padding:3px 8px;border-radius:999px;white-space:nowrap}
.pvchip.bad{background:var(--crit-soft);color:var(--crit-ink)}
.pvchip.warn{background:var(--warn-soft);color:var(--warn-ink)}
.pvchip.ok{background:var(--good-soft);color:var(--good-ink)}
.pvbig .pvchip.bad{background:#fff}
.pvday,.pvneeds,.pvboys{background:var(--panel);border:1px solid var(--line);
  border-radius:14px;padding:13px 15px;margin:0 0 13px}
.pvday h2,.pvneeds h2,.pvboys h2{margin:0 0 8px;font-size:15px}
.pvlist{list-style:none;margin:0;padding:0}
.pvlist li{display:flex;gap:12px;padding:10px 0;border-top:1px solid var(--line-2)}
.pvlist li:first-child{border-top:0}
.pvlist li.past .pvt,.pvlist li.past .pvwho b{color:var(--ink-3)}
/* The calendar's day (dayof.js, 25 September 2026): a session the day's
   changed hours took away is faded and says "Not today"; the line above the
   list says why, in the Daily Schedule's amber; a day off says when school is back. */
.pvlist li.pvnot{opacity:.5}
.pvlist li.pvnot .pvchip{background:var(--line-2);color:var(--ink-2);align-self:flex-start}
.pvdaynote{margin:0 0 8px;padding:7px 10px;border-left:4px solid var(--warn);
  background:var(--warn-soft);border-radius:6px;font-size:13.5px;color:var(--ink-2)}
.pvresume{margin:4px 0 0;font-size:14px;color:var(--ink-3)}
.pvt{flex:none;width:52px;font-weight:700;font-variant-numeric:tabular-nums}
.pvt small{display:block;font-weight:400;font-size:11.5px;color:var(--ink-3)}
.pvm{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px}
.pvmarks{display:flex;gap:6px;margin-top:6px;flex-wrap:wrap}
.pvmk{font:600 13px/1 'Assistant',system-ui,sans-serif;min-height:36px;padding:0 12px;
  border-radius:9px;border:1px solid var(--line);background:var(--bg);color:var(--ink-2);cursor:pointer}
.pvmk.hint{border-color:var(--crit-line);color:var(--crit-ink)}
.pvmk.on{background:var(--brand);border-color:var(--brand);color:#fff}
.pvneeds{background:var(--warn-soft);border-color:var(--warn-line)}
.pvneeds ul{margin:0;padding:0 0 0 18px;color:var(--warn-ink);font-size:14px;line-height:1.6}
/* Every line is a link to what needs her (providerhome.js → needsYou): the
   same ink, underlined, with an arrow, so it reads as something to tap. */
.pvneeds li a{color:inherit;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:3px}
.pvneeds li{padding:2px 0}
/* A report round past its due date (providerhome.js → needsYou). */
.pvneeds li a.late{color:var(--crit-ink);font-weight:700}
.pvlinks{display:flex;gap:8px;margin:0 0 13px}
.pvlinks a{flex:1;text-align:center;padding:11px 6px;border-radius:10px;border:1px solid var(--line);
  background:var(--panel);color:var(--brand);font-weight:700;font-size:14px;text-decoration:none}
.pvmk[disabled]{opacity:.55}
.pvsaid{margin:0 0 13px;padding:10px 13px;border-radius:10px;font-size:13.5px;
  background:var(--crit-soft);color:var(--crit-ink);border:1px solid var(--crit-line)}

/* HER NINE O'CLOCK BOYS' ATTENDANCE — 23 September 2026. The services blue,
   because on the rebbi's sheet the same boy wears the same blue; done goes
   plain. Here / Absent / Late reuse `.pvmk` so her two kinds of marking look
   like one thing. */
.pvatt{background:var(--data-soft);border:1px solid var(--data-line);
  border-radius:14px;padding:13px 15px;margin:0 0 13px}
.pvatt.done{background:var(--panel);border-color:var(--line)}
.pvatt h2{margin:0 0 4px;font-size:15px}
.pvatt h2 small{font-weight:600;color:var(--data-ink);margin-left:6px}
.pvatt.done h2 small{color:var(--ink-3)}
.pvattwhy{margin:0 0 8px;font-size:13px;color:var(--ink-2);line-height:1.4}
.pvatt ul{list-style:none;margin:0;padding:0}
.pvatt li{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:6px 12px;
  padding:9px 0;border-top:1px solid var(--data-line)}
.pvatt li:first-child{border-top:0}
.pvatt .pvmarks{margin-top:0}

/* ⚠ THE PROVIDER'S DOOR ON A COMPUTER — 24 September 2026, his "we should
   also build a computer view". providerhome.js wraps the cards in two columns;
   on a phone the wrappers vanish (`display: contents`) and `order` keeps the
   cards in their phone order: now, attendance, needs you, the day, links. */
.pvcols{display:flex;flex-direction:column}
.pvmain,.pvside{display:contents}
.pvbig{order:1} .pvatt{order:2} .pvneeds{order:3} .pvday{order:4} .pvsaid{order:5} .pvlinks{order:6}
/* TWO JOBS (roles.js → TWO_JOBS): the caseload card stands where the session
   now would, first; the buses come last, after her own links. */
.pvday.pvcase{order:1} .pvday.pvbus{order:7}
@media (min-width: 900px){
  #p-home{grid-column:1 / -1}
  .pvcols{display:grid;grid-template-columns:minmax(0,1.45fr) minmax(0,1fr);gap:16px;align-items:start}
  .pvmain,.pvside{display:flex;flex-direction:column}
  .pvcols .pvbig,.pvcols .pvday,.pvcols .pvatt,.pvcols .pvneeds,.pvcols .pvsaid,.pvcols .pvlinks{margin:0 0 16px}
  .pvbig{padding:20px 22px}
  .pvbig .pvwho b{font-size:24px}
  .pvlist li{padding:12px 0}
  .pvlinks{flex-wrap:wrap}
}

/* START, END, A NOTE, A REASON — 23 September 2026, providerhome.js. Green
   starts, red ends; the reasons are chips; every session has a note. */
.pvmk.go{background:var(--good-deep);border-color:var(--good-deep);color:#fff;font-weight:800}
.pvmk.stop{background:var(--crit-ink);border-color:var(--crit-ink);color:#fff;font-weight:800}
.pvmk.link{min-height:0;padding:0;border:0;background:none;color:var(--brand);line-height:inherit;
  font-weight:600;font-size:12.5px;text-decoration:underline;text-underline-offset:3px}
/* A link on its own line sits at the left, under the names, not centred. */
.pvm > .pvmk.link{align-self:flex-start;margin-top:4px}
.pvrun{display:flex;align-items:center;gap:8px;font-weight:700;color:var(--good-ink);font-size:14px}
.pvdot{width:9px;height:9px;border-radius:50%;background:#2e9b5b;
  box-shadow:0 0 0 4px rgba(46,155,91,.18);flex:none}
.pvtimes{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 8px;font-size:13.5px;color:var(--ink-2)}
.pvtimes b{color:var(--ink);font-variant-numeric:tabular-nums;white-space:nowrap}
.pvtold{font-size:12.5px;font-weight:700;color:var(--good-ink)}
.pvquiet{font-size:13px;color:var(--ink-3)}
.pvtold.bad{color:var(--crit-ink)}
.pvnote{width:100%;box-sizing:border-box;margin-top:6px;border:1px solid var(--line);border-radius:9px;
  padding:8px 10px;font:14px/1.35 'Assistant',system-ui,sans-serif;color:var(--ink);background:#fff;
  min-height:54px;resize:vertical}
/* HOW WAS HE — 16 October 2026 (providerhome.js → howBox): a box a boy under a
   Held session, two rows of three, one tap each. */
.pvhow{display:block;margin-top:8px;padding:10px 12px;background:var(--bg);border-radius:12px}
.pvhow > b{display:block;font-size:14.5px;margin-bottom:4px}
.pvhowrow{display:flex;flex-wrap:wrap;align-items:center;gap:4px 8px;margin:4px 0}
.pvhowrow > span:first-child{font-size:13px;color:var(--ink-2);min-width:74px}
.pvhowopts{display:flex;flex-wrap:wrap;gap:6px}
.pvhowopts button{border:1.5px solid var(--line);background:var(--panel);border-radius:999px;padding:6px 10px;cursor:pointer;
  font:700 12.5px 'Assistant',system-ui,sans-serif;color:var(--ink-2);min-height:34px}
.pvhowopts .on.g{background:var(--good-soft);border-color:var(--good-ink);color:var(--good-ink)}
.pvhowopts .on.m{background:var(--brand-soft);border-color:var(--brand);color:var(--brand)}
.pvhowopts .on.h{background:var(--warn-soft);border-color:var(--warn-ink);color:var(--warn-ink)}
.pvreq{font-size:12px;font-weight:800;letter-spacing:.03em;text-transform:uppercase;color:var(--crit-ink)}
.pvreasons{display:flex;flex-wrap:wrap;gap:6px;margin-top:4px}
.pvrsn{border:1px solid var(--line);border-radius:999px;padding:7px 11px;font:13px 'Assistant',system-ui,sans-serif;
  background:#fff;color:var(--ink);cursor:pointer}
.pvrsn.on{background:var(--brand);border-color:var(--brand);color:#fff;font-weight:700}
.pvbigact{display:flex;align-items:center;gap:10px;margin:10px 0 0;font-size:13.5px}
.pvbigact .pvmk{min-height:44px;font-size:15px;padding:0 18px}
/* Held at another time — 13 October 2026. When and how long, in one box. */
.pvother{display:grid;gap:8px;margin-top:6px;padding:12px;border-radius:10px;background:var(--warn-soft);border:1px solid var(--warn-line)}
.pvask{font-size:14px;font-weight:700;color:var(--ink)}
.pvattime{width:max-content;border:1px solid var(--line);border-radius:9px;padding:7px 10px;
  font:600 17px 'Assistant',system-ui,sans-serif;color:var(--ink);background:#fff}
.pvlens{display:flex;flex-wrap:wrap;gap:6px}
.pvlen{min-width:64px;min-height:38px;border:1px solid var(--line);border-radius:9px;background:#fff;color:var(--ink);
  font:600 14px 'Assistant',system-ui,sans-serif;cursor:pointer}
.pvlen.on{background:var(--brand);border-color:var(--brand);color:#fff}
.pvat{font-size:13.5px;color:var(--ink-2)}
.pvat b{font-variant-numeric:tabular-nums}

/* ===========================================================================
   CUSTOMIZE MY HOME — 24 September 2026, homecustom.js. The shortcuts row
   under the greeting, the edit mode (a bar above each card, the hidden cards at
   the foot, Done at the bottom of the screen), and the layout for a person who
   has customized. ⚠ `.hcon` is ONLY on the body of somebody who has saved a
   layout (or is editing one) — nobody else gets a single rule below it.
   =========================================================================== */
.hcbar{display:flex;flex-direction:column;gap:8px;margin:0 0 12px}
.hcrow{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.hcscs{display:flex;flex-wrap:wrap;gap:8px;flex:1;min-width:0}
.hcscs:empty{display:none}
.hcsc{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--line);background:var(--panel);
  color:var(--ink);border-radius:12px;padding:9px 13px;font:700 14px/1.2 'Assistant',system-ui,sans-serif;
  text-decoration:none;cursor:pointer}
.hcsc:hover{border-color:var(--brand)}
.hcsc .x{width:20px;height:20px;border-radius:50%;background:var(--ink-3);color:var(--panel);
  font-size:11px;line-height:20px;text-align:center}
.hcediting .hcsc{border-style:dashed;border-color:var(--brand)}
.hcsc.hcadd{color:var(--brand);background:transparent}
/* Customize: a quiet link after the last card, not a pill under the greeting */
.hcfoot{display:flex;justify-content:center;margin:6px 0 10px;order:9999;grid-column:1 / -1}
.hccust{border:0;background:transparent;color:var(--ink-3);border-radius:9px;min-height:36px;padding:6px 12px;
  font:700 13px 'Assistant',system-ui,sans-serif;cursor:pointer}
.hccust:hover{color:var(--brand);text-decoration:underline}
.hchead{margin:0;padding:8px 12px;border-radius:10px;background:var(--brand-soft);color:var(--brand);
  font-size:13.5px;font-weight:700}
.hcoffer{display:flex;flex-wrap:wrap;gap:6px;padding:10px;border-radius:12px;background:var(--panel);
  border:1px solid var(--line)}
.hcoffer p{width:100%;margin:0 0 2px;font-size:12.5px;color:var(--ink-3)}
.hcoffer button{border:1px solid var(--line);background:var(--bg);color:var(--ink);border-radius:10px;
  padding:6px 10px;font:700 13.5px 'Assistant',system-ui,sans-serif;cursor:pointer}
.hcoffer button:disabled{opacity:.4;cursor:default}

/* the bar above each card while editing */
.hcctl{display:flex;align-items:center;gap:6px;margin:0 0 6px;padding:8px 10px;border-radius:12px;
  background:var(--panel);border:2px dashed var(--brand)}
.hcctl .why{flex:1;min-width:0;font-size:12.5px;color:var(--ink-3);line-height:1.25}
.hcctl .why b{display:block;color:var(--ink);font-size:14px}
.hcctl button{flex:none;height:36px;min-width:36px;padding:0 10px;border-radius:9px;border:1px solid var(--line);
  background:var(--panel);color:var(--ink);font:800 14px 'Assistant',system-ui,sans-serif;cursor:pointer}
.hcctl button:disabled{opacity:.3;cursor:default}
.hcctl .hide{color:var(--crit-ink)}
.hcctl .lock{font-size:11.5px;font-weight:700;color:var(--ink-3);max-width:74px;line-height:1.2;text-align:right}
/* the cards themselves are looked at, not used, while he arranges them */
.hcediting main.wrap [data-card]{pointer-events:none;opacity:.92}
.hchidden{display:flex;flex-direction:column;gap:6px;padding:10px 12px;border-radius:12px;background:var(--panel);
  border:1px solid var(--line);order:9990;margin:0 0 12px}
.hchidden h3{margin:0;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3)}
.hchidden div{display:flex;align-items:center;justify-content:space-between;gap:8px;font-weight:700;font-size:14.5px}
.hchidden button{border:1px solid var(--brand);color:var(--brand);background:transparent;border-radius:9px;
  padding:5px 12px;font:800 13px 'Assistant',system-ui,sans-serif;cursor:pointer}
.hceditbar{position:fixed;left:0;right:0;bottom:0;z-index:60;display:flex;gap:8px;justify-content:center;
  padding:10px 16px calc(10px + env(safe-area-inset-bottom,0px));background:var(--panel);
  border-top:1px solid var(--line);box-shadow:0 -6px 18px rgba(0,0,0,.08)}
.hceditbar button{flex:1;max-width:260px;border-radius:11px;padding:12px;font:800 15px 'Assistant',system-ui,sans-serif;
  border:1px solid var(--line);background:var(--panel);color:var(--ink-2);cursor:pointer}
.hceditbar .done{background:var(--brand);border-color:var(--brand);color:#fff}
.hcediting main.wrap{padding-bottom:110px}
.hctoast{position:fixed;left:50%;bottom:86px;transform:translate(-50%,8px);z-index:70;max-width:min(92vw,460px);
  padding:11px 16px;border-radius:12px;background:var(--ink);color:var(--panel);font-size:14px;font-weight:600;
  opacity:0;pointer-events:none;transition:opacity .2s,transform .2s}
.hctoast.on{opacity:1;transform:translate(-50%,0)}
.hctoast.bad{background:var(--crit-ink)}

/* ⚠ HIS LAYOUT. Order only works among siblings of a flex or grid box, so:
   on a phone the page becomes ONE column and the doors' own wrappers step out
   of the way (`display: contents`), which makes every card a sibling and lets
   him put any card anywhere. On a computer the doors keep their shape — the
   provider's two columns, the home grid — and his order applies inside each
   door and to the doors themselves. Anything not named in his order (the
   greeting, a notice a door adds) keeps its place at the top or the foot. */
.hcon main.wrap>#a-links{order:-3}
.hcon main.wrap>#t-greet{order:-2}
.hcon main.wrap>#hc-bar{order:-1}
.hcon main.wrap [data-card]{order:500}
.hcon main.wrap .rsoft,.hcon main.wrap #r-bar,.hcon main.wrap .pvsaid{order:9995}
@media (max-width: 899px){
  .hcon main.wrap{display:flex;flex-direction:column}
  .hcon #t-desk,.hcon #t-desk .dcol,
  .hcon #r-home,.hcon #r-home .rcola,.hcon #r-home .rcolb,
  .hcon #p-home,.hcon #p-home .pvcols,.hcon #p-home .pvmain,.hcon #p-home .pvside{display:contents}
}
@media (min-width: 900px){
  /* (#r-home's own two columns, below, are flex columns — his order applies
     inside each, as it does in the provider's two.) */
  .hcbar,.hcon main.wrap>.hcctl,.hchidden{grid-column:1 / -1}
}

/* ===========================================================================
   A REBBI'S DOOR ON A COMPUTER — 24 September 2026. His, previewing a rebbi:
   "his Today tab on my computer was just one long situation. It didn't look
   like it was for a computer. It needs to have a computer view as well."
   It took ONE cell of the home grid — half the page, one long column, the
   other half empty. Now it takes the width in two columns: his DAY on the
   left (back in class, the period now, the day's periods), his CLASSES on the
   right (the tiles and the list he marks, which is the wide thing). And the
   page grows with a big screen, the way Tests and Sent to me do, but only
   while a rebbi's door is showing — the desk and the Dean's door are
   unchanged. A phone is unchanged: the two wrappers are ordinary blocks.
   =========================================================================== */
@media (min-width: 900px){
  main.wrap:has(> #r-home:not([hidden])){max-width:clamp(980px,86vw,1560px)}
  #r-home{grid-column:1 / -1;display:grid;grid-template-columns:minmax(300px,400px) minmax(0,1fr);
    gap:18px;align-items:start}
  #r-home .rcola,#r-home .rcolb{display:flex;flex-direction:column;min-width:0}
  #r-home .rcola:empty{display:none}
  #r-home:has(> .rcola:empty){grid-template-columns:minmax(0,1fr)}
}
@media (prefers-reduced-motion: reduce){ .hctoast{transition:none} }

/* A tap on the Tests or Results grid scrolls to what it opened (bringUp,
   csgrid.js). On a phone the green top bar is sticky, 56px, and a scroll to
   "start" parked the class name and the tabs underneath it — found in the
   photo of the fix, 24 September 2026. Stop short of the bar. */
@media (max-width: 899px){
  #t-bar,#s-grid{scroll-margin-top:calc(64px + env(safe-area-inset-top))}
}

/* ---- Out today, on the office's desk — 6 October 2026 ---------------------
   His "a notification about teachers out and who the subs are". The sub in
   the schedule-change purple, the same as the strip on Mark attendance; a
   class with nobody yet in the critical red, said in words as well. */
.dolist{list-style:none;margin:0;padding:0}
.dolist li{display:grid;grid-template-columns:1fr auto;gap:3px 10px;padding:9px 0;
  border-top:1px solid var(--line-2)}
.dolist li:first-child{border-top:0;padding-top:0}
.dolist li:last-child{padding-bottom:0}
.dowho{min-width:0}
.dowho b{font-weight:700}
.docls{color:var(--ink-3);font-size:12.5px}
.dowhen{color:var(--ink-3);font-size:12.5px;text-align:right;white-space:nowrap}
.dosub{grid-column:1/-1;font-size:13px;color:#4b2d75}
.dosub b{font-weight:700}
.dosub.none{color:var(--crit-ink)}
.dotag{display:inline-block;font-size:9.5px;font-weight:700;text-transform:uppercase;
  letter-spacing:.6px;background:#7b4fb3;color:#fff;border-radius:4px;padding:2px 6px;
  margin-right:6px;vertical-align:1px}
.dotag.none{background:var(--crit-soft);color:var(--crit-ink);border:1px solid var(--crit-line)}

/* THE ITEM THE SEARCH OPENED — 7 October 2026, finditem.js. His: "every result
   should open its exact item". One ring for every screen, so found looks the
   same wherever the menu's search sends somebody. Only appended; nothing else
   names `.found`. */
.found{outline:3px solid var(--brand);outline-offset:3px;border-radius:10px;
  box-shadow:0 0 0 6px var(--brand-soft)}
@media print{ .found{outline:0;box-shadow:none} }
/* A table row draws an outline as two hairlines, so a found row in the
   marking grid ("Mark him", 14 October 2026) is tinted instead. */
#m-grid tr.found{outline:0;box-shadow:none}
#m-grid tr.found > th, #m-grid tr.found > td{background:var(--brand-soft)}
#m-grid tr.found > :first-child{box-shadow:inset 4px 0 0 var(--brand)}

/* ---- came late, on a rebbi's front door — 7 October 2026 ----
   The conduct list's late box, ported into this screen's own dialog
   (rebbihome.js → askLateHere). Only appended; every name is new, and the
   `.rrecfld` row it sits in is the recess box's. */
.rlatebox .rrecfld{flex-direction:column;align-items:stretch;gap:4px;
  font:600 11px 'Assistant',sans-serif;letter-spacing:.7px;text-transform:uppercase;color:var(--ink-3)}
.rlatebox .rrecfld select,.rlatebox .rrecfld input{width:100%;box-sizing:border-box;
  font:600 15px 'Assistant',sans-serif;text-transform:none;letter-spacing:0;color:var(--ink);
  background:var(--panel);border:1px solid var(--line);border-radius:9px;padding:10px 11px;min-height:46px}
.rlatesum{margin:4px 0 14px;font-size:14.5px;line-height:1.7;color:var(--ink-2)}
.rlatesum b{color:var(--ink)}
.rlatesum .big{display:block;margin-top:6px;font:800 24px 'Assistant',sans-serif;
  color:var(--warn-ink);letter-spacing:-.4px}
.rlatebox .tbtn.go{background:var(--brand);border-color:var(--brand);color:#fff}
.rlatebox .tbtn.go[disabled]{opacity:.45;cursor:not-allowed}

/* ---- HIS SUNDAY OFF ON THE DAYS-AHEAD STRIP — 9 October 2026 (week.js) ------
   His: "the whole square should be highlighted". The whole tile blue, its own
   colour (teal is today, sand Shabbos, purple a class change); the open day
   says who covers and takes the request form's box, or shows what he left. */
.w7t.sunoff{background:var(--data-ink);border-color:var(--data-ink);color:#fff}
.w7t.sunoff .m,.w7t.sunoff .d{color:#dbe7f7}
.w7t.sunoff.open{outline-color:#fff}
.w7t.sunoff .w7dots i{box-shadow:0 0 0 1.5px #fff}
.w7key .sunoff{background:var(--data-ink);border-radius:3px}
.w7chip.sunoff{background:var(--data-soft);color:var(--data-ink);border:1px solid var(--data-line)}
.w7sxcov{margin:10px 0 0;padding:10px 12px;border-radius:10px;background:var(--data-soft);font-size:15px}
.w7sxcov b{display:block}
.w7sxcov small{display:block;font-size:13px;color:var(--ink-3)}
.w7big .w7sxlab{display:block;margin:12px 0 6px;font-size:14px;font-weight:700;color:var(--ink)}
.w7sxlab span{font-weight:400;color:var(--ink-3)}
.w7sxbox{width:100%;box-sizing:border-box;min-height:88px;font:inherit;font-size:15px;
  padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--panel);color:var(--ink)}
.w7big .w7sxq{margin:0;padding:10px 12px;border-left:4px solid var(--data-ink);background:var(--panel);
  border-radius:6px;font-size:15px;line-height:1.45;color:var(--ink);white-space:pre-line;overflow-wrap:anywhere}
.w7big .w7sxwhen{margin:4px 0 0;font-size:13px;color:var(--ink-3)}
.w7big .w7sxsay{margin:6px 0 0;font-size:13.5px;color:var(--crit-ink)}
.w7sxacts{display:flex;gap:8px;margin-top:10px}
.w7sxacts button{flex:1;padding:10px;border-radius:10px;font:inherit;font-weight:700;font-size:15px;
  border:1px solid var(--line);background:var(--panel);color:var(--ink);cursor:pointer}
.w7sxacts button.go{background:var(--brand);border-color:var(--brand);color:#fff}
.w7sxacts button[disabled]{opacity:.6;cursor:wait}

/* What the men he covers for left him — the "You are covering" card (covercard.js). */
.covering .cvnotes{margin:10px 0 0;display:grid;gap:8px}
.covering .cvnote{padding:10px 12px;border-left:4px solid var(--data-ink);background:var(--bg);border-radius:6px}
.covering .cvnote b{display:block;font-size:13px;color:var(--ink-2);margin-bottom:2px}
.covering .cvnote p{margin:0;font-size:14.5px;line-height:1.45;white-space:pre-line;overflow-wrap:anywhere}

/* TESTS TO CATCH UP — 14 October 2026, the rebbi's home page (rebbihome.js
   owedCard). Retests, late ones red; boys who did not take a test. */
.rowed{background:var(--panel);border:1px solid var(--line);border-radius:13px;
  padding:14px 15px;margin:0 0 14px;box-shadow:0 1px 2px rgba(23,26,33,.04)}
.rowed h2{margin:0 0 4px;font-size:17px;font-weight:700;letter-spacing:-.2px}
.rolab{display:flex;align-items:center;gap:8px;margin:12px 0 4px;font-size:11.5px;font-weight:800;
  letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3)}
.rolab b{display:inline-grid;place-items:center;min-width:20px;height:20px;padding:0 6px;border-radius:999px;
  background:var(--ink);color:#fff;font-size:11px}
.rotest{border:1px solid var(--line);border-radius:12px;padding:10px 12px;margin-top:8px}
.rotest.late{border-color:var(--crit-line);background:var(--crit-soft)}
.rotop{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
.rotop b{font-size:15px}
.rowhen{margin-left:auto;font-size:12.5px;font-weight:700;color:var(--brand)}
.rotest.late .rowhen{color:var(--crit-ink)}
.rosub{font-size:12.5px;color:var(--ink-3)}
.roboys{list-style:none;margin:8px 0 0;padding:0;display:grid;gap:4px}
.roboys li{font-size:14px}
.roboys li span{color:var(--ink-3);font-size:12.5px;margin-left:4px}
.rogo{display:inline-block;margin-top:9px;font-size:13.5px;font-weight:700;color:var(--brand)}
.romiss{display:flex;align-items:center;gap:8px;padding:9px 0;border-top:1px solid var(--line-2)}
.rolab + .romiss{border-top:0}
.romiss > div{flex:1;min-width:0}
.romiss b{display:block;font-size:14.5px}
.romiss small{display:block;color:var(--ink-3);font-size:12.5px}
.romiss i{font-style:normal;color:var(--ink-2)}
.romark{border:1px solid var(--brand);color:var(--brand);border-radius:9px;padding:6px 10px;font-size:12.5px;
  font-weight:700;white-space:nowrap;text-decoration:none}
.ronone{border:1px solid var(--line);background:var(--panel);color:var(--ink-2);border-radius:9px;padding:6px 10px;
  font:600 12.5px 'Assistant',sans-serif;white-space:nowrap;cursor:pointer}

/* ---- A NEW TALMID IS JOINING — arrivals.js, 16 October 2026 ----------------
   The notice at the top of every screen, until each person presses Seen it.
   Appended rules only: nothing else names these classes. */
.arrvbox{display:flex;flex-direction:column;gap:8px;margin:10px 16px 4px}
.arrv{display:flex;align-items:center;gap:12px;background:var(--warn-soft);border:1px solid var(--warn-line);
  border-radius:14px;padding:12px}
.arrv .avatar{flex:none;box-shadow:0 0 0 3px var(--panel)}
.arrvt{min-width:0;flex:1}
.arrvt i{display:block;font:800 11px 'Assistant',sans-serif;letter-spacing:.08em;text-transform:uppercase;
  color:var(--warn-ink);font-style:normal}
.arrvt b{display:block;font-size:16px;margin-top:2px;color:var(--ink);overflow-wrap:anywhere}
.arrvt span{display:block;font-size:13px;color:var(--ink-2);margin-top:2px}
.arrvt em{display:block;font-style:normal;font-weight:800;color:var(--ink);margin-top:4px;font-size:14px}
.arrvok{flex:none;background:var(--brand);color:#fff;border:0;border-radius:10px;padding:9px 11px;
  font:800 13px 'Assistant',sans-serif;cursor:pointer;line-height:1.15}
.arrvok:disabled{opacity:.6;cursor:default}

/* After 5:00 pm the front door is on the next school day (clock.js → shownDay),
   and today's tile on the days-ahead strip steps back — 18 October 2026. */
.w7t.past{opacity:.5}

/* His elective hour in "Your periods", slightly set apart — his, 17 October
   2026: "Elective should be slightly highlighted" (electivehours.js). */
/* A period of his class that another man takes — lighter, and "with" whom
   (rebbihome.js → myPeriods), 18 October 2026. Breaks are grey already. */
.rhr.other .rw b{font-weight:600;color:var(--ink-2)}
.rhr.other .rby{display:block;color:var(--ink-2);font-size:12.5px}
.rhr.elective{background:var(--sand-soft);border-radius:10px;padding-left:8px;padding-right:8px;
  margin:2px -8px;border-bottom-color:transparent}

/* TWO HATS — the admin hat's bar, 20 October 2026 (nav.js paintHatBar). The
   preview bar's register: a special state, nothing wrong. STICKY, like the
   preview bar — his, the same evening: Itchik "isn't familiar with the app",
   so the way back must never scroll away. The page's own bar gives up
   sticking for it, as it does for a preview. Under a preview it sticks just
   below the preview bar (nav.js sets its `top`), so the preview shows what
   he sees — his: "the whole point of my preview is to see the IDENTICAL
   thing the person sees". */
#hatbar{position:sticky;top:0;z-index:24;display:flex;align-items:center;gap:14px;flex-wrap:wrap;
  background:var(--brand-soft);border-bottom:1px solid var(--line);color:var(--brand);
  padding:9px 18px;padding-top:calc(9px + env(safe-area-inset-top));font-size:14px;font-weight:600}
#hatbar span{min-width:0}
#hatbar button{margin-left:auto;flex:none;background:var(--brand);border:0;color:#fff;
  border-radius:8px;padding:8px 15px;font:700 13.5px 'Assistant',sans-serif;cursor:pointer}
#hatbar button[disabled]{opacity:.6;cursor:default}
body.hatted .topbar, body.hatted .abar{position:static}

/* TO TALK TO — the rebbi's follow-ups on his front door, 20 October 2026
   (rebbihome.js fuCard). The Tests to catch up card's shape. */
.rfu{background:var(--panel);border:1px solid var(--line);border-radius:13px;
  padding:14px 15px;margin:0 0 14px;box-shadow:0 1px 2px rgba(23,26,33,.04)}
.rfu h2{margin:0 0 4px;font-size:17px;font-weight:700;letter-spacing:-.2px}
.rfurow{padding:10px 0;border-top:1px solid var(--line-2)}
.rfu h2 + .rfurow{border-top:0}
.rfurow b{display:block;font-size:15px}
.rfurow p{margin:1px 0 0;font-size:14px;color:var(--ink-2)}
.rfurow small{display:block;font-size:12.5px;color:var(--ink-3);margin-top:2px}
.rfubtns{display:flex;gap:8px;margin-top:8px;flex-wrap:wrap}
.rfubtns button{border-radius:9px;padding:7px 12px;font:700 13.5px var(--sans,system-ui);cursor:pointer;border:1.5px solid var(--brand)}
.rfugo{background:var(--brand);color:#fff}
.rfusnz{background:var(--panel);color:var(--brand)}
.rfubtns button[disabled]{opacity:.5;cursor:default}

/* REVIEW AND SEND — the one dialog before every email (reviewsend.js), 20
   October 2026, the mockup approved. New class names only. */
.rsback{position:fixed;inset:0;background:rgba(20,30,30,.45);z-index:900;display:flex;align-items:flex-start;
  justify-content:center;padding:28px 16px;overflow:auto}
.rsdlg{background:var(--panel);border-radius:16px;max-width:640px;width:100%;box-shadow:0 18px 50px rgba(0,0,0,.25);
  overflow:hidden;font-family:'Assistant',sans-serif;color:var(--ink)}
.rshd{padding:16px 20px 10px;border-bottom:1px solid var(--line)}
.rshd h2{margin:0;font-size:20px}
.rshd p{margin:4px 0 0;color:var(--ink-3);font-size:13.5px}
.rswho{padding:12px 20px;display:grid;gap:8px;border-bottom:1px solid var(--line)}
.rsline{display:grid;grid-template-columns:70px 1fr;gap:8px;font-size:14.5px;align-items:start}
.rsline > span:first-child{font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-3);padding-top:2px}
.rspeople{display:flex;flex-wrap:wrap;gap:6px}
.rspeople b{font-weight:700;background:var(--line-2);border-radius:999px;padding:3px 10px;font-size:13.5px}
.rspeople b small{font-weight:500;color:var(--ink-3);margin-left:4px}
.rsmore{border:0;background:none;color:var(--brand);font:700 13.5px 'Assistant',sans-serif;text-decoration:underline;padding:0;cursor:pointer}
.rsnote{font-size:13px;color:var(--ink-3);margin:0}
.rspick{display:flex;align-items:center;gap:8px;padding:10px 20px;background:var(--brand-soft);font-size:14px;flex-wrap:wrap}
.rspick select{font:600 14px 'Assistant',sans-serif;padding:5px 8px;border:1px solid var(--line);border-radius:8px;max-width:100%}
.rsmail{margin:14px 20px;border:1px solid var(--line);border-radius:12px;background:#fff;overflow:hidden}
.rsmh{padding:12px 16px;border-bottom:1px solid var(--line);font-size:13.5px;color:#444}
.rss{font-size:18px;font-weight:700;color:#1a1a1a;margin:0 0 6px}
.rsmb{padding:4px 16px 14px;font-family:Georgia,'Times New Roman',serif;font-size:15.5px;line-height:1.55;color:#1a1a1a;
  max-height:300px;overflow:auto}
.rsmb a{color:#1a5fb4}
.rslh{display:block;width:100%;max-width:544px;height:auto;margin:0 0 12px}
.rslhf{margin:16px 0 0}
.rsgo{display:flex;gap:10px;justify-content:flex-end;padding:12px 20px 16px;border-top:1px solid var(--line);flex-wrap:wrap}
.rsgo button{border-radius:10px;padding:9px 18px;font:700 15px 'Assistant',sans-serif;border:1px solid var(--brand);
  background:var(--brand);color:#fff;cursor:pointer}
.rsgo button.s{background:var(--panel);color:var(--brand)}

/* ---- MY TO-DO, FROM ANYWHERE — 21 October 2026 (todo.js) -------------------
   "+ To-do" in the phone's top bar and at the head of the menu; the quick form
   that slides up; the one-tap chip on a thing that arrived. Hanhala only. */
.topbar .tdbtn{margin-left:auto;flex:none;background:rgba(255,255,255,.16);border:1px solid rgba(255,255,255,.35);color:#fff;
  font:800 14px 'Assistant',sans-serif;padding:7px 12px;border-radius:999px;cursor:pointer}
.topbar .tdbtn + .heb{margin-left:10px}
.nvtodo{display:block;width:calc(100% - 16px);margin:6px 8px 10px;padding:9px 12px;border-radius:10px;border:1px solid rgba(255,255,255,.3);
  background:rgba(255,255,255,.12);color:#fff;font:800 14px 'Assistant',sans-serif;text-align:left;cursor:pointer}
.tdveil{position:fixed;inset:0;background:rgba(15,25,30,.42);z-index:900}
.tdsheet{position:fixed;left:0;right:0;bottom:0;z-index:901;background:var(--panel);border-radius:18px 18px 0 0;max-width:560px;margin:0 auto;
  padding:18px 18px calc(22px + env(safe-area-inset-bottom));box-shadow:0 -8px 30px rgba(0,0,0,.18);font-family:'Assistant',sans-serif}
.tdsheet h3{margin:0 0 4px;font:800 19px 'Assistant',sans-serif;color:var(--ink)}
.tdsheet .tdfrom{font-size:13px;color:var(--ink-3);margin:0 0 12px}
.tdsheet .tdboy{display:inline-flex;gap:6px;align-items:center;padding:4px 10px;border-radius:999px;background:var(--brand-soft);
  color:var(--brand);font:700 14px 'Assistant',sans-serif;margin:0 0 10px}
.tdsheet textarea{width:100%;box-sizing:border-box;min-height:74px;font:16px 'Assistant',sans-serif;padding:10px;
  border:1px solid var(--line);border-radius:10px;color:var(--ink);background:var(--panel)}
.tdsheet .tdwhen{display:flex;gap:6px;margin:10px 0 14px;flex-wrap:wrap;align-items:center}
.tdsheet .tdwhen button{font:700 14px 'Assistant',sans-serif;padding:8px 12px;border-radius:999px;border:1px solid var(--line);
  background:var(--panel);color:var(--ink-2);cursor:pointer}
.tdsheet .tdwhen button.on{border-color:var(--brand);color:var(--brand);background:var(--brand-soft)}
.tdsheet .tdwhen input{font:15px 'Assistant',sans-serif;padding:7px 9px;border:1px solid var(--line);border-radius:9px}
.tdsheet .tdsay{color:var(--crit-ink);font-size:13.5px;margin:0 0 10px}
.tdsheet .tdacts{display:flex;gap:8px}
.tdsheet .tdacts button{border-radius:10px;padding:10px 18px;font:700 15px 'Assistant',sans-serif;border:1px solid var(--line);
  background:var(--panel);color:var(--ink);cursor:pointer}
.tdsheet .tdacts .tdgo{background:var(--brand);border-color:var(--brand);color:#fff}
.tdchip{display:inline-flex;align-items:center;gap:6px;font:700 13px 'Assistant',sans-serif;padding:7px 12px;border-radius:999px;
  border:1px solid var(--brand);color:var(--brand);background:var(--panel);cursor:pointer}
.tdchip.ok{border-color:var(--good-line);background:var(--good-soft);color:var(--good-ink);cursor:default}

/* A nachas note for later, in every view that marks a boy (nachaslater.js) —
   22 October 2026. The box's words, shared; and the front door's own button,
   yellow like the list's and the room's. */
.nlhint{margin:0;font-size:13.5px;color:var(--ink-2);line-height:1.45}
.nltext{font:500 15px 'Assistant',sans-serif;padding:9px 11px;border:1px solid var(--line);border-radius:10px;
  background:var(--panel);color:var(--ink);width:100%;box-sizing:border-box;min-height:70px}
.nlbtns{display:flex;gap:8px;flex-wrap:wrap}
.rb.nach{background:linear-gradient(#ffe066,#ffc928);border-color:#e8a800;color:#3d2a00}
.rb.nach svg{flex:none}
.rnach{display:grid;gap:9px}

/* THE KRIAH CHECKER CARD on the front door (kriahcard.js), 25 October 2026. */
.krcard{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:14px 16px;margin:0 0 14px}
.krcard h3{margin:0 0 4px;font-size:16.5px;font-weight:700;color:var(--ink)}
.krcard p{margin:0 0 10px;color:var(--ink-2);font-size:14px;line-height:1.4}
.krcgrid{display:grid;grid-template-columns:1fr auto;gap:5px 12px;align-items:center;margin:0 0 12px;font-size:13.5px}
.krcdots{display:flex;gap:5px}
.krcdots span{width:13px;height:13px;border-radius:50%;border:1.5px solid var(--brand);box-sizing:border-box}
.krcdots span.on{background:var(--brand)}
.krcfoot{display:flex;align-items:center;justify-content:space-between;gap:10px}
.krccount{font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3)}
.krcbtn{background:var(--brand);color:#fff;border-radius:10px;padding:10px 18px;font-weight:700;font-size:15px;text-decoration:none}
