/* ====================================================================
   app.css — what the app needs ON TOP of mock.css, and nothing else.

   mock.css is the approved mock-up's stylesheet copied verbatim, and no rule
   in it is changed to suit the app. Everything below is one of three things,
   each said where it stands:

     1. A value the mock-up only substituted (the display face).
     2. A control the app must build without JavaScript, drawn to look like
        the scripted control the mock-up draws (radios for the Surah/Lesson
        switch, <details> for the menus that open).
     3. Something the mock-up has no screen for (the refusal message, the way
        out, the Parent's phone box).

   Every value is one of mock.css's own tokens.
   ==================================================================== */

/* ---- 1. The display face ---------------------------------------------
   The mock-up says of itself: "Marcellus is a web font, and this file must
   run offline from file://, so --font-display substitutes the nearest system
   serif." The app is online and loads Marcellus, which is the approved face
   (public/styles/components.css: "Marcellus is the display face. Do not
   substitute it."). The fallbacks after it are the mock-up's own stack. */
:root{
  --font-display: "Marcellus",ui-serif,"Iowan Old Style","Palatino Linotype",Palatino,
                  "Book Antiqua",Georgia,"Times New Roman",serif;
}

/* ---- 3. What the last write said ---------------------------------------
   The mock-up has no refusal. It is drawn like the mock-up's own .ok box,
   in the neutral colours of a card rather than the green of success. */
.alert{
  border:0;box-shadow:var(--edge-row);
  background:var(--card);border-radius:var(--r-sm);
  padding:var(--s6) var(--s7);margin:var(--s6) 0;
  font:400 var(--text-lg)/1.5 var(--font-ui);color:var(--ink);text-align:left;
}

/* ---- 3. The way out, as a button that reads as its surroundings -------
   In the menu it is one more .nav-item; under the Classes list it is a
   .linkish like the spreadsheet link above it. The form adds no box. */
.signout-form{display:contents}
#nav button.nav-item{
  appearance:none;border:0;background:none;cursor:pointer;width:100%;
  font:400 var(--text-md)/1.35 var(--font-ui);text-align:left;
}

/* Present to a screen reader, absent to the eye. */
.vis-hidden{
  position:absolute;width:1px;height:1px;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap;
}

/* A <details> that stands in for a scripted toggle keeps no marker. */
details > summary{list-style:none}
details > summary::-webkit-details-marker{display:none}
details > summary::marker{content:""}

/* ---- 2. A link standing where the mock-up draws a <button> ---------------
   "Show archived Classes" reloads the screen instead of running a script, so
   it is an <a>. A <button> is inline-block; so is this. */
a.linkish{display:inline-block}

/* ---- 2. Surah or Lesson, switched without a script ----------------------
   The mock-up's two .seg buttons carry aria-pressed and a script. These are
   two <label>s, each holding a radio, in the places the buttons stand, drawn
   with the mock-up's own .seg button and .pane-entry .seg button values. */
.seg label{
  flex:1;font:400 var(--text-lg)/1 var(--font-display);letter-spacing:var(--track-tight);
  cursor:pointer;border:0;border-radius:var(--r-sm);
  background:var(--card);box-shadow:var(--edge-row);color:var(--ink-quiet);
  display:flex;align-items:center;justify-content:center;
  text-transform:none;margin:0;position:relative;
}
.seg label:hover{background:var(--green-pale)}
.seg label:has(input:checked){
  background:var(--green-pale);color:var(--green);
  box-shadow:inset 0 0 0 1.4px var(--green-tick);
}
.seg label:has(input:focus-visible){outline:2px solid var(--green-tick);outline-offset:2px}
.seg label input{position:absolute;opacity:0;width:1px;height:1px;margin:0;pointer-events:none}
.pane-entry .seg label{
  box-sizing:border-box;height:var(--ctl);min-height:var(--ctl);
  padding:0 var(--s5);font-size:var(--text-lg);
}

/* ---- 3. Moving a Student into a Group ---------------------------------------
   The mock-up's select posts itself from a script. Here a Move button stands
   beside it, and the two sit on one line. */
.row .right form.pick{display:inline-flex;align-items:center;gap:var(--s3)}

/* ---- 3. The fourth Grade scale ------------------------------------------
   The mock-up offers three scales in a 440px row. The app also offers
   "Ungraded", and four names in that row wrap onto two lines, so a row of
   four is given the room four need. */
.scalecard .seg:has(> button:nth-child(4)){max-width:586px}
.scalecard .seg button{white-space:nowrap}

/* ---- 9. The Classes page fits the window ---------------------------------
   Owner's call, 2026-09-16, replacing the mock-up's "grow with the list".
   With all nine grades the mock-up's page grew taller than the window, and the
   rail's cards stretched into empty space to match it.

   Wherever the two panes sit side by side, whatever the window's height,
   the page is exactly the window's height:
   - the Classes list scrolls inside its own pane;
   - Today and The Week share the rail's height, growing in a tall window and
     shrinking in a short one, never below their own content.
   In a window too short for the rail, the rail scrolls inside its own pane
   too. In a narrower window the panes stack and the page scrolls as the
   mock-up does, each card only as tall as its content. */
body.page-classes .panes > .rail{align-self:start}
body.page-classes .rail .card{justify-content:flex-start}
body.page-classes .rail .card[data-fill]{flex:none;min-height:0}

@media (min-width:1100px){
  body.page-classes #shell{height:calc(100vh - var(--bh));min-height:0;box-sizing:border-box}
  body.page-classes .panes{grid-template-rows:minmax(0,1fr)}
  body.page-classes .panes > .pane{min-height:0}
  body.page-classes .panes > .rail{align-self:stretch;overflow-y:auto}
  body.page-classes .rail .card{justify-content:space-between}
  body.page-classes .rail .card[data-fill="day"]{flex:1.15 1 auto;min-height:min-content}
  body.page-classes .rail .card[data-fill="week"]{flex:1 1 auto;min-height:min-content}
  body.page-classes .groups{
    overflow-y:auto;overscroll-behavior:contain;scrollbar-width:none;
    --fadeT:0px; --fadeB:0px;
    transition:--fadeT .18s linear,--fadeB .18s linear;
    /* The Roster's dissolve, copied from its rule in mock.css. list-fade.js
       sets the two lengths. */
    -webkit-mask:linear-gradient(to bottom,
      rgba(0,0,0,0)    0,
      rgba(0,0,0,.156) calc(var(--fadeT) * .25),
      rgba(0,0,0,.5)   calc(var(--fadeT) * .5),
      rgba(0,0,0,.844) calc(var(--fadeT) * .75),
      #000             var(--fadeT),
      #000             calc(100% - var(--fadeB)),
      rgba(0,0,0,.844) calc(100% - var(--fadeB) * .75),
      rgba(0,0,0,.5)   calc(100% - var(--fadeB) * .5),
      rgba(0,0,0,.156) calc(100% - var(--fadeB) * .25),
      rgba(0,0,0,0)    100%);
            mask:linear-gradient(to bottom,
      rgba(0,0,0,0)    0,
      rgba(0,0,0,.156) calc(var(--fadeT) * .25),
      rgba(0,0,0,.5)   calc(var(--fadeT) * .5),
      rgba(0,0,0,.844) calc(var(--fadeT) * .75),
      #000             var(--fadeT),
      #000             calc(100% - var(--fadeB)),
      rgba(0,0,0,.844) calc(100% - var(--fadeB) * .75),
      rgba(0,0,0,.5)   calc(100% - var(--fadeB) * .5),
      rgba(0,0,0,.156) calc(100% - var(--fadeB) * .25),
      rgba(0,0,0,0)    100%);
  }
  body.page-classes .groups::-webkit-scrollbar{display:none}
}

/* ---- 10. The Classes list's section line ---------------------------------
   Its label and its count sit on one baseline. The mock-up centres them, and
   capitals of two different weights then sit at two different heights. */
.sect{align-items:baseline}

/* ---- 11. The Today figure reads as a number -------------------------------
   Marcellus draws 0 as a perfect ring, so "0 of 21 Students" read as an empty
   circle. The figure takes the mock-up's own serif, whose digits are digits.
   Its two lines of words end on the number's baseline instead of hanging
   below it. Owner's call, 2026-09-16. */
.figure .big{font-family:ui-serif,"Iowan Old Style","Palatino Linotype",Palatino,"Book Antiqua",Georgia,"Times New Roman",serif}
.figure{align-items:last baseline}

/* ---- 3. Grades 1–3 --------------------------------------------------------
   The mock-up has no pool: one Class holding three school grades. Beside a
   Student's name there, the grade is said quietly, in the ink of a meta line,
   so the name still reads first. */
.st-grade{color:var(--ink-soft);font-family:var(--font-ui);font-size:var(--text-sm);letter-spacing:0}

/* ---- 3. The phone's menu bar holds longer names ----------------------------
   The mock-up's bar holds one-word items on one line. Settings adds
   "Order of the day" and "Print reports", which one line
   cuts off at a phone's width, so a name may wrap onto a second line inside
   its tab — hyphenated where a word itself must break. */
@media (max-width:879px){
  #nav .nav-item{white-space:normal;text-overflow:clip;overflow-wrap:anywhere;hyphens:auto;-webkit-hyphens:auto;line-height:1.15}
}

/* ---- 12. Four grade scales fit a phone ------------------------------------
   With "Ungraded" added there are four buttons, and on a phone one row of
   four ran off the right edge and made the whole page scroll sideways. Under
   520px they sit two by two. */
@media (max-width:520px){
  .scalecard .seg:has(> button:nth-child(4)){display:grid;grid-template-columns:1fr 1fr;max-width:none}
  .scalecard .seg:has(> button:nth-child(4)) button{width:100%}
}

/* ---- 13. Let things stick ---------------------------------------------------
   mock.css stops sideways scrolling with overflow-x:hidden on <body>. That
   also turns <body> into a scroll box that never scrolls, and position:sticky
   then has nothing to stick to, so the Students search box scrolled away.
   clip stops sideways scrolling just the same without making a scroll box. */
html,body{overflow-x:clip}

/* ---- A day that is not today: the date turns gold, and "Back to today"
   joins the header's chips (src/views/layout.ts shownDay, backToToday). */
.pastday{font-weight:600;color:var(--gold)}
.btn.chip.back-today{background:var(--green);color:var(--card);box-shadow:var(--lift-green)}
.btn.chip.back-today:hover{background:var(--green-deep)}
.mast-right{display:flex;align-items:center;gap:var(--s4);flex-wrap:wrap;justify-content:flex-end}
/* The header's chips wrap inside the screen rather than running off its edge. */
.deskhead-right{flex:0 1 auto;min-width:0}
@media (max-width: 640px){ .deskhead-right{justify-content:flex-start} }
/* ---- Class Settings as a gear on the Desk header (src/views/group.ts GEAR):
   a round chip, a full tap target square, the gear centred in it. */
.btn.chip.chip-icon{justify-content:center;flex:none;padding:0;
  width:var(--tap-min);min-width:var(--tap-min);height:var(--tap-min);min-height:var(--tap-min);border-radius:50%}
.btn.chip.chip-icon svg{display:block;flex:none}
/* The Desk header's chips share one height, the count pill's (30px), so the
   row reads as one line. Each stays easy to press: an invisible edge takes
   the tap area back up to var(--tap-min). */
.deskhead-right{--chip-h:30px}
.deskhead-right > .btn.chip,
.deskhead-right > .cal > summary.btn.chip,
.deskhead-right > .deskcount{
  position:relative;box-sizing:border-box;height:var(--chip-h);min-height:0;padding-top:0;padding-bottom:0;
}
.deskhead-right > .btn.chip.chip-icon,
.deskhead-right > .cal > summary.btn.chip{justify-content:center;padding:0;border-radius:50%;
  width:var(--chip-h);min-width:var(--chip-h);height:var(--chip-h)}
/* Calendar is an icon in the Desk header and a word in the Week card. */
.cal-icon{display:block;flex:none}
.deskhead-right .cal-label{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.card .cal-icon{display:none}
.deskhead-right > .btn.chip::before,
.deskhead-right > .cal > summary.btn.chip::before{
  content:"";position:absolute;inset:calc((var(--chip-h) - var(--tap-min)) / 2) 0;
}
.deskhead-right > .btn.chip.chip-icon::before,
.deskhead-right > .cal > summary.btn.chip::before{inset:calc((var(--chip-h) - var(--tap-min)) / 2)}
/* ---- Excuse this Class (and its Undo) at the foot of the Roster pane, out of
   the header: a quiet secondary button the pane's full width. */
.excuse-class{margin:var(--s5) 0 0}
.excuse-class .btn{display:flex;align-items:center;justify-content:center;
  width:100%;min-height:var(--tap-min);box-sizing:border-box}

/* ---- The Calendar: a button that opens one month (src/views/calendar.ts). */
.cal{position:relative;display:inline-block}
.cal > summary{list-style:none;cursor:pointer}
.cal > summary::-webkit-details-marker{display:none}
/* In the Week card the Calendar sits under the ornament, the card's full width. */
.card .cal{display:block;margin:var(--s6) 0 0}
.card .cal > summary{display:flex;justify-content:center;width:100%;box-sizing:border-box}
/* In the Week card, Calendar and Excuse this Day (under it) are the same quiet button. */
/* Drawn slimmer than a fingertip; the invisible edge still reaches var(--tap-min). */
.card .cal > summary.btn.chip,
.card .excuse-day .btn{
  position:relative;min-height:0;height:calc(var(--tap-min) * .8);padding:0 var(--s8);
  border-radius:var(--r-lg);font:400 15px/1 var(--font-display);letter-spacing:var(--track-tight);
  background:transparent;color:var(--ink-quiet);box-shadow:inset 0 0 0 1px var(--grey-line);
}
.card .cal > summary.btn.chip::before,
.card .excuse-day .btn::before{content:"";position:absolute;inset:calc(var(--tap-min) * -.1) 0}
.card .cal > summary.btn.chip:hover,
.card .excuse-day .btn:hover{background:var(--card);color:var(--green)}
.card .excuse-day{margin:var(--s3) 0 0}
.card .cal-panel{right:0;width:auto}
/* The button sits at the foot of a screen that never scrolls, so the month opens upward, over the days. */
@media (min-width: 641px){ .card .cal-panel{top:auto;bottom:calc(100% + var(--s2))} }
.cal-panel{position:absolute;z-index:20;top:calc(100% + var(--s2));left:0;width:max-content;min-width:calc(var(--tap-min) * 5 + var(--s2) * 4 + var(--s5) * 2);
  padding:var(--s5);background:var(--card);border:1px solid var(--grey-line);border-radius:var(--r-md);box-shadow:var(--lift-panel)}
.deskhead-right .cal-panel{left:auto;right:0}
.cal-head{display:flex;align-items:center;justify-content:space-between;margin:0 0 var(--s4)}
.cal-month{font:400 var(--text-md)/1.2 var(--font-display);color:var(--green)}
.cal-arrow{display:inline-grid;place-items:center;min-width:var(--tap-min);min-height:var(--tap-min);
  font:400 var(--text-xl)/1 var(--font-ui);color:var(--green);text-decoration:none;border-radius:var(--r-sm)}
a.cal-arrow:hover{background:var(--green-pale)}
.cal-row{display:grid;grid-template-columns:repeat(5,var(--tap-min));gap:var(--s2);margin-top:var(--s2)}
.cal-names span{text-align:center;font:600 var(--text-xs)/1 var(--font-ui);color:var(--ink-quiet);letter-spacing:var(--track-wide)}
.cal-day{position:relative;display:grid;place-items:center;min-height:var(--tap-min);border-radius:var(--r-sm);
  font:400 var(--text-base)/1 var(--font-ui);color:var(--ink);text-decoration:none}
a.cal-day:hover{background:var(--green-pale)}
.cal-day[data-future]{color:var(--ink-quiet);opacity:.5}
.cal-day[data-today]{box-shadow:inset 0 0 0 1.5px var(--green)}
.cal-day[aria-current="date"]{background:var(--green);color:var(--card)}
.cal-dot{position:absolute;bottom:5px;left:50%;width:5px;height:5px;margin-left:-2.5px;border-radius:50%;background:var(--gold)}
.cal-day[aria-current="date"] .cal-dot{background:var(--card)}
/* A phone: the month sits over the screen, inside its edges, wherever the button is. */
@media (max-width: 640px){
  .cal-panel,.deskhead-right .cal-panel{position:fixed;top:calc(var(--s9) * 2);left:var(--s6);right:var(--s6);width:auto;min-width:0}
  .cal-row{grid-template-columns:repeat(5,1fr)}
}
.cal-foot{margin:var(--s4) 0 0;text-align:right;font:400 var(--text-sm)/1 var(--font-ui)}

/* ---- 16. The two Sessions of a Day (owner, 2026-09-24) ----------------------
   Each Session is a <fieldset> in the Desk's one form (src/views/session-row.ts).
   Its name and its tools sit at the top; its work sits under them, all on
   ONE grid (section 23b). The legend is floated so it lays out as an
   ordinary grid item rather than on the fieldset's border.
   The two Sessions stand side by side, Morning left, each a column of its own
   (owner, 2026-11-01); a phone stacks them. */
.session{
  border:0;margin:0;padding:0;min-width:0;
  display:grid;grid-template-columns:var(--cols);align-items:center;gap:var(--g);
}
.sessions{display:grid;grid-template-columns:minmax(0,1fr);row-gap:var(--s5)}
.session-col{display:flex;flex-direction:column;min-width:0}
.session-col > .session{flex:1 1 auto}
/* A hairline between the Sessions, the one under the Student's name. The owner
   chose it over a card, 2026-09-24, an exception to rule 1 for this line only. */
.session-col + .session-col{padding-top:var(--s5);border-top:1px solid var(--gold-hair)}
/* Wide enough for two columns, 540 px (the owner's laptop window, 1000 px, gives
   its entry panel about 580): the hairline stands upright between them. */
@container (min-width:540px){
  .sessions{grid-template-columns:repeat(2,minmax(0,1fr));row-gap:0}
  .session-col{padding:0 var(--s7)}
  .session-col:first-child{padding-left:0}
  .session-col:last-child{padding-right:0}
  .session-col + .session-col{padding-top:0;border-top:0;border-left:1px solid var(--gold-hair)}
  /* "Give this to everyone" keeps its row in both columns whether or not it shows
     (it shows once a box holds work, and only on a Session this Desk marks), so
     the boxes end on one line either way. */
  .session-work > .session-actions:not(:has(> .give-row:not([hidden]))){display:flex;min-height:var(--track-h)}
  /* The last box of each column reaches the column's foot, so the two end on one
     line (owner, 2026-11-01). The top stays put; the spare height sits inside
     the box, under "Add another". */
  .session-work > .pieces{align-self:stretch}
  .pieces > .piece[data-part="memorize"]{flex:1 1 auto}
}
/* A laptop window about 1000 px wide: the columns are narrower, so the gaps tighten. */
@container (min-width:540px) and (max-width:699px){
  .session-col{padding:0 var(--s5)}
  .session-col:first-child{padding-left:0}
  .session-col:last-child{padding-right:0}
}
.session-name{
  float:left;padding:0;margin:0;min-width:0;
  font:400 var(--text-xl)/var(--track-h) var(--font-display);color:var(--green);letter-spacing:var(--track-tight);
}
/* The Session's tools have no box of their own: each is placed on the
   Session's grid (section 23b). */
.session-tools{display:contents}
/* Clear and "Same as last time" are drawn only when of use (src/views/session-row.ts). */
.session-tools [hidden]{display:none}
/* The boxes take the column's spare height, so "Give this to everyone" stands
   level across the two columns; the boxes themselves keep to the top. */
.session-work{grid-column:1 / -1;min-width:0;display:grid;grid-template-rows:1fr auto;row-gap:var(--g)}
/* Only the half she chose is shown, in the Memorize box of this Session only.
   The mock-up's script does this with display:none on the other half; the
   radios do it. The Review box holds its own kind and follows no switch. */
.session:has(input[type="radio"][value="surah"]:checked) .piece[data-part="memorize"] [data-kind="lesson"],
.session:has(input[type="radio"][value="lesson"]:checked) .piece[data-part="memorize"] [data-kind="surah"]{display:none}
/* An absent Session has no work to show. */
.session[data-attendance="absent"] .session-work{display:none}
/* Absent: no work, so no Surah|Lesson either, in the header or the work row. */
.session[data-attendance="absent"] .session-kind{display:none}
/* Present to a screen reader, absent to the eye. */
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.session-notes{margin-top:var(--s8)}

/* ---- 17. A chip that posts (Excuse this Student) -----------------------------
   Most chips are links; this one changes something, so it is a form. The
   form adds no box of its own, and the chip sits in the row with the rest. */
.chip-form{display:contents}

/* "Excuse this Student" sits bare in .pane-entry, with no wrapper like
   .excuse-day to narrow it back down from .btn's width:100%
   (public/styles/components.css) and mock.css's .btn.chip 38px min-height. */
/* It sits at the right end of the Student's name line. */
.entry-head .chip-form .btn.chip{width:auto;flex:none;min-height:var(--tap-min)}

/* ---- 18. Excuse this Day, in the Week card ------------------------------------
   Under the five Days, the card's full width, like the Calendar button below
   the ornament. On an excused Day the words say so beside the Undo chip. */
.excuse-day{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--s3);margin:0 0 var(--s4)}
/* Drawn like Excuse this Class on the Desk: a quiet button the card's full width. */
.excuse-day .btn{display:flex;align-items:center;justify-content:center;
  width:100%;min-height:var(--tap-min);box-sizing:border-box}
.excused-note{margin:0;font:400 var(--text-md)/1.3 var(--font-ui);color:var(--gold)}

/* ---- 19. Find a student, in the Classes screen's head (src/views/groups.ts) --
   A round magnifier, the count pill's own white chip with its soft shadow,
   just left of the pill. Opened, the chip grows sideways into a pill-shaped
   search box, the magnifier kept at its left and a small × at its right; the
   matches float under it in a card of their own that scrolls, over the list.

   The fold is a <details>, so scripting off still opens it. The width grows
   from the icon's 48px by a keyframe on open; closing plays it backwards
   (.is-closing, set by public/list-fade.js until the animation ends). With
   reduced motion, nothing moves. On a phone the open box takes the whole
   head line, and the card the same width. */
.studentfind{--find-h:var(--tap-min);--find-w:300px;--find-ease:cubic-bezier(.2,.8,.2,1);
  position:relative;flex:none;display:flex;align-items:center;margin:0}
.studentfind > summary{list-style:none}
.studentfind > summary::-webkit-details-marker{display:none}
.studentfind-icon{
  display:flex;align-items:center;justify-content:center;box-sizing:border-box;
  width:var(--find-h);height:var(--find-h);border-radius:var(--r-full);cursor:pointer;
  background:var(--card);color:var(--green);box-shadow:var(--lift-quiet);
  transition:background-color .15s ease;
}
.studentfind-icon:hover{background:var(--green-pale)}
.studentfind-icon:focus-visible{outline:2px solid var(--green-tick);outline-offset:2px}
.studentfind[open] > .studentfind-icon{display:none}
.studentfind-icon svg,.studentfind-glass svg{display:block}

/* The fold's own content box (a real box in current browsers) is a flex item
   of the <details>; it must fill the details, or the box's 100% on a phone
   would be the width of its own content. */
.studentfind::details-content{flex:1 1 auto;min-width:0}
.studentfind-pop{position:relative;width:var(--find-w);max-width:100%}
.studentfind[open] .studentfind-pop{animation:find-grow .28s var(--find-ease) both}
.studentfind.is-closing .studentfind-pop{animation:find-shrink .28s var(--find-ease) both}
@keyframes find-grow{from{width:var(--find-h)}to{width:var(--find-w)}}
@keyframes find-shrink{from{width:var(--find-w)}to{width:var(--find-h)}}

.studentfind-form{position:relative;display:flex;align-items:center;margin:0;
  height:var(--find-h);border-radius:calc(var(--find-h) / 2);overflow:hidden;
  background:var(--card);box-shadow:var(--lift-quiet)}
.studentfind-form:focus-within{box-shadow:var(--lift-quiet),inset 0 0 0 2px var(--green-tick)}
.studentfind-glass{position:absolute;left:0;top:0;width:var(--find-h);height:var(--find-h);
  display:flex;align-items:center;justify-content:center;color:var(--green);pointer-events:none}
.studentfind input[type=search]{
  appearance:none;-webkit-appearance:none;box-sizing:border-box;flex:1 1 auto;min-width:0;width:100%;
  height:var(--find-h);margin:0;padding:0 var(--find-h) 0 var(--find-h);
  font:400 var(--text-lg)/1 var(--font-ui);color:var(--ink);
  border:0;border-radius:0;box-shadow:none;background:transparent;outline:none;
}
.studentfind input[type=search]::-webkit-search-cancel-button{display:none}
.studentfind input[type=search]::placeholder{color:var(--ink-soft)}
.studentfind[open] .studentfind-form > input,
.studentfind[open] .studentfind-close{animation:find-in .2s ease .1s both}
.studentfind.is-closing .studentfind-form > input,
.studentfind.is-closing .studentfind-close{animation:find-out .12s ease both}
@keyframes find-in{from{opacity:0;transform:translateX(-6px)}to{opacity:1;transform:none}}
@keyframes find-out{from{opacity:1}to{opacity:0}}
.studentfind-close{position:absolute;right:0;top:0;width:var(--find-h);height:var(--find-h);
  display:flex;align-items:center;justify-content:center;border-radius:var(--r-full);
  font:400 24px/1 var(--font-ui);color:var(--ink-soft);text-decoration:none}
.studentfind-close:hover{color:var(--green)}
.studentfind-close:focus-visible{outline:2px solid var(--green-tick);outline-offset:-4px}

/* The matches: a card floating under the box, the box's width, over the list. */
.found{position:absolute;z-index:40;left:0;right:0;top:calc(100% + var(--s3));
  max-height:min(60vh, 420px);overflow-y:auto;box-sizing:border-box;padding:var(--s3);
  border-radius:var(--r-md);background:var(--card);box-shadow:var(--lift-soft),var(--lift-quiet);
  text-align:left}
.studentfind[open] .found:not(:empty){animation:find-rise .2s ease both}
.studentfind.is-closing .found{display:none}
@keyframes find-rise{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.found:empty{display:none}
.found-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s2)}
.found-row{
  display:flex;align-items:center;justify-content:space-between;gap:var(--s4);
  min-height:var(--tap-min);box-sizing:border-box;padding:var(--s3) var(--s6);
  border-radius:var(--r-sm);background:var(--card);box-shadow:var(--edge-row);
  text-decoration:none;color:inherit;
}
.found-row:hover{background:var(--green-pale)}
.found-name{font:400 var(--text-lg)/1.2 var(--font-display);color:var(--green);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.found-where{flex:none;font:400 var(--text-sm)/1.25 var(--font-ui);color:var(--ink-soft)}
.found-none{margin:0;padding:var(--s3);font:400 var(--text-md)/1.4 var(--font-ui);color:var(--ink-soft)}
@media (max-width:760px){
  /* The head stacks on a phone; the open box takes the whole line. */
  .mast-right{width:100%;justify-content:flex-start}
  .studentfind[open],.studentfind.is-closing{flex:1 1 100%;order:-1}
  .studentfind{--find-w:100%}
  /* The card reaches out past the head's own inset, to 16px from each edge. */
  .found{left:calc(16px - var(--s6) - var(--s3));right:calc(16px - var(--s6) - var(--s3))}
}
@media (prefers-reduced-motion: reduce){
  .studentfind[open] .studentfind-pop,.studentfind.is-closing .studentfind-pop,
  .studentfind[open] .studentfind-form > input,.studentfind[open] .studentfind-close,
  .studentfind.is-closing .studentfind-form > input,.studentfind.is-closing .studentfind-close,
  .studentfind[open] .found:not(:empty){animation:none}
  .studentfind-icon{transition:none}
}

/* ---- 20. Morning | Afternoon, on the Classes screen (src/views/groups.ts) ----
   Two plain links under the pane's head, drawn as one segmented pill. The one
   shown is filled green. Each half is a full tap tall and half the width, so
   at 375px neither can be missed and nothing runs sideways. */
.daypart{flex:none;display:flex;gap:var(--s2);margin:0 0 var(--s5);padding:var(--s2);
  border-radius:var(--r-md);background:var(--card);box-shadow:var(--edge-row)}
.daypart-opt{flex:1 1 0;display:flex;align-items:center;justify-content:center;
  min-height:var(--tap-min);box-sizing:border-box;border-radius:var(--r-sm);
  font:600 var(--text-md)/1 var(--font-ui);color:var(--green);text-decoration:none}
.daypart-opt:hover{background:var(--green-pale)}
.daypart-opt[aria-current="page"]{background:var(--green);color:var(--card)}
.daypart-opt:focus-visible{outline:2px solid var(--green-tick);outline-offset:2px}

/* An Afternoon Group's Roster holds several Classes: each Student's Class,
   small beside the name, in the grade's own quiet type. */
.st-class{white-space:nowrap}

/* ---- 21. Order of the day (src/views/order.ts) --------------------------------
   Settings rows whose controls drop under the name on a phone and share its
   width, like the Groupings rows. Every button is a full tap tall. */
.orderrow{flex-wrap:wrap;row-gap:var(--s3)}
.orderrow .right{margin-left:auto;display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;
  gap:var(--s3);white-space:normal}
.orderrow form{display:flex;flex-wrap:nowrap;align-items:center;gap:var(--s3);margin:0}
.orderrow .btn,.orderrow select,.orderrow input[type=text]{box-sizing:border-box;min-height:var(--tap-min);margin:0}
.orderrow .btn{display:inline-flex;align-items:center;justify-content:center;padding:0 var(--s6)}
.orderrow .btn[disabled]{opacity:.35;cursor:default}
.orderrow select{width:auto;min-width:11em;font-size:var(--text-md);padding:0 var(--s9) 0 var(--s5)}
.orderrow input[type=text]{width:12em;font-size:var(--text-md);padding:0 var(--s5)}
.order-n{display:inline-block;min-width:1.6em;color:var(--ink-soft);font-family:var(--font-ui);font-size:var(--text-sm)}
/* Class settings: the name and its summary are one link to the Class's own
   page, a full tap tall, never underlined (rule 12). Its hover fill reaches a
   little past the words, the left margin taking it back so the name stays in
   line; the right margin keeps a clear gap before Up and Down. */
.orderrow .order-open{display:flex;align-items:center;gap:var(--s3);box-sizing:border-box;
  min-height:var(--tap-min);margin:0 var(--s5) 0 calc(-1 * var(--s3));padding:0 var(--s3);
  border-radius:var(--r-sm);color:inherit;text-decoration:none}
.orderrow .order-open:hover{background:var(--green-pale)}
.orderrow .order-text{flex:1;min-width:0;display:flex;flex-direction:column}
.orderrow .order-open:hover .chev{color:var(--green)}
/* "No group yet" has no grip: a blank the grip's size keeps its name in line. */
.order-gap{flex:none;width:var(--tap-min);height:var(--tap-min)}
.order-gap[hidden]{display:none}
@media (max-width:640px){
  .orderrow .main{flex:1 1 100%}
  .orderrow .right{flex:1 1 100%;justify-content:stretch}
  .orderrow form{flex:1 1 100%}
  .orderrow form .btn{flex:1 1 0}
  .orderrow select,.orderrow input[type=text]{flex:2 1 0;min-width:0;width:auto}
  .ordergroup form:last-child{flex:1 1 100%}
}

/* Dragging the Morning order (public/order-drag.js). The grip sits at the
   row's left, a full tap square with a row's own corners, and is hidden in
   the markup: only the script shows it, so with scripting off Up and Down are
   all there is. touch-action:none on the grip alone, so a finger on the grip
   drags and a finger anywhere else still scrolls the page. The row she holds
   floats on the lift shadow; the gap it leaves is the drop placeholder. */
.order-grip{flex:none;display:flex;align-items:center;justify-content:center;box-sizing:border-box;
  width:var(--tap-min);height:var(--tap-min);min-height:var(--tap-min);margin:0;padding:0;
  border:0;border-radius:var(--r-sm);background:var(--card);color:var(--ink-soft);box-shadow:var(--edge-row);
  cursor:grab;touch-action:none;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}
.order-grip[hidden]{display:none}
.order-morning{position:relative}
.order-grip:hover{background:var(--green-pale);color:var(--green)}
.order-grip:focus-visible{outline:2px solid var(--green-tick);outline-offset:2px}
.order-morning > .orderrow.is-dragging{position:fixed;z-index:50;margin:0;box-sizing:border-box;
  background:var(--card);border-radius:var(--r-sm);border-bottom:none;box-shadow:var(--lift-quiet)}
.order-morning > .orderrow.is-dragging .order-grip{cursor:grabbing;color:var(--green)}
.order-morning > .order-drop{box-sizing:border-box;border-bottom:1px solid var(--gold-hair);
  background:var(--green-pale);box-shadow:inset 0 0 0 2px var(--gold-hair);border-radius:var(--r-sm)}
.order-morning > .order-drop:last-child{border-bottom:none}
body.order-sorting{cursor:grabbing;-webkit-user-select:none;user-select:none}
@media (max-width:640px){
  /* The grip and the name share the first line; Up and Down go under them,
     a clear gap below the name's link so a tap to open never lands on them. */
  .order-morning .orderrow .main{flex:1 1 0}
  .order-morning > .orderrow{row-gap:var(--s6)}
}
@media (prefers-reduced-motion: reduce){
  .order-morning > .orderrow{transition:none !important}
}

/* ---- 22. Review and Memorize, one over the other, in both Sessions -------
   Version B, "Tap first", approved by the owner 2026-10-06
   (src/views/session-row.ts). Each Session is a column of its own holding
   two boxes, Review over Memorize (owner, 2026-11-01); the title, the quick
   fills, the typing boxes and "Add another" flow down each box.

   Colour per kind, in both Sessions: Review blue, Memorize orange, each a
   light tint with a coloured dot and title, coloured buttons and box edges.
   The words are always there too; colour never carries the meaning alone.
   Green ("done", "chosen") and red ("not marked") are not used for the kinds.
   Every control is var(--r-sm) round; only the title's dot is a circle. */
.session{
  --rev:#245E86;--rev-tint:#E9F1F7;--rev-line:#B9D0E1;
  --mem:#A04E12;--mem-tint:#FBEEE2;--mem-line:#EDC9A9;
  /* The boxes glide open and shut (public/picker.js reads this). */
  --glide:460ms cubic-bezier(.22,1,.36,1);
}
.piece[data-part="review"]{--hue:var(--rev);--tint:var(--rev-tint);--hue-line:var(--rev-line)}
.piece[data-part="memorize"]{--hue:var(--mem);--tint:var(--mem-tint);--hue-line:var(--mem-line)}
/* A Session's boxes flow down its column from the top: Review, then Memorize.
   The Morning's Review is shut until she adds one, and "Add a Review" stands
   in its place with Memorize right under it. */
.pieces{display:flex;flex-direction:column;gap:var(--g);align-self:start;min-width:0}
.pieces[data-review="off"] > .piece[data-part="review"]{display:none}
/* "Give this to everyone", centred under the boxes. It takes no room while
   hidden. */
.session-actions{display:flex;justify-content:center}
.session-actions:not(:has(> .give-row:not([hidden]))){display:none}
.add-review-row{display:flex;justify-content:flex-start}
.add-review-row[hidden]{display:none}
.add-review{
  position:relative;box-sizing:border-box;height:var(--track-h);margin:0;padding:0 var(--s6);
  display:inline-flex;align-items:center;gap:var(--s2);white-space:nowrap;
  border:1.5px dashed var(--rev-line);border-radius:var(--r-sm);background:none;cursor:pointer;
  font:600 var(--text-sm)/1 var(--font-ui);color:var(--rev);
}
.add-review:hover{background:var(--rev-tint)}
.add-review:focus-visible{outline:2px solid var(--rev);outline-offset:2px}
.add-review::before{content:"";position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);min-height:var(--tap-min)}
.piece{
  min-width:0;display:grid;row-gap:0;align-content:start;
  padding:var(--s4);border-radius:var(--r-sm);background:var(--tint);box-shadow:inset 0 0 0 1px var(--hue-line);
}
.piece > .box-title{grid-row:1}
.piece > .box-pick{grid-row:2}
.piece > .box-rest{grid-row:3;align-self:start;display:grid;row-gap:var(--s4);margin-top:var(--s4);min-width:0}
/* (The container is the typing area, not the box: a box's own containment would
   cut it off from the rows it shares.) */
.box-rest > .box-edit{min-width:0;container:box / inline-size}
.box-rest > .box-edit[hidden]{display:none}
.box-title{
  display:flex;align-items:center;gap:var(--s2);margin:0 0 var(--s3);
  font:400 var(--text-lg)/1.2 var(--font-display);letter-spacing:0;text-transform:none;color:var(--hue);
}
.box-title::before{content:"";flex:none;width:12px;height:12px;border-radius:50%;background:var(--hue)}

/* The quick fills: one button per line, the full Surah name, never wrapped.
   The chosen one is outlined in its box's colour. "Something else" is
   dashed, and sits at the foot of its row, open or shut, so both boxes'
   Something else, typing boxes and Add another stand level: the box with
   fewer buttons keeps its gap above Something else. Stacked on a phone the
   row is the box's own, and there is no gap. */
.box-pick{display:flex;flex-direction:column;gap:var(--s2);min-width:0;align-self:stretch}
.fill{
  position:relative;box-sizing:border-box;width:100%;min-height:var(--tap-min);margin:0;padding:var(--s2) var(--s4);
  /* The word, the Surah or book, the tick: on one baseline, the whole
     centred in the button, which grows when the words take two lines. */
  display:grid;grid-template-columns:auto minmax(0,1fr) auto;column-gap:var(--s3);align-items:baseline;align-content:center;
  text-align:left;cursor:pointer;
  border:0;border-radius:var(--r-sm);background:var(--card);box-shadow:inset 0 0 0 1px var(--hue-line);
  font:400 var(--text-md)/1.25 var(--font-ui);color:var(--ink);font-variant-numeric:tabular-nums;
}
.fill b{font-weight:600;color:var(--hue);white-space:nowrap}
/* Never cut: a long Surah or book wraps to a second line and the button grows. */
.fill > span{min-width:0;white-space:normal;overflow-wrap:break-word}
.fill:hover,.fill[aria-pressed="true"]{box-shadow:inset 0 0 0 2px var(--hue)}
.fill:focus-visible{outline:2px solid var(--hue);outline-offset:2px}
/* The chosen quick fill says so with a tick at its end, not by colour alone. */
.fill:not(.fill-other)[aria-pressed="true"]::after{content:"✓";grid-column:3;font-weight:600;color:var(--hue)}
.fill.fill-other{
  display:flex;align-items:center;
  justify-content:center;background:none;box-shadow:none;border:1.5px dashed var(--hue-line);
  font-weight:600;color:var(--hue);
}
.fill.fill-other:hover{background:var(--card)}
.fill.fill-other[aria-pressed="true"]{border-style:solid;border-color:var(--hue);background:var(--card)}
.box-pick > .fill-other{margin-top:auto}

/* The typing boxes. One grid per piece: Surah | Ayah | to | ayah, then the
   Page while "Find by page" is ticked and × on a piece after the first; the
   headings above, in one label style. "Goes into the next Surah" puts the
   Surah it runs to and the ayah it stops at there on a row under it, and
   its To ayah goes. The columns are spaced by each box's own margin, so a
   column with nothing in it takes no room at all. */
.lines{display:grid;row-gap:var(--s4);min-width:0}
.line{display:grid;row-gap:var(--s3);min-width:0}
.line + .line{padding-top:var(--s4);border-top:1px solid var(--hue-line)}
/* A blank first piece set aside by "Add another" (picker.js addLine). */
.line[hidden]{display:none}
.line[hidden] + .line{padding-top:0;border-top:0}
.q-surah,.q-lesson{position:relative;display:grid;row-gap:var(--s2);min-width:0}
.q{
  /* Each column after the first holds its box and the gap before it. */
  --q-num:calc(3.4rem + var(--s2));--q-page:0px;--q-del:0px;
  display:grid;grid-template-columns:minmax(0,1fr) var(--q-num) auto var(--q-num) var(--q-page) var(--q-del);
  column-gap:0;row-gap:var(--s1);align-items:center;min-width:0;
}
.q > *{min-width:0}
.q > :is(.q-ha,.q-hb,.q-hp,.q-a,.q-to,.q-b,.q-p,.q-del,.q-c,.q-x){margin-left:var(--s2)}
.qh{font:400 var(--text-sm)/1 var(--font-ui);letter-spacing:0;text-transform:none;color:color-mix(in srgb, var(--hue) 70%, var(--ink))}
.q-hs{grid-row:1;grid-column:1;padding-left:var(--s3)}
.q-ha{grid-row:1;grid-column:2;text-align:center}
.q-hb{grid-row:1;grid-column:3 / 5;text-align:center}
.q-hp{grid-row:1;grid-column:5;text-align:center}
.q-s{grid-row:2;grid-column:1}
.q-a{grid-row:2;grid-column:2}
.q-to{grid-row:2;grid-column:3;font:400 var(--text-md)/1 var(--font-ui);color:var(--ink-soft)}
.q-b{grid-row:2;grid-column:4}
.q-p{grid-row:2;grid-column:5}
.q-del{grid-row:2;grid-column:6}
.q-e{grid-row:3;grid-column:1;margin-top:var(--s2)}
.q-c{grid-row:3;grid-column:2;margin-top:var(--s2)}
.q-x{grid-row:3;grid-column:3 / 5;justify-self:start;margin-top:var(--s2)}
.piece .q input[type="text"]{width:100%;box-sizing:border-box;min-width:0}
.piece .q > input[type="text"]{width:calc(100% - var(--s2))}
.piece .q :is(.q-a,.q-b,.q-c,.q-p){padding:0 var(--s1);text-align:center;font-variant-numeric:tabular-nums}
.piece input[type="text"]{background:var(--card);box-shadow:inset 0 0 0 1px var(--hue-line)}
.piece input[type="text"]:focus-visible{outline:2px solid var(--hue);outline-offset:1px}
.q-p[data-auto]{color:var(--ink-soft)}
.q :is(.q-hp,.q-p,.q-e,.q-c,.q-x){display:none}
.q:has(> .q-del){--q-del:calc(var(--ctl) + var(--s2))}
.q-surah:has(> .q-page:checked) .q{--q-page:calc(3.6rem + var(--s2))}
.q-surah:has(> .q-page:checked) .q :is(.q-hp,.q-p){display:block}
.q-surah:has(> .q-run:checked) .q :is(.q-e,.q-c){display:block}
.q-surah:has(> .q-run:checked) .q .q-x{display:grid}
.q-surah:has(> .q-run:checked) .q :is(.q-hb,.q-to,.q-b){visibility:hidden}
/* The Surah it goes into says "to" inside its box, at its left. */
.q-e .picker.tagged{--tag-w:1.4rem}
.picker.tagged .inp{padding-left:calc(var(--s5) + var(--tag-w));padding-right:calc(var(--s8) + var(--s3))}
.piece-tag{
  position:absolute;left:var(--s5);top:50%;transform:translateY(-50%);pointer-events:none;
  font:600 var(--text-xs)/1 var(--font-ui);letter-spacing:var(--track-mid);
  text-transform:uppercase;color:var(--hue);
}
/* A Lesson: Book | Lesson | to | lesson, then what they did. */
.q-what{display:grid;gap:var(--s1);margin:0}
.q-what .qh{padding-left:var(--s3)}
.q-what .inp{width:100%;box-sizing:border-box}
/* A narrow box: the Surah takes its own line, the numbers go under it. */
@container box (max-width: 300px){
  .q{grid-template-columns:var(--q-num) auto var(--q-num) minmax(0,1fr) var(--q-page) var(--q-del)}
  .q > :is(.q-ha,.q-a,.q-e,.q-c){margin-left:0}
  .q-hs{grid-row:1;grid-column:1 / -1}
  .q-s{grid-row:2;grid-column:1 / 6}
  .q-del{grid-row:2;grid-column:6}
  .q-ha{grid-row:3;grid-column:1}
  .q-hb{grid-row:3;grid-column:2 / 4}
  .q-hp{grid-row:3;grid-column:5}
  .q-a{grid-row:4;grid-column:1}
  .q-to{grid-row:4;grid-column:2}
  .q-b{grid-row:4;grid-column:3}
  .q-p{grid-row:4;grid-column:5}
  .q-e{grid-row:5;grid-column:1 / 6}
  .q-c{grid-row:6;grid-column:1}
  .q-x{grid-row:6;grid-column:2 / 4}
}

/* The two toggles of a Surah piece are checkboxes, out of sight; their
   labels are the buttons she taps. "Goes into the next Surah" posts, so the
   save reads the second row only while it is ticked. */
.q-toggle{position:absolute;opacity:0;width:1px;height:1px;margin:0;pointer-events:none}
/* Two quiet text links, never a button (owner, 2026-10-12): "Goes into the
   next Surah" flush with the Surah box's left edge, "Find by page" flush
   with the right edge of the boxes above it; on a line of its own when the
   two do not fit. They keep a full tap's height, and show hover and focus
   by an underline and the focus ring alone. */
.q-links{display:flex;flex-wrap:wrap;justify-content:space-between;gap:0 var(--s4)}
.q-surah:has(.q-del) .q-links{padding-right:calc(var(--ctl) + var(--s2))}
.q-link{
  position:relative;display:inline-flex;align-items:center;gap:var(--s2);min-height:var(--tap-min);
  padding:0;margin:calc(-1 * var(--s2)) 0;border-radius:var(--r-sm);background:none;cursor:pointer;
  font:600 var(--text-sm)/1 var(--font-ui);letter-spacing:0;text-transform:none;color:var(--hue);
  text-decoration:none;text-underline-offset:3px;
}
.q-link:not(.q-run-on){margin-left:auto}
.q-link:hover,
.q-surah:has(> .q-run:focus-visible) .q-run-on,
.q-surah:has(> .q-page:focus-visible) .q-link:not(.q-run-on){background:none;text-decoration:underline}
.q-pg-off{display:none}
.q-surah:has(> .q-page:checked) .q-pg-on{display:none}
.q-surah:has(> .q-page:checked) .q-pg-off{display:inline}
.q-surah:has(> .q-run:checked) .q-run-on{display:none}
.q-surah:has(> .q-run:focus-visible) :is(.q-run-on,.q-x),
.q-surah:has(> .q-page:focus-visible) .q-link:not(.q-run-on){outline:2px solid var(--hue);outline-offset:4px}
.q-x{
  position:relative;box-sizing:border-box;width:var(--ctl);height:var(--ctl);place-items:center;
  border-radius:var(--r-sm);cursor:pointer;color:var(--ink-soft);
}
.q-x:hover{background:var(--card);color:var(--hue)}
.q-x::before{content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);min-width:var(--tap-min);min-height:var(--tap-min)}
/* "Ayah 3 of which Surah?": a Page and an ayah two short Surahs share. */
.q-which{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2);font:400 var(--text-md)/1.3 var(--font-ui);color:var(--ink)}
.q-which[hidden]{display:none}
.q-which button{
  min-height:var(--tap-min);padding:0 var(--s4);border:0;border-radius:var(--r-sm);cursor:pointer;
  background:var(--card);box-shadow:inset 0 0 0 1px var(--hue-line);font:600 var(--text-md)/1 var(--font-ui);color:var(--hue);
}
.q-which button:hover,.q-which button:focus-visible{box-shadow:inset 0 0 0 2px var(--hue);outline:none}

/* × on a piece after the first. */
.line-remove{
  position:relative;flex:none;box-sizing:border-box;width:var(--ctl);height:var(--ctl);padding:0;margin:0;
  display:grid;place-items:center;border:0;border-radius:var(--r-sm);background:none;cursor:pointer;color:var(--ink-soft);
}
.line-remove svg{display:block}
.line-remove:hover{background:var(--card);color:var(--hue)}
.line-remove:focus-visible,.line-add:focus-visible{outline:2px solid var(--hue);outline-offset:2px}
/* "Add another", and its "Surah or Book?". */
.add-wrap{position:relative}
.line-add{
  position:relative;width:100%;box-sizing:border-box;height:var(--track-h);margin:0;padding:0 var(--s4);
  display:flex;align-items:center;justify-content:center;gap:var(--s2);
  border:1.5px dashed var(--hue-line);border-radius:var(--r-sm);background:none;cursor:pointer;
  font:600 var(--text-sm)/1 var(--font-ui);color:var(--hue);
}
.line-add:hover{background:var(--card)}
.add-menu{
  position:absolute;left:0;right:0;top:calc(100% + var(--s2));z-index:30;
  display:grid;grid-template-columns:1fr 1fr;gap:var(--s2);padding:var(--s2);
  background:var(--card);border-radius:var(--r-sm);box-shadow:var(--edge-row),var(--lift-quiet);
}
.add-menu[hidden]{display:none}
.add-choice{
  min-height:calc(var(--tap-min) + var(--s4));border:0;border-radius:var(--r-sm);cursor:pointer;
  background:var(--track-fill);box-shadow:var(--edge-row);
  font:400 var(--text-lg)/1 var(--font-display);color:var(--green);
}
.add-choice:hover,.add-choice:focus-visible{background:var(--green-pale);outline:none}
.fill::before,.line-add::before,.line-remove::before{content:"";position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);min-height:var(--tap-min)}
.line-remove::before{left:50%;min-width:var(--tap-min);transform:translate(-50%,-50%)}

/* "Give this to everyone" (issue 13): the same size, corner and type as
   "Add a Review" beside it; a filled card where that one is dashed. */
.give-row{display:flex;justify-content:center}
.give-row[hidden]{display:none}
.give-all{
  position:relative;box-sizing:border-box;height:var(--track-h);margin:0;padding:0 var(--s6);
  display:inline-flex;align-items:center;gap:var(--s2);white-space:nowrap;
  border:0;border-radius:var(--r-sm);background:var(--card);box-shadow:var(--edge-row);cursor:pointer;
  font:600 var(--text-sm)/1 var(--font-ui);color:var(--green);
}
.give-all::before{content:"";position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);min-height:var(--tap-min)}
.give-all:hover{background:var(--green-pale)}
.give-all:focus-visible{outline:2px solid var(--green-tick);outline-offset:2px}
/* "Keep theirs?", a banner over the Day Entry: the question, two big answers. */
.give-ask{
  display:grid;gap:var(--s4);margin:0 0 var(--s5);padding:var(--s5) var(--s6);text-align:center;
  background:var(--card);box-shadow:var(--edge-row);border-radius:var(--r-sm);
}
.give-ask p{margin:0;font:400 var(--text-lg)/1.35 var(--font-display);color:var(--green)}
.give-ask-answers{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s3)}
.give-ask-answers .btn{width:100%;display:inline-flex;align-items:center;justify-content:center;white-space:normal}
/* The page numbers' credit (Tanzil, CC BY 3.0), quiet, under the Day Entry. */
.credit{margin:var(--s5) 0 0;text-align:center;font:400 var(--text-xs)/1.4 var(--font-ui);color:var(--ink-soft)}
.credit a{color:inherit}

@media (max-width:600px){
  .give-ask-answers{grid-template-columns:minmax(0,1fr)}
}
/* The grid of numbers over a From or To box (public/picker.js). */
.numgrid-pop{
  position:fixed;z-index:60;box-sizing:border-box;width:min(24rem,calc(100vw - 2rem));max-height:min(24rem,60vh);
  overflow-y:auto;overscroll-behavior:contain;padding:var(--s3);
  background:var(--card);border-radius:var(--r-sm);box-shadow:var(--edge-row),var(--lift-quiet);
}
.numgrid-title{margin:0 0 var(--s3);padding:0 var(--s2);font:600 var(--text-xs)/1.2 var(--font-ui);
  letter-spacing:var(--track-mid);text-transform:uppercase;color:var(--ink-soft)}
.numgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(3.25rem,1fr));gap:var(--s2)}
.numgrid button{
  position:relative;min-height:var(--tap-min);padding:0;border:0;border-radius:var(--r-sm);cursor:pointer;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
  background:color-mix(in srgb, var(--fill) 55%, transparent);box-shadow:var(--edge-row);
  font:600 var(--text-md)/1 var(--font-ui);color:var(--ink);font-variant-numeric:tabular-nums;
}
.numgrid button:hover,.numgrid button:focus-visible{background:var(--green-pale);outline:none}
.numgrid button[data-last]{box-shadow:inset 0 0 0 2px var(--gold)}
.numgrid button small{font:600 9px/1 var(--font-ui);letter-spacing:.06em;text-transform:uppercase;color:var(--gold)}
.numgrid button[aria-pressed="true"]{background:var(--green);color:var(--card)}
.numgrid button[aria-pressed="true"] small{color:var(--card)}
.numgrid button.dim{opacity:.45}
/* "Choose the work" over a Book box (public/picker.js): the Rename card, wider,
   holding a big tile per book. The chosen book is filled green. */
.book-sheet{width:min(40rem, calc(100vw - 2 * var(--s6)));max-height:calc(100dvh - 2 * var(--s6));overflow-y:auto}
.book-sheet-head{display:flex;align-items:center;justify-content:space-between;gap:var(--s4);margin-bottom:var(--s5)}
.book-sheet-head h3{margin:0;text-transform:none;letter-spacing:var(--track-tight)}
.book-sheet-close,.book-tile{
  border:0;border-radius:var(--r-sm);cursor:pointer;background:var(--card);box-shadow:var(--edge-row);color:var(--green);
}
.book-sheet-close{min-height:var(--tap-min);padding:0 var(--s6);font:400 var(--text-lg)/1 var(--font-display)}
.book-tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(9.5rem,1fr));gap:var(--s2)}
.book-tile{
  min-height:110px;padding:var(--s4);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--s2);
  font:400 var(--text-xl)/1.15 var(--font-display);color:var(--ink);text-align:center;overflow-wrap:anywhere;
}
.book-tile-ico{width:34px;height:34px;stroke:var(--green);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.book-sheet-close:hover,.book-tile:hover{background:var(--green-pale)}
.book-sheet-close:focus-visible,.book-tile:focus-visible{outline:2px solid var(--green-tick);outline-offset:2px}
.book-tile[aria-pressed="true"]{background:var(--green);color:var(--card);box-shadow:var(--lift-green)}
.book-tile[aria-pressed="true"] .book-tile-ico{stroke:var(--card)}
/* "Same as last time" on the Afternoon, with both pieces to copy: a small
   menu right under the button — Both, Review only, Memorize only — opened by
   public/picker.js. Hidden without it, and the button copies both. Each
   choice is a full tap tall. */
.same-wrap{position:relative;display:flex;min-width:0}
.same-wrap > .session-last{flex:1 1 auto;min-width:0}
.same-menu{
  position:absolute;top:calc(100% + var(--s2));right:0;z-index:30;
  display:flex;flex-direction:column;gap:var(--s1);min-width:10.5em;padding:var(--s2);
  background:var(--card);border-radius:var(--r-sm);box-shadow:var(--edge-row),var(--lift-quiet);
}
.same-menu[hidden]{display:none}
.same-choice{
  min-height:var(--tap-min);padding:0 var(--s5);border:0;border-radius:var(--r-xs);
  background:none;cursor:pointer;text-align:left;white-space:nowrap;
  font:400 var(--text-md)/1 var(--font-ui);color:var(--green);
}
.same-choice:hover,.same-choice:focus-visible{background:var(--green-pale);outline:none}

/* ---- 23. The Session's header: toggles (owner, 2026-09-24) ----------------
   Here|Absent, and Surah|Lesson, are each ONE track holding two options. The
   chosen option is drawn as a raised chip inside it, placed from the markup
   alone (the checked radio, or the aria-pressed button), with no script.
   Before Here or Absent is pressed nothing is raised and a hairline parts the
   two. Surah, Lesson and Here are green; Absent the Desk's gold.

   Every track, like every control in the Session, is 36px tall (section
   23b). Every option, Clear and "Same as last time" are drawn slimmer than a
   fingertip; an invisible edge takes each tap back up to var(--tap-min). */
.session{--track-h:36px;--track-pad:4px;--track-r:var(--r-sm);--track-ui:.06em;--track-fill:color-mix(in srgb, var(--fill) 55%, transparent)}
/* Each track fills its cell of the grid, its two options in equal halves. */
.session .seg{
  position:relative;display:grid;grid-template-columns:1fr 1fr;gap:0;min-width:0;
  box-sizing:border-box;height:var(--track-h);padding:var(--track-pad);
  border-radius:var(--track-r);background:var(--track-fill);box-shadow:var(--edge-row);
}
/* A hairline between the two options, on the second one's leading edge. It
   goes once either is chosen: the highlight does the dividing then. */
.session .seg > :nth-child(2)::after{
  content:"";position:absolute;left:0;top:28%;bottom:28%;width:1px;margin-left:-.5px;
  background:var(--grey-line);pointer-events:none;transition:opacity .2s ease;
}
.session .seg:has(> button[aria-pressed="true"]) > :nth-child(2)::after,
.session .seg:has(> label input:checked) > :nth-child(2)::after{opacity:0}
.session .seg > button,
.session .seg > label{
  position:relative;z-index:1;box-sizing:border-box;height:100%;min-height:0;min-width:0;margin:0;padding:0 var(--s1);
  display:flex;align-items:center;justify-content:center;white-space:nowrap;
  border:0;border-radius:calc(var(--track-r) - var(--track-pad));background:transparent;box-shadow:none;cursor:pointer;
  font:600 var(--text-xs)/1 var(--font-ui);letter-spacing:var(--track-ui);text-transform:uppercase;
  color:var(--ink-soft);transition:color .2s ease,background-color .2s ease,box-shadow .2s ease;
}
.session .seg > button:not([aria-pressed="true"]):hover,
.session .seg > label:not(:has(input:checked)):hover{color:var(--ink)}
.session .seg > button[aria-pressed="true"],
.session .seg > label:has(input:checked){
  background:var(--green-pale);color:var(--green);
  box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--green-tick) 22%, transparent),var(--lift-soft);
}
.session .seg > button:nth-child(2)[aria-pressed="true"]{
  background:color-mix(in srgb, var(--gold) 9%, var(--card));color:var(--gold);
  box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--gold) 26%, transparent),var(--lift-soft);
}
.session .seg > button:focus-visible{outline:2px solid var(--green-tick);outline-offset:2px}
/* Clear: quiet small capitals, no box. "Same as last time": a quiet chip,
   the tracks' height, fill and type. */
.session-clear,
.session-last{
  position:relative;box-sizing:border-box;height:var(--track-h);min-height:0;min-width:0;margin:0;white-space:nowrap;
  border:0;border-radius:var(--track-r);cursor:pointer;
  font:600 var(--text-xs)/1 var(--font-ui);letter-spacing:var(--track-ui);text-transform:uppercase;
}
.session-clear{padding:0 var(--s3);background:none;color:var(--ink-soft)}
.session-clear:hover{color:var(--green)}
.session-last{padding:0 var(--s3);background:var(--track-fill);box-shadow:var(--edge-row);color:var(--green)}
.session-last:hover{background:var(--green-pale)}
.session-clear::before,
.session-last::before,
.session .seg > button::before,
.session .seg > label::before{
  content:"";position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);min-height:var(--tap-min);
}
@media (prefers-reduced-motion: reduce){
  .session .seg > button,.session .seg > label{transition:none}
}
/* ---- 23b. One grid for the Session (owner, 2026-09-25) ------------------
   "Bricks, badly placed": so every box in a Session now stands on ONE grid,
   and every edge lines up with an edge above it.

   One height and one gap. Every control is 36px, Here|Absent's height (the
   inputs too: --ctl is 36px inside a Session), and every gap, across and
   down, is --g. 36 rather than the Desk's 46 because the Session may not
   grow: at 46 the Morning's five rows would be ~40px taller. Buttons keep a
   48px tap edge (their ::before).

   Each Session is a column of two equal tracks (owner, 2026-11-01). Line 1:
   the name, Clear at the right. Line 2: the Morning's Surah|Lesson switch
   on the left, Here|Absent on the right (the Afternoon has no switch, so its
   Here|Absent stand at the right, in the same place). Line 3: "Same as last
   time", the full width. The work comes under them. */
.session{--g:var(--s3);--ctl:var(--track-h);--cols:repeat(2,minmax(0,1fr));grid-template-rows:auto auto auto 1fr}
/* The name's line is a control's height, so Clear beside it moves nothing. */
.session > .session-name{grid-row:1;grid-column:1}
.session .session-clear{grid-row:1;grid-column:2;justify-self:end}
.session .session-kind{grid-row:2;grid-column:1}
.session .session-attend{grid-row:2;grid-column:2}
.session .same-wrap,.session > .session-tools > .session-last{grid-row:3;grid-column:1 / -1}
.session > .session-work{grid-row:4;align-self:stretch}
.session .session-attend,
.session .session-kind,
.session .session-last,
.session .same-wrap{width:100%}
/* The smallest phones (320 px): Surah|Lesson and Here|Absent take a full row
   each, so no word presses against its track's edge. */
@media (max-width:340px){
  .session .session-kind{grid-column:1 / -1}
  .session .session-attend{grid-row:3;grid-column:1 / -1}
  .session .same-wrap,.session > .session-tools > .session-last{grid-row:4}
  .session > .session-work{grid-row:5}
  .session:not(:has(.session-kind)) .session-attend{grid-row:2}
  .session:not(:has(.session-kind)) .same-wrap,.session:not(:has(.session-kind)) > .session-tools > .session-last{grid-row:3}
  .session:not(:has(.session-kind)) > .session-work{grid-row:4}
}
/* Two columns in a laptop window about 1000 px wide (section 16): side by side,
   Surah|Lesson and Here|Absent get 61 px a half and "Lesson" spills its edge, so
   each takes a full row, as on the smallest phones. The Afternoon has no switch:
   its line 2 stays empty, so Here|Absent, "Same as last time" and the boxes stand
   level in both columns. */
@container (min-width:540px) and (max-width:699px){
  .session{grid-template-rows:auto var(--ctl) auto auto 1fr}
  .session .session-kind{grid-column:1 / -1}
  .session .session-attend{grid-row:3;grid-column:1 / -1}
  .session .same-wrap,.session > .session-tools > .session-last{grid-row:4}
  .session > .session-work{grid-row:5}
}

/* ====================================================================
   SAVING AND STATUS ON THE DESK — issues 6 and 31, and issue 19's item 5
   and the Afternoon columns on a phone. One block, on purpose, so it merges
   cleanly beside other work on this file. Approved mock-ups: branches
   prototype/save-status (variant B) and prototype/desk-status (variant A).
   ==================================================================== */

/* The red that means "not done yet" and nothing else (issues 7 and 31).
   "Not saved" and "No internet" keep the quiet red, --red-quiet. */
:root{--not-done:#A8433C}

/* ---- The name line (issue 6, approved mock-up prototype/save-status B) ---
   One band: on the left the name, the state pill and the save message; on
   the right of the same band Excuse this Student and Next Student →, at the
   mock-up's own sizes. The pill and the message share one text size (20px)
   and one height (#31), and the pill is only as wide as its word. The name
   always has its own line and the pill and message sit under it, so the
   block is two even lines with the buttons centred beside them. Letting the
   name and pill share a line left the message alone on an empty second line
   before her first change, and the buttons sank below the name (owner,
   2026-10-12). A long name wraps rather than being cut (issue 19, item 5).
   Where the Day Entry is too narrow for that band (a phone, a small laptop),
   it stacks: the name; the pill and message; Excuse, then Next Student,
   each the full width on a row of its own and on one line, so neither label
   breaks (owner, 2026-10-12). Never a lone right-aligned row. The band is a container
   query on the Day Entry, so without one the stacked form still works. */
.pane-entry{container-type:inline-size}
.entry-head{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s4)}
.entry-who{flex-wrap:wrap;row-gap:var(--s3);column-gap:var(--s4)}
.entry-who .who-name{
  flex:0 1 100%;min-width:0;
  white-space:normal;overflow:visible;text-overflow:clip;overflow-wrap:break-word;
}
/* The Roster's names and notes wrap in full too, never cut with "…": on an
   Afternoon Group's Desk a long name carries its Class after it ("· No group
   yet"), and a phone has no room for both on one line. */
.st-name,.st-note{white-space:normal;overflow:visible;text-overflow:clip;overflow-wrap:break-word}
.entry-who .state-pill,.save-msg{
  display:inline-flex;align-items:center;flex:none;box-sizing:border-box;
  height:var(--tap-min);
  font:400 20px/1 var(--font-display);letter-spacing:0;text-transform:none;white-space:nowrap;
}
.entry-who .state-pill{padding:0 var(--s4);border-radius:var(--r-md)}
.entry-actions{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--s4)}
.entry-actions .btn,.entry-head .entry-actions .chip-form .btn.chip{
  display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;
  width:100%;min-height:var(--tap-min);padding:var(--s2) var(--s4);
  white-space:nowrap;line-height:1.15;text-align:center;
}
/* One text size in the name line: Excuse and Next read as large as the pill
   and the save message beside them (owner, 2026-10-06). */
.entry-actions .btn,.entry-head .excuse-menu > summary.btn.chip{font-size:20px;line-height:1}
@container (min-width:740px){
  /* The name has its own line above the pill and the save message, so the
     buttons sit on that second line with them, not between the two. */
  .entry-head{display:flex;align-items:flex-end;justify-content:space-between}
  .entry-who{flex:1 1 auto}
  .entry-actions{display:flex;flex:none;align-items:center}
  .entry-actions .btn,.entry-head .entry-actions .chip-form .btn.chip{width:auto;white-space:nowrap;padding:var(--s5) var(--s7)}
  .save-msg{min-width:calc(28px + var(--s3) + 7em + var(--s2) + var(--s5));padding:0 var(--s5) 0 var(--s2)}
}

/* The save message. In the band it keeps a fixed width in every state, the
   longest ("Saved 12:59 PM") reserved, so the buttons never jump; stacked,
   nothing stands to its right, so it takes only its own width and fits
   beside the pill on a phone. Empty until she changes something. */
.save-msg{justify-content:flex-start;gap:var(--s3);border-radius:var(--r-md);color:var(--ink)}
.save-msg[data-save="failed"]{background:var(--red-quiet);color:#fff;padding:0 var(--s5) 0 var(--s2)}
/* The 28px signs beside the words: an empty ring, a filled tick, a filled
   "!". Never colour alone: each is its own shape. */
.sign{flex:none;width:28px;height:28px}
.sign-ring{fill:none;stroke:var(--ink);stroke-width:3}
.sign-tick .sign-disc{fill:var(--green-tick)}
.sign-tick .sign-check{fill:none;stroke:#fff;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.sign-warn .sign-disc{fill:#fff}
.sign-warn .sign-bar{stroke:var(--red-quiet);stroke-width:3.2;stroke-linecap:round}
.sign-warn .sign-dot{fill:var(--red-quiet)}

/* ---- No internet, or signed out: one banner above the title (issue 6) --
   In the page, never fixed: it pushes the Desk down and covers nothing. */
.net-banner{
  display:flex;align-items:center;justify-content:center;gap:var(--s4);
  margin:var(--s4) 0 var(--s6);padding:var(--s4) var(--s7);
  border-radius:var(--r-lg);background:var(--red-quiet);color:#fff;
  font:400 20px/1.3 var(--font-display);text-align:center;
}
.net-banner[hidden]{display:none}
.net-banner a{color:#fff;text-underline-offset:3px}

/* ---- The one count, in the Desk's header (issue 31) --------------------
   "2 not done yet": white bold words on the red, 36px tall. "All done ✓":
   green on pale green. An excused Class keeps the quiet "No class". */
.deskhead-right > .deskcount{height:36px;padding:0 var(--s6);border-radius:var(--r-md)}
.deskcount[data-on="todo"],.deskcount[data-on="done"]{
  font:700 15px/1 var(--font-ui);letter-spacing:0;text-transform:none;
}
.deskcount[data-on="todo"]{background:var(--not-done);color:#fff;box-shadow:none}
.deskcount[data-on="done"]{background:var(--green-pale);color:var(--green);box-shadow:inset 0 0 0 1px var(--gold-line)}

/* ---- The Roster's marks (issue 31) -------------------------------------
   Not done yet: an empty red ring where the grey ○ was. Done keeps the
   green tick; Absent the grey cross. A one-off is the grey word Visiting. */
.st-mark[data-mark="todo"]{background:transparent;box-shadow:inset 0 0 0 2.5px var(--not-done);color:transparent}
.st[aria-current="true"] .st-mark[data-mark="todo"]{background:transparent;box-shadow:inset 0 0 0 2.5px #F2B8B1}
.st-mark.st-visit{
  width:auto;height:auto;border-radius:0;background:transparent;padding:0 2px;
  font:600 var(--text-sm)/1 var(--font-ui);color:var(--ink-soft);
}
.st[aria-current="true"] .st-mark.st-visit{background:transparent;color:rgba(251,246,232,.72)}
.st[data-visiting] .st-num{color:var(--ink-soft)}

/* ---- 24. The Printed Sheets (src/views/sheets.ts) -------------------------
   On screen: the period bar, then each sheet as a white card. On paper: the
   sheets alone, each on a page of its own, in thin grey rules. One Week is
   five rows and fits a page with room to spare; a longer period flows on,
   the table's head printed again at the top of each page. */
/* About a page wide on a laptop, so what she sees is what prints. */
body.page-print #shell.wide{max-width:980px}
/* The helper line may run to two lines on a phone: give it room to. */
.print-mast .print-help{line-height:1.35}
/* The toolbar: the four quick picks, From and To, Print. ONE shape for every
   control on it (owner, 2026-09-24): var(--tap-min) tall, var(--r-sm) round,
   drawn like the Session's toggles (section 23). A part inside another (the
   chosen pick in its track, a date in its box) is inset by --track-pad and
   rounded var(--r-sm) less that inset, so the curves run parallel. On a
   laptop all of it is one row, Print at its right end. */
.print-bar{--track-h:var(--tap-min);--track-pad:4px;--track-r:var(--r-sm);--track-ui:.06em;
  --track-inner:calc(var(--track-r) - var(--track-pad));
  --track-fill:color-mix(in srgb, var(--fill) 55%, transparent);
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2);margin:0 0 var(--s7)}
.print-presets{display:grid;grid-auto-flow:column;grid-auto-columns:auto;gap:0;margin:0;
  box-sizing:border-box;height:var(--track-h);padding:var(--track-pad);
  border-radius:var(--track-r);background:var(--track-fill);box-shadow:var(--edge-row)}
.print-opt{position:relative;display:flex;align-items:center;justify-content:center;
  box-sizing:border-box;height:calc(var(--track-h) - 2 * var(--track-pad));padding:0 var(--s3);
  border-radius:var(--track-inner);white-space:nowrap;text-decoration:none;
  font:600 var(--text-xs)/1 var(--font-ui);letter-spacing:var(--track-ui);text-transform:uppercase;
  color:var(--ink-soft);transition:color .2s ease,background-color .2s ease,box-shadow .2s ease}
.print-opt:not([aria-current="page"]):hover{color:var(--ink)}
.print-opt[aria-current="page"]{background:var(--green-pale);color:var(--green);
  box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--green-tick) 22%, transparent),var(--lift-soft)}
.print-opt:focus-visible{outline:2px solid var(--green-tick);outline-offset:2px}

/* From and To in one box. Her own period (no quick pick) is the chosen one,
   so the box then wears the chosen pick's green. */
.print-range{display:flex;align-items:center;gap:var(--s2);margin:0}
.print-dates{display:flex;align-items:center;gap:var(--s2);box-sizing:border-box;
  height:var(--track-h);padding:var(--track-pad) var(--track-pad) var(--track-pad) var(--s3);
  border-radius:var(--track-r);background:var(--track-fill);box-shadow:var(--edge-row);
  transition:background-color .2s ease,box-shadow .2s ease}
.print-dates[aria-current="true"]{background:var(--green-pale);
  box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--green-tick) 22%, transparent),var(--lift-soft)}
.print-dates label{margin:0;white-space:nowrap;cursor:pointer;
  font:600 var(--text-xs)/1 var(--font-ui);letter-spacing:var(--track-ui);text-transform:uppercase;color:var(--ink-soft)}
.print-dates[aria-current="true"] label{color:var(--green)}
.print-dates input[type=date]{box-sizing:border-box;height:calc(var(--track-h) - 2 * var(--track-pad));min-width:0;
  margin:0;padding:0 var(--s1) 0 var(--s2);border:0;border-radius:var(--track-inner);background:var(--card);
  box-shadow:inset 0 0 0 1px var(--gold-line);
  font:400 var(--text-md)/1 var(--font-ui);color:var(--ink);font-variant-numeric:tabular-nums}
.print-dates input[type=date]::-webkit-calendar-picker-indicator{margin:0 0 0 var(--s1);padding:0;cursor:pointer}
.print-dates input[type=date]:focus-visible{outline:2px solid var(--green-tick);outline-offset:1px}
/* With scripting off only: the same shape as the box beside it. */
.print-show{box-sizing:border-box;height:var(--track-h);margin:0;padding:0 var(--s6);border:0;
  border-radius:var(--track-r);background:var(--card);box-shadow:var(--edge-row);cursor:pointer;
  font:600 var(--text-xs)/1 var(--font-ui);letter-spacing:var(--track-ui);text-transform:uppercase;color:var(--green)}
.print-show:hover{background:var(--green-pale)}
/* Print: the one green action, in the display face like every main button,
   but this toolbar's shape, never the pill. */
.print-go{display:inline-flex;align-items:center;justify-content:center;margin:0 0 0 auto;
  box-sizing:border-box;height:var(--track-h);padding:0 var(--s6);white-space:nowrap;
  border:0;border-radius:var(--track-r);background:var(--green-deep);box-shadow:var(--lift-button);cursor:pointer;
  font:400 var(--text-lg)/1 var(--font-display);letter-spacing:var(--track-tight);color:#FBF6E8}
.print-go:hover{background:linear-gradient(165deg,#1B6647 0%,#0F5438 55%,#0B442F 100%)}
.print-go:active{transform:translateY(1px)}
.print-go:focus-visible{outline:2px solid var(--green-tick);outline-offset:2px}
/* print.js shows Print; until then it is not there. */
.print-go[hidden]{display:none}
/* Five picks (Since last print is offered): the page is 980px at most, and
   five picks, From and To and "Print 50 sheets" need about 1070 (measured
   2026-09-24), so they cannot share one row. The picks take the first row;
   From and To and Print share the second, Print still at its right end, so
   no control stands on a row alone. Four picks stay one row, as before. */
@media (min-width:641px){
  .print-bar:has(> .print-presets > :nth-child(5)){display:grid;grid-template-columns:auto minmax(0,1fr) auto;
    row-gap:var(--s3)}
  .print-bar:has(> .print-presets > :nth-child(5)) > .print-presets{grid-column:1 / -1;justify-self:start}
  .print-bar:has(> .print-presets > :nth-child(5)) > .print-go{grid-column:3}
}
/* Last printed, with Undo and Mark as printed; and, after the print dialog,
   "Did these sheets print?". The buttons are this toolbar's shape: the Yes
   is Print's green, the rest quiet like the Show beside the dates. */
.print-status,.print-confirm{--track-h:var(--tap-min);--track-r:var(--r-sm);--track-ui:.06em;
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2) var(--s4);margin:0 0 var(--s5)}
.print-status[hidden],.print-confirm[hidden]{display:none}
.print-status form{margin:0}
.print-last,.print-ask{margin:0;font:400 var(--text-md)/1.35 var(--font-ui);color:var(--ink-soft)}
.print-ask{font-weight:600;color:var(--ink)}
.print-confirm{padding:var(--s2) var(--s2) var(--s2) var(--s5);border-radius:var(--r-sm);
  background:var(--green-pale);box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--green-tick) 22%, transparent)}
.print-confirm .print-yes{margin:0 0 0 auto}
.print-quiet{box-sizing:border-box;height:var(--track-h);margin:0;padding:0 var(--s6);border:0;white-space:nowrap;
  border-radius:var(--track-r);background:var(--card);box-shadow:var(--edge-row);cursor:pointer;
  font:600 var(--text-xs)/1 var(--font-ui);letter-spacing:var(--track-ui);text-transform:uppercase;color:var(--green)}
.print-quiet:hover{background:var(--green-pale)}
.print-quiet:focus-visible{outline:2px solid var(--green-tick);outline-offset:2px}
.print-quiet:disabled,.print-go:disabled{opacity:.6;cursor:default}
@media (prefers-reduced-motion: reduce){
  .print-opt,.print-dates{transition:none}
  .print-go:active{transform:none}
}
/* A phone: the picks two by two, each row a full tap tall; From and To
   across the width; Print under them, across the width too. */
@media (max-width:640px){
  .print-bar{display:grid;grid-template-columns:minmax(0,1fr)}
  .print-presets{grid-auto-flow:row;grid-template-columns:1fr 1fr;
    grid-auto-rows:calc(var(--track-h) - 2 * var(--track-pad));row-gap:calc(2 * var(--track-pad));height:auto}
  /* Five picks: Since last print, first, across the track. */
  .print-presets > .print-opt:first-child:nth-last-child(5){grid-column:1 / -1}
  .print-range,.print-dates{width:100%}
  .print-dates input[type=date]{flex:1 1 0}
  .print-go{width:100%;margin:0}
  /* The question over its two answers, each across the width. */
  .print-confirm{padding:var(--s4)}
  .print-confirm .print-ask{flex:1 1 100%}
  /* Its long label on one line: the display face a size down. */
  .print-confirm .print-yes{margin:0;font-size:var(--text-md);letter-spacing:0}
  .print-confirm .print-yes,.print-confirm .print-quiet{flex:1 1 100%}
}

.sheet{background:var(--card);box-shadow:var(--edge-row);border-radius:var(--r-sm);
  padding:var(--s7) var(--s8);margin:0 0 var(--s7)}
.sheet-head{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--s2) var(--s6);
  padding:0 0 var(--s4);margin:0 0 var(--s4);border-bottom:1px solid var(--grey-line)}
.sheet-name{font:400 var(--text-xl)/1.15 var(--font-display);color:var(--green)}
.sheet-class{font:600 var(--text-md)/1.2 var(--font-ui);color:var(--ink)}
.sheet-when{margin-left:auto;font:400 var(--text-md)/1.2 var(--font-ui);color:var(--ink-soft);font-variant-numeric:tabular-nums}
.sheet-days{width:100%;border-collapse:collapse;table-layout:fixed;
  font:400 var(--text-base)/1.35 var(--font-ui);color:var(--ink)}
.sheet-days th,.sheet-days td{padding:var(--s2) var(--s3);border-bottom:1px solid var(--grey-line);
  text-align:left;vertical-align:top;overflow-wrap:anywhere}
.sheet-days thead th{font:600 var(--text-xs)/1.2 var(--font-ui);letter-spacing:var(--track-mid);
  text-transform:uppercase;color:var(--ink-soft);border-bottom-color:var(--grey-mark)}
.sheet-days tbody th{font-weight:600;white-space:nowrap;font-variant-numeric:tabular-nums}
/* Date, Attendance, Classwork, Notes: the Date and the Attendance narrow,
   the Classwork and the Notes the rest, about 55 to 45. A graded Class's
   sheet adds a narrow Grade after the Classwork. Sized in the body's type,
   not the small head's, so "Wed 16 Sep" fits its column; the Attendance is
   as wide as its head, which is wider than "No class". The Classwork is a
   plain share of the table: a table column ignores a width that mixes a
   share and a length. */
/* Since 2026-09-25 the Morning and the Afternoon side by side: Date; the
   Morning's Attendance and Classwork; the Afternoon's Attendance, Review and
   Memorize; Notes. The widths sit on <col>s, the narrow ones in lengths, the
   four text columns sharing what is left equally. A thin rule starts the
   Afternoon and the Notes (.sheet-split), and "Morning" and "Afternoon" head
   their columns on a row above. */
.sheet-days col.sheet-date{width:calc(var(--text-base) * 7.5)}
/* The Attendance holds one word, so its head is set tighter to keep it
   narrow and give the work columns the room (owner, 2026-09-25). */
.sheet-days col.sheet-att{width:calc(var(--text-base) * 7.5)}
/* The Morning's one Classwork column holds what the Afternoon splits in two,
   so it gets the widest share; the Notes take what is left (owner,
   2026-09-25: "Morning is too tight compared to afternoon"). */
.sheet-days col.sheet-classwork{width:25%}
.sheet-days col.sheet-review,.sheet-days col.sheet-memorize{width:15%}
.sheet-days thead th.sheet-att{letter-spacing:.02em}
.sheet-days .sheet-nb{white-space:nowrap}
.sheet-days col.sheet-grade{width:calc(var(--text-base) * 6)}
.sheet-days thead .sheet-att,
.sheet-days td[class^="att-"]{white-space:nowrap}
.sheet-days .sheet-split{border-left:1px solid var(--grey-line)}
.sheet-days thead .sheet-part{color:var(--green);border-bottom-color:var(--grey-line);text-align:left}
.sheet-days thead tr:first-child th[rowspan]{vertical-align:bottom}
/* Each Day's date opens that Day on the Student's Desk (src/views/sheets.ts
   deskDayHref). The text as before, ink and weight unchanged; the app's
   link underline only on hover. The whole date cell is the tap area, a full
   tap tall on screen. On paper it is plain text (@media print below). */
.sheet-days tbody th{position:relative}
.sheet-days a.sheet-day{color:inherit;text-decoration:none}
.sheet-days a.sheet-day::after{content:"";position:absolute;inset:0}
.sheet-days a.sheet-day:hover{text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--green-tick)}
.sheet-days a.sheet-day:focus-visible{outline:none}
.sheet-days tbody th:has(a.sheet-day:focus-visible){outline:2px solid var(--green-tick);outline-offset:-2px}
@media screen{.sheet-days tbody th{height:var(--tap-min)}}
/* The Morning's register in words, no dot and no pill. Absent in the gold
   and weight of the Session's Absent toggle (section 23), so it stands out on
   screen and still in black and white; No class quiet; Present plain ink. */
.sheet-days .att-absent{font-weight:600;color:var(--gold)}
.sheet-days .att-none{color:var(--ink-soft)}
/* "Present 8 · Absent 1", beside the Class, in the period's quiet type. */
.sheet-tally{font:400 var(--text-md)/1.2 var(--font-ui);color:var(--ink-soft);font-variant-numeric:tabular-nums}
.sheet-sign{display:flex;align-items:flex-end;gap:var(--s4);margin:var(--s8) 0 0;white-space:nowrap;
  font:400 var(--text-md)/1 var(--font-ui);color:var(--ink)}
.sheet-line{flex:0 1 20em;min-width:0;border-bottom:1px solid var(--ink-soft);height:1.4em}
/* A phone shows the table as it is, sideways inside its card, never the page.
   The head is the name on a line of its own, then the Class and the period
   on one line under it, at either end, then the attendance line. */
@media (max-width:640px){
  .sheet{padding:var(--s5);overflow-x:auto}
  .sheet-days{min-width:900px}
  .sheet-head{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:var(--s2) var(--s4);position:sticky;left:0}
  .sheet-name{grid-column:1 / -1}
  .sheet-tally{grid-column:1 / -1;order:1}
  .sheet-when{margin-left:0;white-space:nowrap}
  .sheet-sign{position:sticky;left:0}
}

@page{size:portrait;margin:12mm}
/* The sheets alone print sideways: eight columns (ten on a graded Class)
   leave a portrait A4's 186mm about 20mm a text column, too narrow for
   "Al-Mulk · p. 562 · 1–12". Landscape gives them 273mm. */
@page sheet{size:landscape;margin:10mm}
@media print{
  /* The whole Printed Sheets page on the landscape page, not only each
     .sheet: anything before the first sheet (the SVG defs at the top of
     <body>) otherwise opens a blank portrait page 1 in Chrome. */
  body.page-print,.sheet{page:sheet}
  html,body{background:var(--card)}
  #nav,#navEdge,#crumb,.print-mast,.print-bar,.print-status,.print-confirm{display:none !important}
  #shell,#shell.wide{max-width:none;margin:0;padding:0}
  .sheet{box-shadow:none;border-radius:0;padding:0;margin:0;overflow:visible}
  .sheet + .sheet{break-before:page}
  .sheet-days{min-width:0}
  .sheet-days thead{display:table-header-group}
  .sheet-days tr{break-inside:avoid}
  .sheet-sign{break-inside:avoid;break-before:avoid}
  .sheet-days a.sheet-day,.sheet-days a.sheet-day:hover{color:inherit;text-decoration:none}
  .sheet-days a.sheet-day::after{content:none}
}

/* ---- The roster's search box (src/views/group.ts) ---------------------------
   Above the class list on the Desk. 48px tall, the app's 17px corner. A row
   it filters out takes .is-filtered. */
.rostersearch{margin:0 0 var(--s4)}
.rostersearch input[type=search]{
  appearance:none;-webkit-appearance:none;box-sizing:border-box;width:100%;
  height:var(--tap-min);min-height:var(--tap-min);padding:0 var(--s5);
  font:400 var(--text-md)/1.3 var(--font-ui);color:var(--ink);
  border:0;box-shadow:var(--edge-row);border-radius:var(--r-sm);background:var(--card);
}
.rostersearch input[type=search]:focus{outline:2px solid var(--green-tick);outline-offset:1px}
.rostersearch input::placeholder{color:var(--ink-soft);opacity:.7}
.roster .st.is-filtered{display:none}
.rostersearch-none{margin:var(--s3) 0 0}

/* ---- A Student who comes to one Session only (owner, 2026-09-25) ----------
   On the Desk, the Session they do not come to is one quiet line, not the
   tall empty pane. On a Student's form, the three choices stand one under
   the other, drawn as the Surah | Lesson radios are. */
.desk .empty.session-off{padding:var(--s4) var(--s6)}
.seg.seg-stack{flex-direction:column;margin-top:var(--s2)}
.seg.seg-stack label{
  flex:none;box-sizing:border-box;min-height:var(--tap-min);padding:0 var(--s3);
  font:400 var(--text-md)/1.2 var(--font-ui);justify-content:flex-start;
}

/* ---- A one-off Session (owner, 2026-09-25) -------------------------------
   Beside "Morning only", the quiet "Came this afternoon" that opens it for
   the Day; under an Afternoon Group's Roster, "Also came today" and a quiet
   button per Morning-only Student. Both keep the one corner, --r-sm. */
.session-off-row{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--s3) var(--s5);padding:var(--s2) 0}
.session-off-row .empty.session-off{padding:var(--s3) 0;margin:0}
/* A Session she does not mark today is still a Session of its own: it takes
   its column, with the same hairline between it and the other, so "Morning
   only" never reads as the foot of the Morning. The note over a Session they
   came to anyway stays at the top of that Session's column. */
.session-off-row .btn,.also-came .btn{
  display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;
  min-height:var(--tap-min);padding:0 var(--s6);border-radius:var(--r-sm);
  font:400 var(--text-md)/1.2 var(--font-ui);width:auto;flex:none;
}
/* On the Day Entry its button is the size, corner and type of "Add a Review"
   and "Give this to everyone", so every button under the boxes is one kind. */
.session-off-row .btn{
  position:relative;height:36px;min-height:0;padding:0 var(--s6);white-space:nowrap;
  background:var(--card);box-shadow:var(--edge-row);color:var(--green);
  font:600 var(--text-sm)/1 var(--font-ui);letter-spacing:0;
}
.session-off-row .btn:hover{background:var(--green-pale);color:var(--green)}
.session-off-row .btn::before{content:"";position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);min-height:var(--tap-min)}
.session-off-row .empty.session-off{font:400 var(--text-md)/1.3 var(--font-ui);color:var(--ink-soft)}
.also-came{display:flex;flex-wrap:wrap;gap:var(--s3);margin:var(--s5) 0 0}
.also-came .cap{
  flex:1 0 100%;margin:0;padding:0 var(--s3);
  font:600 var(--text-xs)/1 var(--font-ui);letter-spacing:var(--track-mid);text-transform:uppercase;color:var(--gold);
}
.also-came .btn .st-grade{margin-left:.35em}

/* ==== BUILD: the Classes screen, menu and search ====================
   Issues 7 (version E), 31 (the menu's badges, the empty Afternoon Group) and
   19 (items 3 and 4). One block, so it merges on its own. Red means "not done
   yet" and nothing else (issue 7); it never stands alone: a count, a ring
   with a number, or words always come with it. */
:root{--not-done:#A8433C;--not-done-pale:#FBEDEB}

/* -- The Week's heading: ‹ THIS WEEK ›, centred, the arrows held still. */
.card-cap.week-cap{justify-content:center;align-items:center;gap:var(--s2);padding:var(--s5) 0}
.week-cap-words{display:inline-block;width:13em;text-align:center;white-space:nowrap;font-variant-numeric:tabular-nums}
.week-step{position:relative;display:grid;place-items:center;box-sizing:border-box;width:28px;height:28px;margin:-8px 0;
  border-radius:var(--r-full);color:var(--gold);text-decoration:none;transition:background-color .15s ease-out}
.week-step:first-child svg{transform:scaleX(-1)}
/* The arrow is drawn small; its press reaches 44px. */
.week-step::before{content:"";position:absolute;inset:-8px}
.week-step:hover{background:var(--green-pale)}
.week-step:active{background:var(--gold-line)}
.week-step:focus-visible{outline:2px solid var(--green);outline-offset:2px}

/* -- The circles: green filled is the real today only; a Day gone by is ringed. */
.wday{position:relative;text-decoration:none}
.wday[data-day="open"]{box-shadow:inset 0 0 0 3px var(--not-done)}
.wday[data-day="done"]{box-shadow:inset 0 0 0 2px var(--green-tick)}
.wday-left{position:absolute;top:-6px;right:-6px;box-sizing:border-box;min-width:22px;height:22px;padding:0 5px;
  display:grid;place-items:center;border-radius:var(--r-md);background:var(--not-done);color:var(--card);
  font:700 var(--text-sm)/1 var(--font-ui);font-variant-numeric:tabular-nums;box-shadow:var(--lift-soft)}
.week-back{display:flex;justify-content:center;margin:var(--s6) 0 0}

/* -- One line per unfinished Day, under the circles. */
.dayleft-list{display:grid;gap:var(--s2);margin-top:var(--s7)}
.dayleft{display:flex;align-items:center;gap:var(--s4);box-sizing:border-box;min-height:var(--tap-min);padding:var(--s3) var(--s6);
  border-radius:var(--r-sm);background:var(--card);color:var(--ink);text-decoration:none;
  box-shadow:inset 0 0 0 2px var(--not-done);transition:background-color .15s ease}
.dayleft:hover{background:var(--not-done-pale)}
.dayleft:focus-visible{outline:2px solid var(--not-done);outline-offset:2px}
.dayleft-day{font:400 var(--text-lg)/1.2 var(--font-display)}
.dayleft-parts{margin-left:auto;font:600 var(--text-sm)/1.2 var(--font-ui);font-variant-numeric:tabular-nums;white-space:nowrap}
.dayleft svg{flex:none;color:var(--not-done)}

/* -- All marked: a white tick inside the green dot. */
.grp .dot[data-mark="done"] i{width:16px;height:16px;background:var(--green) center/10px no-repeat
  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 22'%3E%3Cpath d='M4 11.5l5 5L18 6' fill='none' stroke='white' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")}

/* -- A card's name is read in full, never cut with "…": it wraps onto two
   lines, and the card grows to hold it (she has poor vision). The capped
   height also let a phone's meter run into the next card. */
.grp{max-height:none}
.grp-name{white-space:normal;overflow:visible;text-overflow:clip;overflow-wrap:normal}
@media (max-width:760px){
  /* Number, name and Open on top, Open held to the top right; then the meter. */
  .grp-id{flex:1 1 0}
  .grp-open{align-self:flex-start}
}

/* -- An Afternoon Group with no Students yet: grey, quiet, no meter. */
.grp[data-state="empty"]{background:var(--cream-2)}
.grp[data-state="empty"] .grp-name{color:var(--ink-soft)}
.grp[data-state="empty"] .grp-num{color:var(--ink-soft)}
.grp[data-state="empty"] .grp-num::after{background:var(--grey-line)}

/* -- Settings: the Desk's gear, the same round white button as Find a student. */
.gear-link{display:flex;align-items:center;justify-content:center;box-sizing:border-box;flex:none;
  width:var(--tap-min);height:var(--tap-min);border-radius:var(--r-full);background:var(--card);color:var(--green);
  box-shadow:var(--lift-quiet);transition:background-color .15s ease}
.gear-link:hover{background:var(--green-pale)}
.gear-link:focus-visible{outline:2px solid var(--green-tick);outline-offset:2px}
.gear-link svg{display:block}

/* -- The Calendar, home screen and Desk alike: an unfinished Day in red. */
.cal-day[data-open]{color:var(--not-done);font-weight:700;box-shadow:inset 0 0 0 2px var(--not-done)}
.cal-day[data-open] .cal-dot{background:var(--not-done)}
a.cal-day[data-open]:hover{background:var(--not-done-pale)}
/* Day cells stay the app's rounded squares, 48 by 48, at every width. */
.cal-day{height:var(--tap-min)}
/* The home screen's month is as wide as its five columns, centred over the Calendar button, like the Desk's. */
.card .cal-panel{left:50%;right:auto;width:max-content;transform:translateX(-50%)}
@media (max-width: 640px){
  .cal-row{grid-template-columns:repeat(5,var(--tap-min))}
  .cal-panel,.deskhead-right .cal-panel,.card .cal-panel{left:50%;right:auto;width:max-content;transform:translateX(-50%)}
}

/* -- Find a student: a Student who starts after the Day shown says so, on
   a line of its own under the name, so the name keeps its whole width. */
.found-row:has(.found-starts){flex-wrap:wrap;row-gap:var(--s1);padding-top:var(--s4);padding-bottom:var(--s4)}
.found-starts{order:3;flex:1 0 100%;font:600 var(--text-sm)/1.25 var(--font-ui);color:var(--ink-quiet)}
/* -- The Desk, when the Student asked for starts later: the one line, centred. */
.empty.starts-later,.empty.starts-later .linkish{font-size:var(--text-lg)}

/* -- The menu: each Class's name, then its badge for the Session shown. */
#nav .nav-item.has-badge{display:flex;flex-direction:row;align-items:center;justify-content:flex-start;gap:var(--s3);text-align:left}
#nav .nav-item.has-badge .nav-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}
.nav-badge{flex:none;display:inline-grid;place-items:center;box-sizing:border-box;min-width:22px;height:22px;
  font:700 var(--text-sm)/1 var(--font-ui);font-variant-numeric:tabular-nums}
.nav-badge[data-badge="left"]{padding:0 6px;border-radius:var(--r-md);background:var(--not-done);color:var(--card)}
.nav-badge[data-badge="done"]{color:var(--green-tick)}
.nav-badge[data-badge="noclass"]{font-weight:400;color:var(--ink-soft)}

/* -- Issue 19, item 4: the phone's menu bar. A word breaks only at a real
   hyphen point, or not at all. Every tab one text size (Sign out was
   larger). At most five tabs to a row: six sit three by two. */
@media (max-width:879px){
  #nav .nav-item{overflow-wrap:normal}
  #nav button.nav-item{font:400 var(--text-sm)/1.15 var(--font-ui);text-align:center}
  #nav:has(> a.nav-item:nth-of-type(5)){display:grid;grid-template-columns:repeat(3,minmax(0,1fr));grid-auto-rows:1fr}
  #nav:has(> a.nav-item:nth-of-type(5)) .nav-item{border-bottom:1px solid var(--gold-hair)}
  #nav:has(> a.nav-item:nth-of-type(5)) .nav-item:nth-of-type(3n){border-right:none}
  body:has(#nav > a.nav-item:nth-of-type(5)) #shell{padding-bottom:calc(var(--tap-min) * 2 + 104px)}
}
/* ==== end BUILD: the Classes screen, menu and search ====================== */

/* ---- Excused, one Session at a time (issues 8 and 9) ---------------------
   The Excuse button opens one question, "Which Session?", in the page's own
   flow: the panel, the name line or the Week card grows to hold it, so it is
   never cut off and never covered. The three answers stand stacked as the
   Surah | Lesson radios are, the Session she is marking already picked, and
   one Excuse under them. "No class" is said once, in a quiet banner above
   the Desk, with its Undo; "Keep them?" is a banner of its own with two big
   buttons. Every corner is a token; nothing is pinned. */
.excuse-form{display:contents}
.excuse-menu{display:block}
.excuse-menu > summary{list-style:none;cursor:pointer;display:flex;align-items:center;justify-content:center;
  box-sizing:border-box;min-height:var(--tap-min)}
.excuse-menu > summary::-webkit-details-marker{display:none}
.excuse-menu > summary::after{content:"";width:7px;height:7px;margin-left:var(--s3);
  border-right:1.6px solid currentColor;border-bottom:1.6px solid currentColor;transform:translateY(-2px) rotate(45deg)}
.excuse-menu[open] > summary::after{transform:translateY(2px) rotate(225deg)}
.excuse-panel{
  margin-top:var(--s3);box-sizing:border-box;padding:var(--s5);
  display:flex;flex-direction:column;gap:var(--s3);
  background:var(--card);border-radius:var(--r-sm);box-shadow:var(--edge-row),var(--lift-quiet);text-align:left;
}
.excuse-ask{margin:0;font:600 var(--text-xs)/1 var(--font-ui);letter-spacing:var(--track-mid);text-transform:uppercase;color:var(--gold)}
.excuse-panel .seg.seg-stack{margin:0;gap:var(--s2)}
.excuse-panel .seg.seg-stack label{font-size:var(--text-lg)}
.excuse-panel .btn.primary,.ask-banner .btn{min-height:var(--tap-min);border-radius:var(--r-sm)}
.card .excuse-day .excuse-form,.card .excuse-day .excuse-menu,.card .excuse-day .excuse-menu > summary{width:100%;box-sizing:border-box}
/* The Week card's quiet buttons (above) must not reach the one Excuse inside the question. */
.card .excuse-day .excuse-panel .btn.primary{
  height:auto;min-height:var(--tap-min);padding:0 var(--s6);border-radius:var(--r-sm);
  background:var(--green-deep);color:#FBF6E8;box-shadow:var(--lift-button);font-size:var(--text-lg);
}
.excuse-class .excuse-menu > summary,.excuse-class .excuse-form > .btn{width:100%}
/* Stacked, Excuse is as wide as Next under it; in one line, its own width. */
.entry-head .excuse-menu > summary.btn.chip{width:100%;padding:0 var(--s6)}
@container (min-width:740px){.entry-head .excuse-menu > summary.btn.chip{width:auto}}
/* Open in the name line, the question takes its whole width: the line takes
   its stacked form, and Next waits below the question. */
.entry-head:has(.excuse-menu[open]){display:grid;grid-template-columns:minmax(0,1fr)}
.entry-head:has(.excuse-menu[open]) .entry-actions{display:grid;grid-template-columns:minmax(0,1fr);align-items:start}
.entry-actions .excuse-menu[open]{grid-column:1 / -1}
.entry-actions .excuse-menu[open] > summary.btn.chip{width:100%}

.noclass-banner{
  display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--s3) var(--s6);
  margin:var(--s4) 0 var(--s6);padding:var(--s5) var(--s7);
  border-radius:var(--r-sm);background:var(--card);box-shadow:var(--edge-row);text-align:center;
}
.noclass-banner p{margin:0;font:400 var(--text-lg)/1.35 var(--font-display);color:var(--ink)}
.noclass-banner p b{font-weight:400;color:var(--green)}
.noclass-banner form{display:contents}
.noclass-banner .btn{width:auto;min-height:var(--tap-min);padding:0 var(--s7);border-radius:var(--r-sm)}

.ask-banner{
  display:flex;flex-direction:column;align-items:center;gap:var(--s5);
  margin:var(--s4) 0 var(--s6);padding:var(--s7);
  border-radius:var(--r-sm);background:var(--card);box-shadow:var(--edge-row),var(--lift-quiet);text-align:center;
}
.ask-banner p{margin:0;font:400 var(--text-lg)/1.45 var(--font-display);color:var(--ink)}
.ask-banner p b{font-weight:400;color:var(--green)}
.ask-buttons{display:flex;flex-wrap:wrap;justify-content:center;gap:var(--s4);width:100%}
.ask-buttons .btn{flex:1 1 220px;max-width:320px;font-size:var(--text-lg)}

.came-anyway .cap{color:var(--gold)}
.weekly-now{margin:var(--s2) 0 var(--s4);font:400 var(--text-lg)/1.3 var(--font-display);color:var(--green)}
@media (max-width:520px){
  .ask-buttons .btn{max-width:none}
  .entry-head .excuse-menu{width:100%}
  .entry-head .excuse-menu > summary.btn.chip{width:100%}
}

/* The Class's week as a timetable: the Day's name, then its Morning and
   Afternoon, every button one height and corner. "Excuse" is the quiet
   white of every button; "Excused" is the Desk's gold, as on the Desk,
   and says so in words, never by colour alone. Under the Day's name, a
   small gold note says which Session is left. */
.wk-grid{
  display:grid;grid-template-columns:auto minmax(0,1fr) minmax(0,1fr);align-items:center;
  gap:var(--s2) var(--s3);max-width:560px;
}
.wk-head{text-align:center;font:600 var(--text-sm)/1 var(--font-ui);color:var(--ink-soft)}
.wk-day{padding-right:var(--s3);font:400 var(--text-lg)/1 var(--font-display);color:var(--green)}
.wk-note{display:block;margin-top:var(--s1);font:600 var(--text-sm)/1.2 var(--font-ui);color:var(--gold)}
.wk-grid button{
  box-sizing:border-box;min-width:0;height:var(--tap-min);margin:0;padding:0 var(--s3);
  border:0;border-radius:var(--r-sm);cursor:pointer;
  background:var(--card);box-shadow:var(--edge-row);
  font:400 var(--text-lg)/1 var(--font-display);letter-spacing:var(--track-tight);color:var(--green);
}
.wk-grid button:hover{background:var(--green-pale)}
.wk-grid button[aria-pressed="true"]{
  background:color-mix(in srgb, var(--gold) 9%, var(--card));color:var(--gold);
  box-shadow:inset 0 0 0 1.5px color-mix(in srgb, var(--gold) 45%, transparent);
}
.wk-grid button[aria-pressed="true"]:hover{background:color-mix(in srgb, var(--gold) 16%, var(--card))}
.wk-grid button:focus-visible{outline:2px solid var(--green-tick);outline-offset:2px}
.weeklycard .fieldnote{margin-top:var(--s4)}

/* ---- The Groups screen (/groups, issue 5) ---------------------------------
   All group work in one place: the Grades 1–3 boxes, then the Afternoon
   Groups' boxes, every box the same shape. A box is a card with its edge
   drawn inside it; its name and count sit centred on its axis, its names run
   down in rows, and its two buttons stand level at its foot. Every box is
   one width, and a row of them is centred on the page's axis; every box in
   a row takes the row's tallest height, its buttons at its foot, so they line up. Names wrap at a
   space, never cut. */
#s-groupwork .sub{margin-bottom:var(--s5)}
.gw-how{margin:0 0 var(--s6);font:400 var(--text-md)/1.5 var(--font-ui);color:var(--ink-soft);text-align:center}
.gw-said{margin:0 0 var(--s5);text-align:center;font:600 var(--text-md)/1.4 var(--font-ui);color:var(--green)}
.gw-h2{margin:var(--s9) 0 var(--s3);text-align:center}
.gw-h2:first-of-type{margin-top:var(--s6)}
.gw-boxes{
  display:flex;flex-wrap:wrap;justify-content:center;
  gap:var(--s7);align-items:stretch;margin:0 0 var(--s6);
}
.gw-box{
  display:flex;flex-direction:column;flex:0 1 360px;min-width:0;box-sizing:border-box;
  padding:var(--s6);border-radius:var(--r-sm);background:var(--card);box-shadow:var(--edge-row);
  scroll-margin-top:calc(var(--s9) * 2);
}
.gw-box-nogroup{background:#FAF3E2}
.gw-box.is-target{box-shadow:inset 0 0 0 2px var(--green-tick);background:var(--green-pale)}
.gw-head{display:flex;flex-direction:column;align-items:center;gap:var(--s1);text-align:center;margin-bottom:var(--s4)}
.gw-name,.gw-name h3{margin:0;font:400 var(--text-xl)/1.25 var(--font-display);color:var(--green);
  text-transform:none;letter-spacing:var(--track-tight);overflow-wrap:break-word}
.gw-rename{width:100%}
.gw-rename > summary{list-style:none;cursor:pointer;display:flex;justify-content:center;align-items:center;
  min-height:var(--tap-min);border-radius:var(--r-sm);padding:0 var(--s4)}
.gw-rename > summary::-webkit-details-marker{display:none}
.gw-rename > summary:hover{background:var(--green-pale)}
.gw-rename > summary:focus-visible{outline:2px solid var(--green-tick);outline-offset:2px}
.gw-rename > summary h3{text-decoration:underline;text-decoration-color:var(--gold-line);text-underline-offset:5px}
.gw-rename[open] > summary{display:none}
.gw-rename-form{display:grid;grid-template-columns:1fr 1fr;gap:var(--s3);width:100%}
.gw-rename-form input{grid-column:1 / -1;min-width:0;text-align:center}
.gw-rename-form .btn,.gw-add-form .btn{display:flex;align-items:center;justify-content:center;box-sizing:border-box;
  min-height:var(--tap-min);padding:var(--s3) var(--s4);border-radius:var(--r-sm);font-size:var(--text-lg)}
.gw-name-fixed{min-height:var(--tap-min);display:flex;align-items:center;justify-content:center}
.gw-count{font:400 var(--text-md)/1.3 var(--font-ui);color:var(--ink-soft);font-variant-numeric:tabular-nums}

.gw-list{list-style:none;margin:0 0 var(--s5);padding:0}
.gw-item{
  display:flex;align-items:center;gap:var(--s4);min-height:var(--tap-min);box-sizing:border-box;
  padding:var(--s2) var(--s4) var(--s2) var(--s2);border-bottom:1px solid var(--gold-hair);
  font:400 var(--text-lg)/1.3 var(--font-display);color:var(--ink);
  touch-action:pan-y;user-select:none;-webkit-user-select:none;
}
.gw-item:last-child{border-bottom:none}
.gw-who{flex:1 1 auto;min-width:0;overflow-wrap:break-word}
.gw-meta{flex:none;font:400 var(--text-sm)/1.2 var(--font-ui);color:var(--ink-soft);white-space:nowrap}
.gw-grip{
  flex:none;display:inline-flex;align-items:center;justify-content:center;
  width:var(--tap-min);height:var(--tap-min);margin:calc(-1 * var(--s2)) 0;color:var(--gold);
  border-radius:var(--r-sm);cursor:grab;touch-action:none;
}
.gw-item:hover .gw-grip{background:var(--green-pale);color:var(--green)}
.gw-empty{margin:0 0 var(--s5);padding:var(--s5) var(--s4);text-align:center;
  font:400 var(--text-md)/1.45 var(--font-ui);color:var(--ink-soft)}
.gw-hint{margin:0;text-align:center;font:400 var(--text-md)/1.45 var(--font-ui);color:var(--ink-soft)}

/* The foot: "Choose students" and "Remove", two equal buttons on one line. */
/* The two buttons are one height, each on one line. Where a box is too
   narrow for both side by side, they stand one under the other, each the
   full width, never one wrapped onto two lines beside the other. */
.gw-foot{margin-top:auto;display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,9.5rem),1fr));gap:var(--s3);align-items:stretch}
.gw-foot > details:not([open]) > summary.btn,
.gw-foot > form > .btn{height:100%}
.gw-foot > form{display:grid}
.gw-foot:has(> .gw-choose[open]) > .gw-remove,.gw-foot:has(> .gw-ask) > .gw-choose,.gw-foot > .gw-choose:only-child{grid-column:1 / -1}
.gw-foot > .gw-choose[open]{grid-column:1 / -1}
.gw-foot .btn{
  display:flex;align-items:center;justify-content:center;box-sizing:border-box;width:100%;
  min-height:var(--tap-min);padding:var(--s3) var(--s2);border-radius:var(--r-sm);font-size:var(--text-lg);line-height:1.15;
  white-space:nowrap;
}
.gw-foot > .gw-hint{grid-column:1 / -1}
.gw-foot form{margin:0}
.gw-choose > summary{list-style:none}
.gw-choose > summary::-webkit-details-marker{display:none}
.gw-choose[open] > summary{background:var(--green-pale)}
.gw-ticks{margin-top:var(--s4)}
.gw-ticklist{list-style:none;margin:0;padding:0}
label.gw-tick{
  display:flex;align-items:center;gap:var(--s4);margin:0;min-height:var(--tap-min);box-sizing:border-box;
  padding:var(--s2) var(--s4);border-bottom:1px solid var(--gold-hair);cursor:pointer;
  text-transform:none;letter-spacing:0;font:400 var(--text-lg)/1.3 var(--font-display);color:var(--ink);
}
.gw-ticklist li:last-child label.gw-tick{border-bottom:none}
label.gw-tick:hover{background:var(--green-pale)}
label.gw-tick:focus-within{outline:2px solid var(--green-tick);outline-offset:-2px}
label.gw-tick input{flex:none;width:24px;height:24px;margin:0;accent-color:var(--green);cursor:pointer}
.gw-tick-who{flex:1 1 auto;min-width:0;overflow-wrap:anywhere}
.gw-tick-note{flex:none;max-width:45%;text-align:right;font:400 var(--text-sm)/1.25 var(--font-ui);color:var(--ink-soft)}
.gw-tick-note:is(:not(:empty)){overflow-wrap:anywhere}
.gw-ticks .gw-save{margin-top:var(--s4)}
/* With the script running every tick saves itself, so there is no Save. */
.gw-js .gw-save{display:none}
[data-groupwork].saving{opacity:.6;transition:opacity .15s ease}

/* "Are you sure?", in the box's own foot. */
.gw-ask{grid-column:1 / -1;display:flex;flex-direction:column;gap:var(--s4);text-align:center;
  padding:var(--s5);border-radius:var(--r-sm);box-shadow:var(--edge-row);background:#FAF3E2}
.gw-ask p{margin:0;font:400 var(--text-lg)/1.4 var(--font-display);color:var(--ink)}
.gw-ask-buttons{display:grid;gap:var(--s3)}

/* "Add a group": one button centred under the boxes; open, a name box in its place. */
.gw-add{display:flex;flex-direction:column;align-items:center;margin:0 auto var(--s6);max-width:420px}
.gw-add > summary{
  list-style:none;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:var(--s3);
  min-height:var(--tap-min);box-sizing:border-box;padding:var(--s3) var(--s8);border-radius:var(--r-lg);
  background:var(--card);box-shadow:var(--edge-row);
  font:400 var(--text-lg)/1.2 var(--font-display);letter-spacing:var(--track-tight);color:var(--green);
}
.gw-add > summary::-webkit-details-marker{display:none}
.gw-add > summary:hover{background:var(--green-pale)}
.gw-add > summary:focus-visible{outline:2px solid var(--green-tick);outline-offset:2px}
.gw-plus{display:inline-flex;align-items:center;justify-content:center;flex:none}
.gw-add[open] > summary{display:none}
.gw-add-form{display:flex;flex-direction:column;gap:var(--s3);width:100%;box-sizing:border-box;
  padding:var(--s6);border-radius:var(--r-sm);background:var(--card);box-shadow:var(--edge-row)}
.gw-add-form label{margin:0;text-align:center}
.gw-add-form input{text-align:center}


/* A name being dragged: a copy follows the finger, the row it left is faint. */
.gw-item.is-dragging{opacity:.35}
.gw-ghost{
  position:fixed;z-index:50;pointer-events:none;box-sizing:border-box;
  border-radius:var(--r-sm);background:var(--card);box-shadow:var(--edge-row),var(--lift-quiet);
}

/* "3 students have no group yet": at the top of the Classes and Groups screens. */
.nogroup-line{
  display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--s2) var(--s5);
  min-height:var(--tap-min);box-sizing:border-box;margin:var(--s4) 0 var(--s6);padding:var(--s3) var(--s6);
  border-radius:var(--r-sm);background:#FAF3E2;box-shadow:var(--edge-row);
  text-align:center;text-decoration:none;color:var(--ink);
}
a.nogroup-line:hover{background:var(--green-pale)}
a.nogroup-line:focus-visible{outline:2px solid var(--green-tick);outline-offset:2px}
.nogroup-words{font:400 var(--text-lg)/1.3 var(--font-display);color:var(--gold)}
.nogroup-go{display:inline-flex;align-items:center;gap:var(--s2);font:600 var(--text-md)/1.2 var(--font-ui);color:var(--green)}
.nogroup-go svg{flex:none}
@media (max-width:520px){
  .gw-box{flex-basis:100%}
}

/* ---- The Students screen (issue 15) ---------------------------------------
   One school-wide list and one page per Student. Headings, actions and the
   jump links sit on the page's centred axis, as on the Groups screen; the
   rows are the Class Settings rows; the form is one column of the app's own
   boxes. Every size, colour and corner comes from a token. */
:is(#s-students,#s-students-left,#s-student,#s-student-left) :is(h1,.sub,h2){text-align:center}
.st-actions{
  display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:center;gap:var(--s5) var(--s7);
  margin:var(--s7) 0 var(--s5);
}
.st-actions > .btn,.st-find .btn{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box}
.st-find label{margin:0 0 var(--s3);text-align:left}
.st-find-line{display:flex;gap:var(--s3)}
.st-find-line input{flex:1 1 auto;min-width:0;width:calc(var(--tap-min) * 5)}
.st-jump{
  display:flex;flex-wrap:wrap;justify-content:center;gap:var(--s2) var(--s3);
  margin:0 0 var(--s5);
}
.st-jump a{
  display:inline-flex;align-items:center;min-height:var(--tap-min);box-sizing:border-box;padding:0 var(--s5);
  font:400 var(--text-md)/1.2 var(--font-ui);color:var(--green);
  text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--gold-line);
}
.st-jump a:hover{text-decoration-color:var(--green-tick)}
.st-class{scroll-margin-top:var(--s7)}
.st-class .row{scroll-margin-top:var(--s9)}
.st-none,.st-nomatch,.st-foot,.st-leave{text-align:center}
.st-nomatch{margin:var(--s8) 0;font:400 var(--text-lg)/1.5 var(--font-ui);color:var(--ink)}
.st-foot{margin:var(--s8) 0 0}
.st-foot .linkish,.st-nomatch .linkish,.st-tap{display:inline-flex;align-items:center;min-height:var(--tap-min)}
.st-empty,.st-confirm,.st-leftbefore,.st-gone{text-align:center}
.st-empty .btnrow,.st-confirm .btnrow,.st-leftbefore form,.st-gone form,#s-student .btnrow{justify-content:center}
.st-empty-title{margin:0;font:400 var(--text-xl)/1.3 var(--font-display);color:var(--green)}
.st-leftbefore p,.st-gone p{margin:0 0 var(--s5);font:400 var(--text-lg)/1.5 var(--font-ui);color:var(--ink)}
.st-leftbefore form,.st-gone form{display:flex}
.st-confirm p{font:400 var(--text-lg)/1.5 var(--font-ui);color:var(--ink)}

/* One Student's page: one column on the page's axis. */
.st-form,.st-leftbefore,.st-gone,.st-confirm,.st-empty{max-width:calc(var(--tap-min) * 12);margin-left:auto;margin-right:auto;box-sizing:border-box}
.st-call + label{margin-top:var(--s2)}
.st-form{display:flex;flex-direction:column;container:st-form / inline-size}
.st-form > label:first-child{margin-top:0}
.st-form .minilab{margin:var(--s7) 0 var(--s3)}
.st-form h2{margin:var(--s8) 0 0;text-align:center}
.st-form input[type=date],.st-form input[type=tel]{
  width:100%;box-sizing:border-box;font:400 var(--text-lg)/1.3 var(--font-ui);color:var(--ink);
  min-height:var(--tap-min);padding:var(--s4) var(--s6);
  border:0;box-shadow:var(--edge-row);border-radius:var(--r-sm);background:var(--card);
  font-variant-numeric:tabular-nums;
}
.st-form input:focus-visible{outline:2px solid var(--green-tick);outline-offset:2px}
.st-form .fieldnote{margin:var(--s3) 0 0}
/* "Afternoon with" does not matter to a Student who comes in the Morning only:
   hidden, not removed, so the select still posts and the stored group is kept. */
.st-form:has(input[name="attends"][value="morning"]:checked) .st-afternoon-with{display:none}
.st-grades{display:grid;grid-template-columns:repeat(9,minmax(0,1fr));gap:var(--s2)}
.st-grades:has(.st-notset){grid-template-columns:repeat(5,minmax(0,1fr))}
.st-grades .st-notset{grid-column:span 2}
.st-grades label{box-sizing:border-box;min-height:var(--tap-min);padding:0 var(--s1)}
/* Nine in a row needs 9 × 48px and 8 gaps of 7px: 488px. Narrower, three rows of three. */
@container st-form (max-width:487px){
  .st-grades,.st-grades:has(.st-notset){grid-template-columns:repeat(3,minmax(0,1fr))}
  .st-grades .st-notset{grid-column:1 / -1}
}
.st-parent{display:flex;flex-direction:column}
.st-form .st-parent-lab{margin:var(--s7) 0 0;color:var(--ink)}
.st-parent > label:first-child{margin-top:var(--s5)}
.st-call{align-self:flex-start;display:inline-flex;align-items:center;min-height:var(--tap-min)}
.st-parent2{margin:var(--s7) 0 0}
.st-parent2 > summary{display:flex;align-items:center;justify-content:center;box-sizing:border-box;width:100%;cursor:pointer}
.st-parent2[open] > summary{display:none}
.st-form .btnrow{margin:var(--s8) 0 0}
.st-form .btnrow .btn{min-height:var(--tap-min)}
.st-leave{margin:var(--s9) 0 0}
.st-leave .btn{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box}
#s-students-left .row .btn{min-height:var(--tap-min);display:inline-flex;align-items:center}
@media (max-width:640px){
  .st-actions{flex-direction:column;align-items:stretch}
  .st-find-line input{width:auto}
  .st-form .btnrow .btn,.st-empty .btnrow .btn{flex:1 1 100%}
}
