/* ══════════════════════════════════════════════════════════════════════════════
   Planbook — the attendance registry (WO-2.1): one class, students down, the last
   six weekdays across.

   The second per-screen stylesheet, following the rule src/shell.css's header sets
   and src/home.css instanced first: one file per screen, loaded after the shell's,
   styling only its own class names, with its own @media (pointer: coarse) block at
   the end. No selector below names anything shell.css or home.css styles — the
   controls here that ARE shell components (`.class-action-btn`, `.pill`,
   `.search-box`, `.panel*`) are worn as-is and never restyled from here, and the
   three places this screen needs something shell.css has no name for get names of
   their own (`.attendance-toggle-on`, `.attendance-panel`, `.attendance-body`).

   WHERE THIS SCREEN SITS CHANGED AT WO-1.13 and none of the values below did. It
   was a dialog over the home screen and is now `#classView` in <main>, so the frame
   is `.panel` / `.panel-header` / `.panel-title` instead of `.modal-panel` /
   `.modal-header` / `.modal-body` — which is why the four <p> rules further down are
   qualified by `.attendance-body` where they used to say `.modal-body`. Same
   specificity, same cascade posture, same screen.

   THE THREE STATES HAVE THREE PALETTES, and they are chosen rather than picked:

     TAKEN         positive green wash (#eafaf1 / #a3e4bc / #27ae60). Done.
     NOT TAKEN YET the caution wash the two strips at the top of the page use
                   (#fff8e6 / #f0dfa8 / #8a6d1a), which in this app means "act on
                   this" — and at 8:47am that is exactly what an untaken class is.
     DIDN'T MEET   quiet slate on a DASHED hairline, the same grammar
                   `.class-row.archived` uses in shell.css for a class the teacher
                   set aside. A dropped class is settled: there is nothing to do.

   AND EACH ONE IS SAID TWICE — in the column head, in words, and again as a wash
   down the whole column and a glyph in every cell under it. That is not belt and
   braces. In a grid an empty cell means "present" on a day that was taken and "no
   data at all" on a day that wasn't, and if those two look alike the teacher reads
   a forgotten Tuesday as a Tuesday when everybody showed up. So no cell is ever
   empty: `P` on the green wash, `?` on the amber, `–` on the dashed grey. An
   untaken column is a stripe you see from across the room, which is the whole
   answer to "which day did I forget?".

   TODAY AND AN UNLOCKED PAST DAY are marked differently on purpose. Today gets a
   pale rail down each side and an indigo date — it is where you are, not something
   you chose. An unlocked past column gets a solid indigo rail, and the banner above
   the grid says which day in words, because "you are not on today" has to survive
   being read at arm's length in a lit classroom.

   The two things that read like mistakes here are the same two that read like
   mistakes in shell.css and home.css: colours are written out inline rather than as
   custom properties, and there is no dark variant of anything. Both are deliberate
   suite conventions — read CLAUDE.md before tidying either one.
   ══════════════════════════════════════════════════════════════════════════════ */

/* One grouped selector for this sheet's tappable classes, the way shell.css has one for the app
   and for the reason home.css states: a control only covered by another selector's rule is a
   control the next reader has to go and prove is covered. */
.attendance-cell, .attendance-day-btn, .attendance-sort-btn, .attendance-toggle-on,
.attendance-detail-btn, .attendance-pass-btn, .attendance-pass-card-btn, .attendance-keys-btn {
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
}

/* ── THE PANEL ── */
/* A modifier on shell.css's `.panel`, which is the main area's frame — it was a modifier on
   `.modal-panel` until WO-1.13 moved this screen out of a dialog, and the number below is the one
   thing about it that did not change.

   IT WAS 720px UNTIL 2026-08-06, AND THE OWNER OVERRULED THAT. The argument for the cap was real
   and is kept here because it is the thing being traded away: six columns of 54px are 324px, and
   every pixel past what the names need is empty floor between a student's name and the cell you
   are aiming at on their row — a longer eye-track and a longer thumb-track on the one screen where
   both are counted in seconds.

   What the argument missed is that 720 was a DIALOG's width, and it survived the move out of the
   dialog unchanged. Sitting in a 1300px `.main` it does not read as a wide panel, it reads as a
   window that never closed — which is exactly what the owner reported, twice, having come from
   Roll Call!'s registry view, which fills its main area. `plans/work-orders/README.md` says a work
   order that contradicts the roadmap is wrong; the same goes for a stylesheet that contradicts the
   person using it every period.

   Do not put the cap back without asking her. If the row ever does feel too wide, the fix is a
   wider name column or a `max-width` in the four figures — not a return to dialog dimensions. Note
   also that WO-2.8's hall-pass column takes ~160px of this width the moment it lands, which is
   width that does not exist under a 720px cap.

   `max-width: 100%` rather than the 95vw a centred dialog needed: this sits inside `.main`, which
   already carries the page gutters and the safe-area insets. */
.attendance-panel { width: 100%; max-width: 100%; }

/* The body of that panel, under `.panel-header` — the padding `.modal-body` used to supply, in
   this screen's own name because two stylesheets never style one class (src/shell.css's header).
   Its left and right match `.panel-header`'s 20px and `.home-grid`'s, so the toolbar, the grid and
   the panel title all line up on one edge; the top is zero because the header above it already
   ends in a gap. */
.attendance-body { padding: 0 20px 20px; }

/* ── "YOU ARE NOT ON TODAY" ── */
/* A band, not a tint. The acceptance line is that this is visible in a glance, on an iPad, in a
   classroom, and a tint is exactly the fine print this screen keeps refusing. Interactive indigo
   rather than caution amber: being on a past day is a state the teacher CHOSE, and amber in this
   app means "something needs doing". */
.attendance-body .attendance-banner {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  margin: 0 0 10px; padding: 10px 14px;
  border: 1.5px solid #b0bcf0; border-left: 5px solid #5b6fcc; border-radius: 8px;
  background: #eef2ff; color: #3a4a8a;
  font-size: 14px; font-weight: 700; line-height: 1.4;
}
.attendance-banner-text { flex: 1 1 220px; }
.attendance-banner-btn { flex: 0 0 auto; }

/* ── THE STATE LINE ── */
/* Qualified by the container rather than left bare, which is the posture `.roster-hint` and
   `.class-error` take in shell.css — the qualifier was `.modal-body` until WO-1.13 and is this
   screen's own body now. It describes the ONE day that accepts edits, which is today unless a past
   column has been unlocked. */
.attendance-body .attendance-state {
  display: block; margin: 2px 0 10px;
  padding: 8px 12px; border-radius: 8px;
  border: 1.5px solid #eef0f4; background: #f8f9fb;
  font-size: 13px; font-weight: 700; color: #6b7a8d; line-height: 1.4;
}
.attendance-body .attendance-state.taken { background: #eafaf1; border-color: #a3e4bc; color: #27ae60; }
.attendance-body .attendance-state.not-taken { background: #fff8e6; border-color: #f0dfa8; color: #8a6d1a; }
/* Started and not finished (WO-2.10) — the caution wash over the taken one, on source order, for
   the reason src/home.css gives at the same rule on the card: a class holding `U`s is a meeting
   with an absence for every student nobody reached, and it must not read as done. */
.attendance-body .attendance-state.unconfirmed { background: #fff8e6; border-color: #f0dfa8; color: #8a6d1a; }
/* Dashed, for the reason given in the header: this is the "set aside" grammar the class manager
   already uses, and it is the half of the distinction that survives being read at arm's length. */
.attendance-body .attendance-state.dropped { background: #fff; border-style: dashed; border-color: #d0d8e4; color: #6b7a8d; }
/* THE FOURTH STATE (WO-2.3) — the calendar says nobody meets. The dropped grey made SOLID and one
   shade deeper, which is the whole of the distinction and it is deliberate rather than timid.

   No new hue: a holiday is not an alarm, and the amber beside it already means "you have not done
   this yet". What is different about a covered day is not the mood, it is WHERE THE UNDO LIVES —
   dropped is taken back with the ↩ on this screen, covered is taken back on the days-off panel — so
   the thing that has to carry the difference is the WORD, and the fill is what makes the two look
   like two states rather than one rendering glitch. Roll Call!'s own no-school column is a filled,
   greyed, italic head (dashboard.html:573) against nothing dashed at all; this is that distinction
   with Planbook's dropped column, which Roll Call! does not have, kept on the other side of it. */
.attendance-body .attendance-state.covered { background: #f5f7fa; border-color: #d0d8e4; color: #6b7a8d; }
.attendance-body .attendance-totals {
  margin: -4px 0 10px; color: #6b7a8d; font-size: 12px; font-weight: 700; line-height: 1.4;
}

/* ── THE CLASS-LEVEL CONTROLS ── */
/* "Everyone's here" and "Didn't meet", side by side and wrapping rather than squeezing — the same
   call `.roster-actions` makes over the same kind of row. */
.attendance-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }

/* THE DOOR TO THE CALENDAR, HELD AWAY FROM THE CONTROLS THAT WRITE. src/attendance.js's
   daysOffDoor() carries the reasoning: the three buttons to its left act on the day on screen and
   are aimed at with a class walking in, this one opens a dialog and changes nothing. `margin-left:
   auto` puts the row's spare width between them, so the separation is real on a laptop and on an
   iPad in landscape rather than a convention a reader has to know about.

   THE ROW WRAPS, AND THAT IS WHY THE MARGIN IS ALL THIS RULE DOES. On a narrow window the door
   wraps to a line of its own — still last, still apart, and with a whole line's gap instead of a
   flexible one. A fixed offset would have had to be undone at that width; the auto margin is
   already correct there.

   `:not(:only-child)` because a covered day draws this button and NOTHING ELSE. There is nothing to
   be held away from there, and an auto margin would have parked a lone control against the right
   edge of the panel with the sentence explaining it starting at the left. */
.attendance-actions-door:not(:only-child) { margin-left: auto; }

/* The pressed state of "Everyone's here", which shell.css has no variant for: `.primary` is a
   call to action and this is a fact about where you already are. Interactive wash + tinted border,
   the same answer `.toggle-btn.active` gives to the same question — but on the action button's own
   shape, so the two controls in this row stay one row of one kind of thing.
   `:hover:not(:disabled)` matches `.class-action-btn`'s own hover specificity exactly, so this
   sheet wins on load order rather than by being more specific than it needs to be. */
.attendance-toggle-on { background: #eef2ff; border-color: #b0bcf0; color: #5b6fcc; }
.attendance-toggle-on:hover:not(:disabled) { background: #dfe6ff; border-color: #5b6fcc; color: #5b6fcc; }

/* Two <p>s inside the panel body, qualified for the reason `.attendance-state` is. The note is
   what a dropped day says instead of a list of students; the hint is the standing sentence about
   what this screen stores and how the cycle runs, and it is the answer to both "why is nobody
   marked present?" and "why doesn't this start with P?" */
.attendance-body .attendance-note {
  margin: 0 0 10px; padding: 8px 12px; border-radius: 8px;
  background: #f8f9fb; border: 1.5px solid #eef0f4;
  font-size: 12px; line-height: 1.5; color: #6b7a8d;
}
/* WHY THE PASS BUTTONS ARE OFF (WO-2.8). The caution wash rather than the note's grey: three
   students are out of the room, and the sentence that says so is a thing to act on rather than a
   standing explanation. It sits directly under the action row, above the toolbar, so it is between
   the teacher's eye and the grid she is about to find a greyed-out button in. */
.attendance-body .attendance-pass-note {
  margin: 0 0 10px; padding: 8px 12px; border-radius: 8px;
  background: #fff8e6; border: 1.5px solid #f0dfa8;
  font-size: 12px; font-weight: 700; line-height: 1.5; color: #8a6d1a;
}
.attendance-body .attendance-hint { font-size: 11px; color: #8a9bb0; margin-top: 12px; line-height: 1.6; }
.attendance-body .attendance-empty { font-size: 13px; color: #a0aab8; padding: 10px 2px; line-height: 1.5; }

/* ── SEARCH, FILTER, SORT ── */
/* Roll Call!'s `.search-row` un-centred: over there the pill row is absolutely positioned in the
   middle of a full-width panel, which is a layout that only works when the row has a middle to
   spare. In a dialog the three groups sit in flow and wrap, and shell.css's `.search-box` and
   `.pill` are worn exactly as they are. */
.attendance-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 10px; }
.attendance-pills { display: flex; flex-wrap: wrap; gap: 5px; }
.attendance-sort { display: flex; align-items: center; gap: 4px; margin-left: auto; }
.attendance-sort-label {
  font-size: 10px; font-weight: 700; color: #8a9bb0;
  letter-spacing: 0.8px; text-transform: uppercase;
}
/* THE NAME AND THE TERM LINE UNDER IT, AND SINCE WO-2.6 THEY ARE A BUTTON — the door into that
   student's history, which is Roll Call!'s `.s-name-link` arrangement (dashboard.html: an onclick on
   the name that opens the student report) with the target grown to the whole identity block.

   Growing it is what keeps the row the height it was. A 44px minimum on the name ALONE would push
   the term line down and take the row from 62px to 75 on a coarse pointer — twenty-six students'
   worth of extra scrolling on the one screen measured in seconds. The block is 31px of text
   already, so 44 on the block is 13px of padding and the row is still decided by the 44px cells to
   its right. The arithmetic is restated at the coarse rule.

   The reset is the usual button reset and one line of it is load-bearing: `font: inherit`. A button
   does not inherit type, and without it the name inside would drop to the UA's 13.33px Arial while
   `.attendance-name` above says 13px/600 — which reads as a rendering fault rather than as a
   control. */
.attendance-student-identity {
  display: flex; flex-direction: column; min-width: 0;
  width: 100%; margin: 0; padding: 0;
  border: 0; background: none; font: inherit; color: inherit;
  text-align: left; cursor: pointer;
}
/* Roll Call!'s `.s-name-link:hover { color: #5b6fcc }`, on the name rather than on the whole block:
   the term line under it is grey on purpose and a hover that lit both up would read as a selection.
   The coarse block adds the underline that stands in for a hover nobody has on a touch screen,
   which is that stylesheet's own answer at the same selector. */
.attendance-student-identity:hover .attendance-student-name { color: #5b6fcc; }
.attendance-student-totals {
  color: #8a9bb0; font-size: 10px; font-weight: 600; line-height: 1.35; white-space: normal;
}
.attendance-detail-totals { color: #6b7a8d; font-size: 12px; font-weight: 700; line-height: 1.45; }
/* Roll Call!'s `.sort-btn`, under a name of this app's own because shell.css never lifted that
   one — the first screen with something to sort is what instances it, and this is it. */
.attendance-sort-btn {
  padding: 3px 9px; border-radius: 6px;
  border: 1.5px solid #e0e4ea; background: #fff; color: #8a9bb0;
  font-size: 10px; font-weight: 700; cursor: pointer; transition: all 0.15s; white-space: nowrap;
}
.attendance-sort-btn:hover { border-color: #5b6fcc; color: #5b6fcc; }
.attendance-sort-btn.active { background: #5b6fcc; border-color: #5b6fcc; color: #fff; }

/* ── THE PAGER ── */
/* Earlier · range · Today · Later, immediately above the columns it moves. "Later" sits disabled
   at the window that ends today rather than disappearing: a control that is there and greyed says
   there is no tomorrow to go to, and a control that is absent says nothing at all. */
.attendance-pager { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 6px; }
.attendance-pager-range {
  font-size: 11px; font-weight: 700; color: #8a9bb0; margin-right: auto;
  font-variant-numeric: tabular-nums;
}

/* ── THE GRID ── */
/* `overflow-x: auto` is a SAFETY VALVE and not the design. src/attendance.js draws fewer day
   columns on a narrow viewport — and exactly one in portrait, since WO-2.12 — precisely so this
   never engages; it is here because a table whose fixed columns
   outgrow their box would otherwise put a horizontal scrollbar on the whole page, and a page that
   scrolls sideways on an iPad is a teacher swiping the app instead of the list. Vertical scrolling
   still belongs to the overlay alone — one surface, one flick — which is why there is no
   max-height here. */
.attendance-grid-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
/* LIFTED FROM ROLL CALL!'s `table` — `width: 100%; border-collapse: collapse;` and NOTHING ELSE.
   No `table-layout: fixed`, and that omission is the whole point.

   We had fixed layout with hard widths, and it is what produced every spacing complaint on this
   screen. Under fixed layout a column with no width absorbs all the slack, so the name column ate
   ~988px of a 1300px panel and the cells huddled right; capping the name column then just moved the
   arbitrary number somewhere else. Auto layout is what the source material uses, columns declare a
   `min-width` and the browser distributes the rest by content. Stop computing these by hand. */
.attendance-grid { width: 100%; border-collapse: collapse; }

/* Roll Call!'s `thead th`: `padding: 9px 10px`, 10px/700/#8a9bb0 uppercase, 0.5px tracking, a 1px
   #eef0f4 rule under it, nowrap — and the Student column's own `min-width: 170px` from its
   `renderHead()`. Ours ran 6px 8px 6px 2px with 0.8px tracking and a 1.5px rule, which is why the
   two screens read differently at a glance even where the structure matched. */
.attendance-corner {
  min-width: 170px;
  text-align: left; padding: 9px 10px;
  font-size: 10px; font-weight: 700; color: #8a9bb0;
  letter-spacing: 0.5px; text-transform: uppercase;
  border-bottom: 1px solid #eef0f4; white-space: nowrap;
}
/* Roll Call!'s `tbody td`: `padding: 9px 10px; vertical-align: middle`. The row carries the rule,
   not the cell — see the `tbody tr` rule further down. */
.attendance-name {
  text-align: left; padding: 9px 10px; vertical-align: middle;
  font-size: 13px; font-weight: 600; color: #1a1a2e;
  white-space: nowrap;
}
/* Roll Call!'s `.student-cell`: `display: flex; align-items: center; gap: 9px`. The avatar inside
   is shell.css's `.avatar` — 32px, round, `flex-shrink: 0` — worn as-is, because two stylesheets
   never style one class (src/shell.css's header). This sheet owns the row around it and nothing in
   it. */
/* `.attendance-student-cell` IS STILL NOT A TOUCH TARGET — it is the flex wrapper, and what it
   holds is an avatar, the identity button and the ⋯. Both of those carry their own 44px in the
   block at the foot of this file. (Until WO-2.6 nothing in this cell was tappable at all, and the
   note that stood here said so; the identity block is what changed, not this wrapper.) */
.attendance-student-cell { display: flex; align-items: center; gap: 9px; }
/* The name is what gives way when the column is tight, never the avatar: a 32px circle that
   shrinks to 20px reads as a rendering fault, while a truncated name reads as a truncated name and
   keeps its full text on the cell's `title`.

   `white-space: nowrap` is restated here rather than inherited from `.attendance-name` since
   WO-2.6: the ellipsis needs it, and the element is inside a <button> now — a form control is
   where an inherited white-space value is least safe to assume across engines. */
.attendance-student-name {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  transition: color 0.1s;
}

/* ── THE PASSES COLUMN (WO-2.8) ── */
/* Roll Call!'s `col-passes`, at its own `min-width: 160px` (dashboard.html:3988) and in its own
   place — between the name and the day columns. A MINIMUM, like every other column on this grid:
   the browser distributes the rest, and 160px is what three buttons and their labels ask for on a
   fine pointer.
   THAT 160px IS ALSO A NUMBER src/attendance.js KNOWS. dayColumnCount() reserves it before deciding
   how many day columns to draw. Change it here and change PASS_COL_PX there in the same edit.

   It was this column that cost portrait its day columns at WO-2.8 — four days instead of six — and
   WO-2.12 answered that by giving portrait TODAY ONLY, so the 160px is now spent against six day
   columns in landscape and against one in portrait. The column itself does not move with the
   orientation: a pass is about now, not about a date. */
.attendance-passes {
  min-width: 160px;
  text-align: left; padding: 9px 10px;
  font-size: 10px; font-weight: 700; color: #8a9bb0;
  letter-spacing: 0.5px; text-transform: uppercase;
  border-bottom: 1px solid #eef0f4; white-space: nowrap;
}
.attendance-pass-td { padding: 9px 10px; vertical-align: middle; }
/* Roll Call!'s `.pass-btns`: `display: flex; gap: 5px; align-items: center`. */
.attendance-pass-cell { display: flex; align-items: center; gap: 5px; }
/* And its `.pass-btn`: a small pill, no border, the type in the fill. The three fills are the
   suite's own — indigo, danger red, positive green — and they are the ones Roll Call! uses for
   these same three buttons, so the owner's eye lands on the right one without reading it. */
.attendance-pass-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 3px;
  padding: 4px 9px; border-radius: 14px; border: 1.5px solid transparent;
  font-size: 11px; font-weight: 700; font-family: inherit;
  cursor: pointer; white-space: nowrap; transition: all 0.15s;
}
.attendance-pass-btn.bathroom { background: #eef0fb; color: #5b6fcc; }
.attendance-pass-btn.nurse    { background: #fdeaea; color: #e74c3c; }
.attendance-pass-btn.quick    { background: #eafaf1; color: #27ae60; }
/* Out of the room. Tardy's amber, because this is the one state on the row that is unfinished and
   the teacher is waiting on it — and it is a filled pill rather than a wash so that one row in
   twenty-six reads as different from across the room. */
.attendance-pass-btn.out { background: #fef5ea; border-color: #e67e22; color: #e67e22; }
.attendance-pass-btn:hover:not(:disabled) { border-color: #5b6fcc; }
.attendance-pass-btn.out:hover:not(:disabled) { border-color: #e67e22; background: #fdead4; }
/* At the cap. Faded rather than hidden, with the reason on the control's own title and again in
   the line above the grid: a control that vanishes says nothing about why. */
.attendance-pass-btn:disabled { opacity: 0.35; cursor: not-allowed; }
.attendance-pass-icon { font-size: 12px; line-height: 1; }
/* The time the student left, beside the Return button rather than under it: a caption on its own
   line would make three rows of a twenty-six-row grid taller than the rest, and a row that changes
   height as a pass is issued moves the target under the next thumb. Same reasoning, and the same
   refusal, as `.attendance-cell-time` being out of flow. */
.attendance-pass-since {
  font-size: 10px; font-weight: 700; color: #8a9bb0;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* ── THE PASS BANNER (WO-2.11) ── */
/* Roll Call!'s `#activePassBanner` and its `.pass-card` (dashboard.html:319–376), lifted with its
   MEASUREMENTS AND ITS COLOURS, not just its shape.

   This block was first written as a re-colour — an amber card on a white band — on the argument
   that "the whole suite is light and there is no dark surface anywhere for it to belong to." That
   argument was wrong on its own terms: shell.css:109 is a dark header in this app, in the same
   navy family as Roll Call!'s. The dark band belongs, and re-deriving a palette for a card that
   had already been tuned in the predecessor cost the tuning for nothing. Where a Roll Call! number
   works, it is used here verbatim so a fix over there still ports.

   THE CARDS ARE A FLEX ROW ABOVE THE GRID, never a column beside it — this is the one place
   Planbook's layout genuinely differs, and the reason is local. src/attendance.js's
   dayColumnCount() spends this screen's width to the pixel and WO-2.12 is about to spend it again,
   so a panel on the right would cost a day column; a band across the top costs vertical space,
   which is the one dimension this screen is not short of. Roll Call! puts the band across the top
   too — the difference is only that it is sticky under the header there and sits inside the panel
   here, because here it belongs to one class's registry rather than to the page. */
.attendance-pass-banner {
  display: flex; flex-wrap: wrap; gap: 10px; justify-content: center;
  margin: 0 0 10px; padding: 12px; border-radius: 10px;
  background: #0d1f2f; border-bottom: 2px solid #e67e22;
}
.attendance-pass-card {
  min-width: 260px;
  display: flex; flex-direction: column;
  padding: 10px 14px; border-radius: 10px;
  background: rgba(255,255,255,0.07); border: 1px solid rgba(255,255,255,0.12);
}
.attendance-pass-card-main { display: flex; align-items: center; gap: 12px; }
/* The avatar was left off the first build on the grounds that "the card is one line of names and
   not a roster." It is on the card in Roll Call! for the reason the roster and the home screen also
   carry it: the colour is how a teacher recognises a person at a glance, and this card is read at a
   glance by definition. shell.css's `.avatar` worn as-is, off the same avatarClass() every other
   surface asks, so the circle beside the name here is the circle beside the name there. */
.attendance-pass-card-info { flex: 1; min-width: 0; }
.attendance-pass-card-name {
  color: #fff; font-size: 13px; font-weight: 700;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* NAME ON TOP, TYPE AND TIME OUT UNDER IT — Roll Call!'s two-line info block, not four things on
   one line. The name is the fact you are looking for and it gets the line to itself; the type and
   the hour you last saw them are the qualifiers, and they read as one quiet line beneath. */
.attendance-pass-card-meta { display: flex; align-items: center; gap: 6px; margin-top: 3px; }
/* The word on its own — no glyph, unlike the row's buttons. See the note in passCard(). */
.attendance-pass-card-type {
  display: inline-flex; align-items: center;
  font-size: 10px; font-weight: 700; padding: 2px 7px; border-radius: 8px; white-space: nowrap;
}
/* The three type fills at Roll Call!'s dark-card values — the same hues the row's own pass buttons
   wear, at the alpha that reads on #0d1f2f rather than on white. */
.attendance-pass-card-type.bathroom { background: rgba(91,111,204,0.3); color: #a0b0ff; }
.attendance-pass-card-type.nurse    { background: rgba(231,76,60,0.3);  color: #ff9999; }
.attendance-pass-card-type.quick    { background: rgba(39,174,96,0.3);  color: #7defa7; }
.attendance-pass-card-out { font-size: 10px; color: rgba(255,255,255,0.45); white-space: nowrap; }
/* THE ELAPSED CLOCK'S SLOT, HELD OPEN AND DELIBERATELY EMPTY. WO-2.9 owns the counter itself —
   it is the half that has to survive iOS suspending a backgrounded PWA, and it was cut to Ship 2.
   What is kept here is its GEOMETRY, at Roll Call!'s numbers: the big orange tabular-nums figure
   sits between the info block and the buttons, and reserving that place now means the counter
   lands into a card that already fits it instead of re-flowing one a teacher has learned. */
.attendance-pass-card-elapsed {
  font-size: 20px; font-weight: 800; color: #e67e22;
  font-variant-numeric: tabular-nums; white-space: nowrap;
  min-width: 52px; text-align: right;
}
/* THE TWO BUTTONS ARE DIFFERENT SHAPES ON PURPOSE, and this is the half of "Return and Cancel
   cannot be confused at speed" that a stylesheet owns: Return is FILLED and green, Cancel is an
   OUTLINE that only turns red under a finger. The glyph and the word do the other two thirds. Both
   at Roll Call!'s values — it is the difference in fill rather than the difference in hue that
   survives being seen out of the corner of an eye. */
.attendance-pass-card-btn {
  flex-shrink: 0;
  padding: 6px 13px; border-radius: 8px;
  font-size: 11px; font-weight: 800; font-family: inherit;
  cursor: pointer; white-space: nowrap;
}
.attendance-pass-card-btn.back {
  border: none; background: #27ae60; color: #fff;
  transition: background 0.15s, transform 0.1s;
}
.attendance-pass-card-btn.back:hover  { background: #219a52; }
.attendance-pass-card-btn.back:active { transform: scale(0.95); }
.attendance-pass-card-btn.cancel {
  background: transparent; color: rgba(255,255,255,0.7);
  border: 1.5px solid rgba(255,255,255,0.3);
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.attendance-pass-card-btn.cancel:hover {
  background: rgba(220,38,38,0.25); color: #fff; border-color: rgba(220,38,38,0.6);
}
.attendance-pass-card-btn.cancel:active { transform: scale(0.95); }
/* Full width under the row rather than beside it: a note is a sentence, and the card is already
   carrying four things on one line at 390px. */
.attendance-pass-card-note {
  margin-top: 6px; width: 100%; box-sizing: border-box;
  padding: 4px 8px; border-radius: 5px;
  background: rgba(255,255,255,0.07); border: 1px solid rgba(255,255,255,0.15);
  color: rgba(255,255,255,0.85);
  font-size: 11px; font-family: inherit;
}
/* THE ONE THING NOT TAKEN FROM ROLL CALL!'S CARD: its `.pass-card-note-input` suppresses the focus
   ring and leans on the border change alone. This app has a standing check that no sheet anywhere
   does that, and both halves of the tooling failed on this line the moment it was copied over. A
   border that changes colour is not a focus ring to someone navigating by keyboard. The lift takes
   the design; it does not take a rule this project has already decided against — and the check
   catching it is the reason a lift can be done confidently rather than carefully. */
.attendance-pass-card-note::placeholder { color: rgba(255,255,255,0.35); }
.attendance-pass-card-note:focus { border-color: rgba(255,255,255,0.4); background: rgba(255,255,255,0.1); }

/* ── THE ⋯ AT THE END OF A NAME, AND THE PANEL IT OPENS (WO-2.10) ── */
/* The way into one student's mark on the day being edited: the time it settled, a note, and the
   un-confirm. `margin-left: auto` pushes it to the far edge of the name cell, so it is nowhere near
   the first day column and a thumb aiming at today's cell cannot catch it. Quiet until it is
   hovered or open, because on a full grid there are twenty-six of them and none of them is the
   thing the teacher came to this screen to tap. */
.attendance-detail-btn {
  flex: 0 0 auto; margin-left: auto;
  padding: 2px 6px; border-radius: 6px; line-height: 1;
  border: 1.5px solid transparent; background: transparent; color: #a0aab8;
  font-size: 13px; font-weight: 800; font-family: inherit; cursor: pointer; transition: all 0.15s;
}
.attendance-detail-btn:hover { border-color: #5b6fcc; color: #5b6fcc; }
.attendance-detail-btn.active { background: #eef2ff; border-color: #b0bcf0; color: #5b6fcc; }

/* The panel, as a row of the table spanning every column: at 390px the cell it describes is 44px
   wide and a note is a sentence, so the width has to come from the table rather than from the
   column. Indigo, like the "not on today" band and for the same reason — this is a state the
   teacher CHOSE, not one that needs acting on. */
.attendance-detail-row > td { padding: 0 0 10px; }
.attendance-detail {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  padding: 10px 12px; border-radius: 8px;
  border: 1.5px solid #b0bcf0; border-left: 5px solid #5b6fcc; background: #eef2ff;
}
.attendance-detail-who { font-size: 12px; font-weight: 700; color: #3a4a8a; }
/* The mark itself, in words, wearing the cell's own palette — the same fill and edge the circle in
   the grid has, so the panel and the cell read as one thing. The classes come from the same
   `.attendance-cell-*` family and are worn as-is. */
.attendance-detail-mark {
  padding: 3px 9px; border-radius: 999px;
  border: 1.5px solid #e0e4ea; background: #fff;
  font-size: 11px; font-weight: 800; white-space: nowrap;
}
.attendance-detail-note {
  flex: 1 1 220px; min-width: 0;
  padding: 7px 10px; border-radius: 6px;
  border: 1.5px solid #b0bcf0; background: #fff;
  font-size: 12px; font-family: inherit; color: #1a1a2e;
}
.attendance-detail-note:focus { border-color: #5b6fcc; }
.attendance-detail-hint { flex: 1 1 220px; font-size: 11px; line-height: 1.5; color: #5a6a9a; }

/* ── ONE COLUMN HEAD ── */
/* Roll Call!'s `thead th.day-th`: `text-align: center; min-width: 72px`. A MINIMUM, not a width —
   the column grows into whatever the table has spare, which is the half we had wrong. Ours was a
   hard 52px, so six columns could never total more than 312px however wide the panel got. */
.attendance-day {
  min-width: 72px;
  padding: 9px 10px; text-align: center; vertical-align: top;
  border-bottom: 1px solid #eef0f4;
}
.attendance-day-dow {
  display: block; font-size: 9px; font-weight: 700;
  letter-spacing: 0.5px; color: #a0aab8;
}
.attendance-day-date {
  display: block; font-size: 12px; font-weight: 800; color: #3a4a5a;
  font-variant-numeric: tabular-nums;
}
/* The state, in words, above every column. Wraps to two lines rather than truncating: "Didn't
   meet" abbreviated to fit is the fine print this line exists to avoid. */
/* The min-height is what keeps the control under it on one line across all six columns: "Didn't
   meet" wraps to two lines where "Taken" does not, and a row of pencils that steps up and down is
   a row of pencils the thumb has to aim at individually. */
.attendance-day-state {
  display: block; margin: 2px 0 3px; min-height: 2.3em;
  font-size: 9px; font-weight: 700; line-height: 1.15; white-space: normal;
}
/* One glyph, so it needs width as much as height under a coarse pointer — the lesson `.cls-tab`
   and `.class-action-btn` in shell.css already paid for. */
.attendance-day-btn {
  padding: 2px 4px; border-radius: 6px; line-height: 1;
  border: 1.5px solid transparent; background: transparent; color: #6b7a8d;
  font-size: 12px; font-weight: 700; cursor: pointer; transition: all 0.15s;
}
.attendance-day-btn:hover { border-color: #5b6fcc; color: #5b6fcc; }
.attendance-day-btn.active { background: #eef2ff; border-color: #b0bcf0; color: #5b6fcc; }

/* ── ONE CELL ── */
/* Roll Call!'s `tbody tr` carries the rule and the hover, not the individual cells — one 1px
   #f3f4f6 line per row instead of one per cell, which is why its rows read as rows. */
.attendance-grid tbody tr { border-bottom: 1px solid #f3f4f6; transition: background 0.1s; }
.attendance-grid tbody tr:hover { background: #fafbfc; }
/* The detail row belongs to the row above it, so it takes neither the rule nor the hover — two
   lines under one student would read as two students. */
.attendance-grid tbody tr.attendance-detail-row { border-bottom: 0; }
.attendance-grid tbody tr.attendance-detail-row:hover { background: transparent; }
/* `position: relative` is here for the time caption below, which is positioned OUT OF FLOW on
   purpose: a table row is as tall as its tallest cell, so a caption in normal flow would add ~12px
   to every one of twenty-six rows the moment a single student is marked tardy. */
.attendance-cell-td { padding: 9px 10px; text-align: center; vertical-align: middle; position: relative; }
/* The moment a `T` or a `D` settled, under the glyph it belongs to (WO-2.10). It sits in the
   padding the cell already has, so a class with three tardies in it is exactly as tall as a class
   with none. Tabular numerals because six of these down a column should line up. */
.attendance-cell-time {
  position: absolute; left: 0; right: 0; bottom: 1px;
  font-size: 9px; font-weight: 700; line-height: 1; color: #8a9bb0;
  font-variant-numeric: tabular-nums; pointer-events: none;
}
.attendance-cell {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0; border-radius: 50%;
  border: 1.5px solid #e0e4ea; background: #fff;
  font-size: 13px; font-weight: 800; color: #6b7a8d;
  font-family: inherit; cursor: pointer; transition: all 0.15s;
}
/* A past column that has not been unlocked, and every cell of a day the class did not meet. A
   plain <span> rather than a disabled <button>: a disabled control reads as something broken, and
   this is a record. */
.attendance-cell.locked { cursor: default; }
.attendance-cell:hover:not(.locked) { border-color: #5b6fcc; }

/* Present: the wash, because it is where every student already is — and because twenty-six solid
   green cells would read as twenty-six things having been done, which is the opposite of true. */
.attendance-cell-P { background: #eafaf1; border-color: #a3e4bc; color: #27ae60; }
.attendance-cell-T { background: #fef5ea; border-color: #e67e22; color: #e67e22; }
.attendance-cell-A { background: #fdeaea; border-color: #e74c3c; color: #e74c3c; }
/* Event takes the deep danger tone rather than absent's, so the two reds beside each other are two
   colours and not one — Roll Call! draws its Event cell in this same #c0392b. */
.attendance-cell-E { background: #fdeaea; border-color: #c0392b; color: #c0392b; }
/* Dismissed is the style guide's "special state", which is the example that table names. */
.attendance-cell-D { background: #f3eaff; border-color: #7c3aed; color: #7c3aed; }
/* The hole. Amber, and a question mark, and repeated down the whole column — see the header. */
.attendance-cell-untaken { background: #fff8e6; border-color: #f0dfa8; color: #8a6d1a; }
/* The class did not meet: dashed and quiet, because there is nothing to do about it. */
.attendance-cell-none { background: #fff; border-style: dashed; border-color: #d0d8e4; color: #c0cad5; }
/* And the calendar's version of the same nothing (WO-2.3): the same dash on a SOLID quiet ring
   rather than a dashed one. At 34px a border-style is about as much difference as a cell can carry,
   and it is enough to tell a column of pre-dropped days from a column of dropped ones at a glance —
   which is the only question a cell has to answer here, because the reason is in the head above it
   and in the cell's own accessible name. */
.attendance-cell-covered { background: #f5f7fa; border-color: #d0d8e4; color: #a0aab8; }
/* A day that has not happened (2026-08-08, with the columns that made one reachable). The quietest
   cell in the sheet, and deliberately quieter than "did not meet": that one is a fact recorded
   about a day, this is the absence of any fact at all yet. White ground, hairline ring, a middot
   the same near-invisible grey — the cell is there, it is legible, and it asks for nothing.

   IT IS NOT THE UNTAKEN AMBER, AND THAT IS THE WHOLE REASON THIS CLASS EXISTS. `?` in amber means
   "you have a hole to fill", said in the one colour on this screen that is an alarm. Next Tuesday
   is not a hole, and a week read ahead of time would otherwise be five columns of them. */
.attendance-cell-future { background: #fff; border-color: #eef0f4; color: #c0cad5; }

/* ── THE COLUMN, AS A COLUMN ── */
/* Worn by the <th> and by every <td> under it, from one function in src/attendance.js, which is
   what keeps "said in the header" and "said in the cells" one fact rather than two that drift. */
.attendance-col-not-taken { background: #fffdf5; }
.attendance-col-dropped { background: #fbfcfd; }
/* The covered column (WO-2.3). Deeper than the dropped column's near-white and solid where that
   one is dashed — a whole column of it should read as "greyed out" from across a desk, which is
   what Roll Call! achieves with `opacity: 0.5` on the same kind of column and what this achieves
   without dimming the date a teacher still has to read. */
.attendance-col-covered { background: #f5f7fa; }
/* THE FUTURE, WHICH IS A MODIFIER AND NOT A FIFTH COLUMN STATE. It is worn ALONGSIDE the state
   class, never instead of it, so a day off next November is still `covered` and still reads as the
   grey band a covered day reads as — the calendar's answer about a day is worth more than the fact
   that the day is ahead.

   All it does is quiet the one state whose colour is an alarm. `not-taken` is a cream wash and an
   amber underline meaning "go and fill this in"; ahead of today there is nothing to fill in, so the
   column goes to plain white with the neutral hairline every other quiet thing on this screen uses.
   Declared after the four states so it wins on source order, and qualified with `.attendance-col-
   not-taken` so it can only ever reach that one. src/attendance.js's columnClasses() and
   stateChip() are the other two halves — the wash, the word and the glyph move together or the
   column says two different things about itself. */
.attendance-col-not-taken.attendance-col-future { background: #fff; }
.attendance-day.attendance-col-taken { border-bottom-color: #a3e4bc; }
.attendance-day.attendance-col-not-taken { border-bottom-color: #f0dfa8; }
.attendance-day.attendance-col-not-taken.attendance-col-future { border-bottom-color: #e0e4ea; }
.attendance-day.attendance-col-dropped { border-bottom-style: dashed; border-bottom-color: #d0d8e4; }
.attendance-day.attendance-col-covered { border-bottom-color: #a0aab8; }
.attendance-col-taken .attendance-day-state { color: #27ae60; }
.attendance-col-not-taken .attendance-day-state { color: #8a6d1a; }
/* "Ahead" in the neutral, for the reason the wash above it goes white. */
.attendance-col-not-taken.attendance-col-future .attendance-day-state { color: #a0aab8; }
.attendance-col-dropped .attendance-day-state { color: #a0aab8; font-style: italic; }
/* The fourth word, and it is NOT italic where the dropped one is. Roll Call! italicises its
   no-school head (dashboard.html:574) and this is the point of departure: over there italic-grey is
   the only exception treatment there is, and here it already belongs to the same-day drop. Two
   exception states in one row of column heads have to be told apart, and the stronger colour goes
   to the one whose reason the teacher has to go somewhere else to change. */
.attendance-col-covered .attendance-day-state { color: #6b7a8d; }
/* "12 to go" over a column being taken: the count wears the caution colour rather than the taken
   green it sits on, because it is the alarm rather than the state. Declared after the three above
   so it wins on source order inside the taken column (WO-2.10). */
.attendance-day-state.waiting { color: #8a6d1a; }
/* Today: a pale rail down each side and an indigo date. Where you are, not something you chose. */
.attendance-col-today { box-shadow: inset 1px 0 0 #e0e4ea, inset -1px 0 0 #e0e4ea; }
.attendance-col-today .attendance-day-date { color: #5b6fcc; }
/* An unlocked past column: a solid indigo rail down both sides, so the column the taps are landing
   in is obvious without reading the banner — which is there too, saying the date in words. */
.attendance-col-editing { box-shadow: inset 2px 0 0 #5b6fcc, inset -2px 0 0 #5b6fcc; }

/* ── THE KEYBOARD (WO-2.5) ── */

/* THE SELECTED ROW — Roll Call!'s `.row-selected`, which
   design/portable-components.md:152 names as the row highlight for keyboard navigation, copied by
   value: `rgba(91,111,204,0.07)` across the row and a 3px `#5b6fcc` rail down its left edge
   (dashboard.html:403-404). CLAUDE.md's rule is lift the design with the function, and the scar
   behind it is WO-2.11 — a card that kept the shape and re-derived everything else.

   THE NAME IS THIS SHEET'S PREFIX RATHER THAN ROLL CALL!'s BARE ONE, and that is the local rule
   beating the imported one: src/shell.css's header sets "one class name is styled by one
   stylesheet", and `.row-selected` is a name the roster, the class list and every later table would
   also want. The treatment is unchanged; only the label is.

   THE RAIL IS RESERVED ON EVERY ROW rather than added to the selected one, and it is the single
   departure. Roll Call! puts the border on the selected row's first cell, so twenty-six names step
   3px sideways every time the selection moves — once per student, down a class of thirty, on the
   one screen in this app whose whole design is about not looking at it. Transparent by default and
   coloured when selected is the same picture with no reflow. The head takes the same reservation so
   the column stays aligned with its body.

   AND IT WINS ON SPECIFICITY RATHER THAN ON `!important`. Roll Call! needs the bang because its
   rule and the column washes it sits on are both one class deep; here `.attendance-grid tbody
   tr.attendance-row-selected > *` outranks `.attendance-col-not-taken` outright, in the base sheet
   and in the coarse block. What the wash costs is the faintest of the four state greys on ONE row
   at a time — the column head still says the word, and the cell inside still wears its own tone at
   full strength, which are the two halves the four-state distinction is actually made of. */
.attendance-grid tr > :first-child { border-left: 3px solid transparent; }
.attendance-grid tbody tr.attendance-row-selected > * { background: rgba(91,111,204,0.07); }
.attendance-grid tbody tr.attendance-row-selected > :first-child { border-left-color: #5b6fcc; }

/* The door to the key list, in the toolbar with search, filter and sort — the other three controls
   on this screen that change what you are looking at and write nothing. It wears shell.css's
   `.class-action-btn` as-is; this name exists so the control is listed by name in the coarse block
   below, the same arrangement `.attendance-banner-btn` and `.attendance-actions-door` make, and to
   carry the one rule that is its own: it does not shrink. Two words and a glyph in a flex row is
   exactly the shape that spills over its own border — the defect the owner found on 2026-08-08. */
.attendance-keys-btn { flex-shrink: 0; }

/* THE KEY LIST inside the dialog. A <dl>, because that is what a term-and-definition list is, and
   the rows are <div>s inside it, which HTML5 allows and which is what lets each pair be one flex
   line. Nothing here is a control: the only tappable thing in that panel is the ✕, which is
   shell.css's `.modal-close` and takes its 44px from there. */
.attendance-keys { display: flex; flex-direction: column; gap: 7px; margin: 14px 0 16px; }
.attendance-keys-lead { font-weight: 600; }
.attendance-key-row { display: flex; align-items: baseline; gap: 12px; }
/* Wide enough for the two arrows side by side and for `Esc`, so every description starts on the
   same column and the list reads as a list rather than as eight ragged sentences. */
.attendance-key-row dt { flex: 0 0 76px; display: flex; flex-wrap: wrap; gap: 4px; }
.attendance-key-row dd { flex: 1 1 auto; margin: 0; font-size: 13px; line-height: 1.55; color: #4a5a6a; }
/* A keycap: the app's own type at a smaller size on the quiet wash, with the bottom edge doubled so
   it reads as a key without a shadow or a gradient. `font-family: inherit` on purpose — a monospace
   default here would put a second typeface into an app that has one. */
.attendance-key {
  display: inline-block; min-width: 24px; padding: 3px 7px;
  border: 1.5px solid #d0d8e4; border-bottom-width: 3px; border-radius: 6px;
  background: #f8f9fb; color: #3a4a8a;
  font-family: inherit; font-size: 12px; font-weight: 700; line-height: 1.3; text-align: center;
}

/* ══════════════════ THE TWO READ-BACK SURFACES (WO-2.6) ══════════════════

   One student's history, and the class's record for a term. Both are dialogs over this screen and
   both are drawn by src/attendance-report.js, so their rules are here rather than in a sheet of
   their own — the same call the ⌨ key list above makes, which is also a dialog belonging to this
   screen rather than a screen of its own.

   The grammar is lifted from Roll Call!'s student report (`.sr-*` in dashboard.html) and from the
   registry above: an identity row with the avatar and a rate badge, section labels in the app's
   uppercase micro-label voice, and the five mark palettes worn EXACTLY as the cells wear them —
   `.attendance-cell-A` and its four siblings, which this sheet owns and which the row's detail
   panel already reuses the same way. A second amber for a tardy in a printed table is a second
   answer to "what colour is a tardy". */

/* The door in the toolbar. Wears shell.css's `.class-action-btn`; the name exists so the control is
   listed by name in the coarse block below and to carry the one rule that is its own — it does not
   shrink, for the reason `.attendance-keys-btn` beside it states. */
.attendance-record-btn { flex-shrink: 0; }

/* Wider than a form dialog because a table of dates is what is in it. `.modal-panel` supplies the
   frame and the 95vw; this raises the cap only, so a narrow screen is unchanged. */
.attendance-report-panel { max-width: 900px; }

/* Print and Download CSV, at the TOP of the body — index.html says why: the table under them is a
   term long, and a control below it is a control reached by scrolling past the whole record. */
.attendance-report-actions {
  display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 16px;
}

/* Roll Call!'s `.sr-header-left` — avatar, then name over a quiet subtitle — with its rate badge
   pushed to the far end. On the record dialog the same row carries the class name and the printed
   header instead, with no avatar, because a class has no face. */
.attendance-report-head {
  display: flex; align-items: center; flex-wrap: wrap; gap: 12px;
  padding: 0 0 12px; margin: 0 0 4px;
  border-bottom: 1.5px solid #eef0f4;
}
/* The way from this student's attendance to the same student's grades (WO-3.7). Under the identity
   row rather than in it: that row is the answer to "how often is this student here", and a door to
   another screen sitting inside it reads as part of the answer. `.class-action-btn` worn as-is —
   this sheet adds a margin and nothing else, so the control is the app's own and is already 44px
   under a coarse pointer from src/shell.css's block. */
.attendance-report-door { margin: 12px 0 4px; }

.attendance-report-who { flex: 1 1 200px; min-width: 0; }
.attendance-report-name { font-size: 16px; font-weight: 700; color: #1a1a2e; line-height: 1.3; }
.attendance-report-sub {
  font-size: 12px; font-weight: 600; color: #8a9bb0; line-height: 1.5; margin-top: 2px;
}
/* Roll Call!'s `.sr-rate-badge`, in this app's positive green rather than that app's three
   threshold colours: a threshold is Phase 4's to decide, and a badge that turned amber here would
   be this file inventing what "at risk" means before the work order that owns it is written. */
.attendance-report-rate {
  flex: 0 0 auto;
  padding: 6px 14px; border-radius: 999px;
  border: 1.5px solid #a3e4bc; background: #eafaf1; color: #27ae60;
  font-size: 18px; font-weight: 700; line-height: 1.2;
}

/* design/style-guide.md §9's uppercase micro label, which shell.css calls `.modal-section-label` on
   a dialog; named here because these two dialogs put one over each table and the spacing above a
   table is not the spacing above a form row. */
.attendance-report-label {
  margin: 18px 0 8px;
  font-size: 10px; font-weight: 700; color: #8a9bb0;
  letter-spacing: 0.8px; text-transform: uppercase;
}

.attendance-report-table {
  width: 100%; border-collapse: collapse;
  font-size: 12px; font-variant-numeric: tabular-nums;
}
.attendance-report-table th, .attendance-report-table td {
  padding: 6px 8px; text-align: left; vertical-align: middle;
  border-bottom: 1px solid #f3f4f6;
}
.attendance-report-table thead th {
  font-size: 10px; font-weight: 700; color: #8a9bb0;
  letter-spacing: 0.5px; text-transform: uppercase;
  border-bottom: 1.5px solid #eef0f4; white-space: nowrap;
}
.attendance-report-num { text-align: right; white-space: nowrap; }
.attendance-report-table thead th.attendance-report-num { text-align: right; }
.attendance-report-row-head { font-weight: 600; color: #1a1a2e; }
/* The term the registry behind this dialog is on, in the interactive wash the rest of this app uses
   for "you are here" — Roll Call!'s `.sr-q-current` in Planbook's palette. */
.attendance-report-open > * { background: #eef2ff; }
.attendance-report-dow {
  display: inline-block; min-width: 30px;
  font-size: 10px; font-weight: 700; color: #a0aab8; letter-spacing: 0.5px;
}
.attendance-report-date { font-weight: 600; color: #4a5a6a; }
/* A mark, wearing the cell palette from the top of this sheet. `.attendance-detail-mark`'s shape,
   at the size a table row can hold. */
.attendance-report-mark {
  display: inline-block; min-width: 22px; padding: 2px 8px;
  border: 1.5px solid transparent; border-radius: 6px;
  font-size: 11px; font-weight: 700; text-align: center;
}
.attendance-report-empty { font-size: 13px; color: #a0aab8; line-height: 1.5; margin: 8px 0; }
.attendance-report-note { font-size: 11px; color: #8a9bb0; line-height: 1.6; margin-top: 16px; }

/* ── the day-by-day grid, and its slices ──
   One slice per printed page's worth of date columns. On screen the slice scrolls sideways inside
   its own wrapper exactly as the registry's grid does; on paper it is a page. */
.attendance-report-slice { margin-bottom: 16px; overflow-x: auto; -webkit-overflow-scrolling: touch; }
.attendance-report-slice-label {
  font-size: 11px; font-weight: 700; color: #8a9bb0; margin-bottom: 4px;
}
.attendance-report-grid th, .attendance-report-grid td { padding: 4px 5px; }
.attendance-report-day { text-align: center; }
.attendance-report-day .attendance-report-dow,
.attendance-report-day .attendance-report-date { display: block; min-width: 0; }
.attendance-report-cell { text-align: center; }
.attendance-report-grid .attendance-report-mark { min-width: 20px; padding: 1px 4px; }

/* ── RESPONSIVE ── */
/* Same order design/style-guide.md §6 names and both other sheets follow: (pointer: coarse)
   first, then the width blocks. */

/* The touch pass for this screen's controls, in the same pass that adds them — there is no later
   one. The cells are the reason this block exists: they are one glyph wide, so they need min-width
   as much as min-height, and the column has to widen with them or the row spills. */
@media (pointer: coarse) {
  /* THE 720px CAP LIVED HERE TOO, AND THAT IS WHY LIFTING IT DID NOT REACH THE IPAD. The block at
     the top of this file records the owner overruling the cap on 2026-08-06; the base rule was
     changed and this one was not, so the fix landed on the laptop — a fine pointer, which never
     reads this block — and every touch device kept the dialog width. The screen it was overruled
     FOR is the only screen that never got it.

     The visible cost was the WO-2.10 detail panel: at 720px the grid's own columns want 711px
     inside 680px of body, the wrap's `overflow-x` safety valve engages, and the note field sits
     16px past the right edge in both orientations — identically, because a fixed panel width makes
     the geometry the same whichever way the iPad is held. Rotating to landscape left 288px of
     screen unused and changed nothing, which is what the report described.

     Two rules matter here and they are not interchangeable: this one gives landscape the room
     outright, and `.attendance-name`'s cap below is what clears portrait, where even the full
     screen was 8px short. Removing either one puts the note back under the edge.

     WO-2.12 took portrait down to one day column, so portrait now has 200px of slack rather than
     8px. That is why the cap below could go back up to 256 — and not a reason to drop either rule:
     the panel still needs the whole width in landscape, and the cap is still what stops a long
     surname widening the table past its wrap on any screen. */
  .attendance-panel { width: 100%; }
  .attendance-body .attendance-banner { font-size: 15px; padding: 12px 16px; }
  .attendance-banner-text { flex-basis: 200px; }
  /* The size comes from `.class-action-btn`'s own coarse rule, which this button also wears; it is
     named here so the control appears in this block rather than being covered somewhere else. */
  .attendance-banner-btn { min-height: 44px; min-width: 44px; }
  .attendance-body .attendance-state { font-size: 14px; padding: 10px 14px; }
  .attendance-actions { gap: 10px; margin-bottom: 12px; }
  .attendance-toggle-on { min-height: 44px; min-width: 44px; }
  /* Same arrangement as `.attendance-banner-btn` above: the 44px comes from `.class-action-btn`,
     which this button also wears, and the name appears here so a reader of this block can see the
     control was considered rather than having to go and prove it is covered elsewhere. It also does
     not shrink, which is the shell.css defect the owner found on 2026-08-08 — this button is two
     words and a glyph in a flex row, which is exactly the shape that spills over its own border. */
  .attendance-actions-door { min-height: 44px; min-width: 44px; flex-shrink: 0; }
  /* The row's ⋯ and everything inside the panel it opens (WO-2.10). Three of the five are touch
     targets and take the floor; the two that are text — the name line and the mark chip — are named
     here anyway so that a reader of this block can see that they were considered rather than
     missed, which is the call `.attendance-student-cell` makes twenty lines above for the same
     reason. The "Un-confirm" button inside the panel is a `.class-action-btn` and takes its 44px
     from shell.css's own coarse rule for that component. */
  .attendance-detail-btn { min-height: 44px; min-width: 44px; font-size: 15px; padding: 2px 8px; }
  /* The panel's row is a table row and not a target — what takes a thumb is the field and the
     button inside it, both named below. It is in this block for its spacing: the gap that separates
     the panel from the next student grows with everything else here, and a rule missing from the
     coarse block is a rule the next reader has to go and prove was considered. */
  .attendance-detail-row > td { padding-bottom: 12px; }
  .attendance-detail { gap: 10px; padding: 12px 14px; }
  .attendance-detail-who { font-size: 13px; }
  .attendance-detail-mark { font-size: 12px; padding: 4px 10px; }
  .attendance-detail-note { min-height: 44px; font-size: 14px; padding: 10px 12px; }
  .attendance-detail-hint { font-size: 12px; }
  .attendance-body .attendance-note { font-size: 13px; }
  .attendance-body .attendance-pass-note { font-size: 13px; padding: 10px 14px; }
  .attendance-body .attendance-hint { font-size: 12px; }
  .attendance-body .attendance-empty { font-size: 14px; }
  .attendance-toolbar { gap: 10px; }
  .attendance-pills { gap: 6px; }
  .attendance-sort { gap: 6px; }
  .attendance-sort-label { font-size: 11px; }
  /* ── WO-2.5, and the touch half of a keyboard work order is not a contradiction. A Smart Keyboard
     on an iPad is a coarse pointer with letters, so this door has to be tappable on the device that
     will not have a mouse; and the roadmap line is "both, not either" precisely because building
     for one device is how the other becomes unusable. Same arrangement as `.attendance-banner-btn`
     and `.attendance-actions-door`: the 44px comes from `.class-action-btn`'s own coarse rule,
     which this button also wears, and the name is here so a reader of this block can see the
     control was considered rather than having to go and prove it is covered elsewhere.
     `flex-shrink` is restated for the reason that one restates it — a two-word label in a flex row
     is the shape that clears 44px in both directions and still spills through its own border. */
  .attendance-keys-btn { min-height: 44px; min-width: 44px; flex-shrink: 0; }
  /* The row highlight and the key list are not controls, and they are named here anyway for the
     reason `.attendance-student-cell` gives above: a rule missing from this block is a rule the
     next reader has to go and prove was considered. What they get is the same step up in size
     everything else here takes, because a coarse pointer is the device held at arm's length. */
  .attendance-keys { gap: 9px; margin: 16px 0 18px; }
  .attendance-keys-lead { font-size: 15px; }
  .attendance-key-row { gap: 14px; }
  .attendance-key-row dt { flex-basis: 86px; }
  .attendance-key-row dd { font-size: 14px; }
  .attendance-key { min-width: 28px; padding: 4px 9px; font-size: 13px; }
  .attendance-sort-btn { padding: 8px 14px; font-size: 12px; min-height: 44px; min-width: 44px; }
  .attendance-pager { gap: 10px; }
  .attendance-pager-range { font-size: 12px; }
  .attendance-grid-wrap { margin-top: 2px; }
  .attendance-grid { font-variant-numeric: tabular-nums; }
  .attendance-corner { font-size: 11px; }
  .attendance-name { font-size: 14px; padding: 4px 8px 4px 2px; }
  /* ── THE SIX THIS BLOCK WAS MISSING (WO-2.5's audit) ──
     Three comments in this file already point at `.attendance-student-cell` as the precedent for
     naming a non-control here, and it was not actually in the block — so the precedent existed
     only in prose. That is the audit WO-2.5 owes: every control added in WO-2.1 through WO-2.4,
     read against this block rather than measured. The measurement is in tools/verify-shell.mjs and
     it was green either way, which is exactly why the reading is the half that finds this.

     NONE OF THE SIX IS A TOUCH TARGET, and that is the finding rather than an excuse: five are
     text, and the sixth is the flex row that holds the avatar, the name and the ⋯ — where the ⋯
     is the only tappable thing and takes its 44px from `.attendance-detail-btn` below. So what
     they get is their base values RESTATED rather than new ones, the arrangement
     `.attendance-pass-card-main`'s `nowrap` makes further down: the name column is the tightest
     thing on this screen (see the cap below, and PASS_COL_PX in src/attendance.js), and a coarse
     pointer is not the place to start growing the type inside it. Named, considered, unchanged. */
  .attendance-student-cell { gap: 9px; }
  .attendance-student-identity { min-width: 0; }
  .attendance-student-name { overflow: hidden; text-overflow: ellipsis; }
  .attendance-student-totals { font-size: 10px; line-height: 1.35; }
  .attendance-detail-totals { font-size: 12px; }
  .attendance-body .attendance-totals { font-size: 12px; }
  /* THE NAME COLUMN MAY NOT DEMAND MORE THAN THE SIX COLUMNS LEAVE. This cell is `nowrap`, so its
     min-content is the whole name laid out flat — 279px for "Delacroix-Nguyen, Xiomara" — and a
     table cell's min-content is a floor the browser will widen the TABLE to honour. Six day columns
     at 72px are 432px, an iPad portrait leaves 688px of body, and 688 - 432 = 256: past that the
     grid stops fitting its wrap and the wrap starts clipping, which is how the note field ended up
     under the right edge.

     The cap does not truncate anything on its own — it releases the floor. The column still takes
     whatever the table has spare (512px in landscape, where there is room), and it only gives way
     where something else needs the pixels. The ellipsis and the full name on the row's `title` were
     already built for this moment in `.attendance-student-name` above; nothing before this rule
     ever made them engage.

     Any value from 200 to 256 measures identically, because what matters is that a cap EXISTS.
     256 was the arithmetic above and truncated the least, so it was the one written down.

     IT CAME DOWN TO 232 AT WO-2.8, and the arithmetic is the same arithmetic with one more column
     in it. The Passes column asks for 160px and four day columns ask for 288, which leaves
     688 - 160 - 288 = 240 in portrait; 232 takes that with 8px to spare rather than landing on it
     exactly, because three columns rounding the same way is how a grid ends up one pixel over its
     wrap. The number that decides there are FOUR day columns and not six is PASS_COL_PX in
     src/attendance.js, and it reads this cap to do it — the two move together or neither is right.
     The value is still inside the 200-to-256 band above, so nothing about what this rule proves
     has changed.

     AND IT WENT BACK TO 256 AT WO-2.12, which is the pressure being taken off rather than a third
     opinion about the number. Portrait draws ONE day column now, so the sum that forced 232 no
     longer exists: 688 - 160 - 72 = 456px of room for a cap of 256, and 200px of that is slack.
     Landscape is the tighter of the two and still clears it — 944 - 160 - 432 = 352 — so nothing
     about what landscape draws moves, which WO-2.12 puts out of scope. 256 is the top of the
     200-to-256 band and the value that truncates least, which is the only reason to prefer it: on a
     full-screen iPad in either orientation the column takes its spare and no name is cut at all, and
     the cap is left standing as what it always was — the FLOOR released, so a 279px surname can
     never widen the table past its wrap.

     NAME_COL_COARSE_PX in src/attendance.js is this number and must move with it in the same edit,
     the way PASS_COL_PX and `.attendance-passes` do. What it decides there is now only the LANDSCAPE
     column count: at 256 a coarse landscape viewport needs 928px for six day columns, and every iPad
     in landscape is 1024 or wider. */
  .attendance-name { max-width: 256px; }
  /* 54px of column for a 44px cell and 5px either side: the mis-tap this spacing is bought against
     is marking the wrong student absent on the wrong day, which is a phone call home about a child
     who was in the room. Same reasoning as `.roster-row-actions`' 8px in shell.css.

     THE COLUMN IS NOT 54px WIDE, AND THIS RULE IS NOT WHY. The base `.attendance-day` carries
     `min-width: 72px` — lifted from Roll Call!'s `thead th.day-th` and deliberate — and a minimum
     beats a width, so the rendered column is 72px. The body cell independently needs 60px (a 44px
     circle inside `.attendance-cell-td`'s 8px sides), so nothing here is starved; what is wrong is
     only the reading. Left as it stands because 72px is the value that was chosen on purpose and
     narrowing the column is not what the 2026-08-06 report asked for — but do the arithmetic with
     72, not 54. Doing it with 54 is what produced the tablet block's confident wrong sum below. */
  /* THE PASS BUTTONS LOSE THEIR WORDS HERE, and that is the trade this column makes for its 44px.
     Three targets at 44 wide, 2px between them and the cell's own 12px of padding come to 148px
     inside a 160px column; three targets carrying "Bath", "Nurse" and "Quick" as well come to about
     200, and the 40px difference is a day column off the far end of an iPad in portrait. The word
     is still on the button's accessible name and on its tooltip, and the icon is the part the
     owner's eye is already trained on — these are Roll Call!'s own three glyphs. The Return button
     keeps its word: there is one of it, and it is the one that must not be guessed at. */
  .attendance-pass-word { display: none; }
  .attendance-pass-cell { gap: 2px; }
  .attendance-pass-btn { min-height: 44px; min-width: 44px; padding: 4px 6px; font-size: 12px; }
  .attendance-pass-btn.out { padding: 4px 10px; }
  .attendance-pass-icon { font-size: 16px; }
  .attendance-pass-since { font-size: 11px; }
  /* THE BANNER CARD (WO-2.11). Three of these are touch targets and take the floor; the four that
     are text are named anyway, for the reason `.attendance-student-cell` gives above — a rule
     missing from this block is a rule the next reader has to go and prove was considered.

     THE CARD IS ONE ROW ON A THUMB, THE SAME ROW IT IS UNDER A MOUSE. This block first tried the
     opposite — `flex-wrap: wrap` on the main row, the info block forced to `flex: 1 1 100%`, and
     Return set to grow — reasoning that 44px targets could not share a line with a name and a chip.
     On the owner's iPad on 2026-08-07 that produced two rows of buttons in landscape and three in
     portrait, against a desktop layout that was already correct. The wrap rules were the whole
     cause: a full-width info block leaves the buttons nowhere to go but downward, and a Return that
     grows then takes the rest of the line and puts Cancel on a third row.

     What was wrong was the premise, not the arithmetic. The line fits once it stops carrying things
     it did not need: the chip's emoji is gone (the row's three buttons need their glyphs because
     they lost their words to a 160px column — this chip kept its word, so the glyph was saying it
     twice), and the two buttons are tightened to the same padding. What does NOT give to buy that
     room is the 10px between Return and Cancel: they are 44px apart-able or this card has a defect
     the whole work order exists to prevent. */
  .attendance-pass-banner { gap: 10px; margin-bottom: 12px; }
  /* `min-width: 0` against the 260px floor the card carries on a mouse: two cards side by side in
     834pt portrait is the case acceptance line 9 protects, and a floor that cannot be crossed is
     how the second card pushes the banner wider than the grid it sits above. */
  .attendance-pass-card { min-width: 0; padding: 12px 14px; }
  /* `nowrap` stated rather than inherited: the base rule does not wrap, and this line is what stops
     a future tablet tweak from reintroducing the three-row card by accident. */
  .attendance-pass-card-main { gap: 10px; flex-wrap: nowrap; }
  /* Neither of these is a touch target — `-info` is the name-and-meta column, `-meta` the line under
     the name. They carry rules here anyway so the sweep's "added a selector with no coarse rule"
     line is answered in the sheet. `min-width: 0` is the load-bearing half: it is what lets the
     name ellipsize instead of shoving the buttons off the end of the row. */
  .attendance-pass-card-info { flex: 1; min-width: 0; }
  .attendance-pass-card-meta { gap: 8px; margin-top: 4px; }
  .attendance-pass-card-name { font-size: 14px; }
  .attendance-pass-card-type { font-size: 12px; padding: 4px 10px; }
  .attendance-pass-card-out { font-size: 12px; }
  .attendance-pass-card-elapsed { font-size: 22px; }
  /* 12px of side padding, not 16: the two buttons are the same size as each other here, and the
     8px that came off the pair is most of what buys the single row. `min-width: 44px` stays — it is
     the floor, and neither button is anywhere near it with a word on it. */
  .attendance-pass-card-btn { min-height: 44px; min-width: 44px; font-size: 13px; padding: 10px 12px; }
  /* No `transform` on tap for either: a 0.95 scale under a thumb reads as the button moving away
     from the finger that hit it. Roll Call! kills the same two here for the same reason. */
  .attendance-pass-card-btn.back:active, .attendance-pass-card-btn.cancel:active { transform: none; }
  .attendance-pass-card-note { min-height: 44px; font-size: 14px; padding: 10px 12px; }
  .attendance-passes { font-size: 11px; padding: 6px 6px; }
  .attendance-pass-td { padding: 9px 6px; }
  .attendance-day { width: 54px; padding: 6px 3px; }
  .attendance-day-dow { font-size: 10px; }
  .attendance-day-date { font-size: 13px; }
  .attendance-day-state { font-size: 10px; }
  .attendance-day-btn { min-height: 44px; min-width: 44px; font-size: 15px; padding: 2px; }
  .attendance-cell-td { padding: 9px 8px; }
  .attendance-cell { width: 44px; height: 44px; min-width: 44px; min-height: 44px; font-size: 15px; }
  /* One point larger, and still out of flow: the caption has to be readable at arm's length on the
     device that gets stood in front of a class, and the 44px circle above it must not move. */
  .attendance-cell-time { font-size: 10px; bottom: 0; }
  /* Every tone one step louder, because a touch device is the one that gets held at arm's length
     and stood in front of a class. A 1.5px edge that reads fine on a laptop 50cm away is the edge
     that stops carrying the difference between an absence and an event across a room. */
  .attendance-cell-P, .attendance-cell-T, .attendance-cell-A,
  .attendance-cell-E, .attendance-cell-D,
  .attendance-cell-untaken, .attendance-cell-none,
  .attendance-cell-covered, .attendance-cell-future { border-width: 2px; }
  /* And the same for the column, which is the half of the four-state distinction that is read
     without looking at any single cell. The covered column takes the biggest step of the four,
     because it and the dropped column are the pair a teacher has to tell apart across a room and
     the fine pointer's two shades of near-white are what the extra distance costs. */
  .attendance-col-taken { background: #fff; }
  .attendance-col-not-taken { background: #fff8e6; }
  .attendance-col-dropped { background: #f8f9fb; }
  .attendance-col-covered { background: #e8edf4; }
  /* The future stays white while the untaken column goes LOUDER here, which widens the gap between
     them rather than holding it — correct, and the reason this line exists rather than being left
     to the base rule: the amber is stepping up precisely so a hole is unmissable across a room, and
     a day that has not happened must not step up with it. Declared after the four so it wins on
     source order inside this block too. */
  .attendance-col-not-taken.attendance-col-future { background: #fff; }
  .attendance-col-today { box-shadow: inset 2px 0 0 #d0d8e4, inset -2px 0 0 #d0d8e4; }
  .attendance-col-editing { box-shadow: inset 3px 0 0 #5b6fcc, inset -3px 0 0 #5b6fcc; }
  /* And the keyboard's row one step louder with them (WO-2.5): the rail 3px → 4px, the wash 7% →
     10%. Same call as the two lines above and as every cell tone in this block — a highlight that
     reads fine on a laptop 50cm away is the one that stops carrying on a device held at arm's
     length, and a teacher marking from a Smart Keyboard is looking at the room, not the screen.
     The reservation on every row moves with the rail or the names step sideways again, which is the
     whole reason it is reserved; the two values are one decision written twice. */
  .attendance-grid tr > :first-child { border-left-width: 4px; }
  .attendance-grid tbody tr.attendance-row-selected > * { background: rgba(91,111,204,0.1); }

  /* ── WO-2.6's controls and the two dialogs they open ──

     THE IDENTITY BLOCK IS A TOUCH TARGET NOW, and the 44px goes on the BLOCK rather than on the
     name inside it. The arithmetic, because it decides the height of every row on the screen this
     app is measured by: the block is a 13px name over a 10px totals line, about 31px; the row is
     decided by the 44px cell plus `.attendance-cell-td`'s 9px of padding either side, so 62px.
     44 on the block leaves the cell still deciding (44 + 18 of `.attendance-name` padding = 62) and
     the row does not move. 44 on the NAME alone would make the block 44 + 13 = 57, the cell 75, and
     twenty-six students two thirds of a screen taller. `justify-content: center` is what keeps the
     text where it was inside the taller box. */
  .attendance-student-identity { min-width: 0; min-height: 44px; justify-content: center; }
  /* Roll Call!'s own coarse rule at the same selector (`.s-name-link { text-decoration: underline }`
     in dashboard.html's `@media (pointer: coarse)`, under the heading "always-visible hover-only
     affordances"): there is no hover on a touch screen, so the affordance that a hover carries on a
     laptop has to be drawn all the time. Lifted, not re-derived. */
  .attendance-student-name { text-decoration: underline; }
  /* The 44px comes from `.class-action-btn`'s own coarse rule, which this button also wears; the
     name is here so a reader of this block can see the control was considered rather than having to
     go and prove it is covered elsewhere, and `flex-shrink` is restated for the reason
     `.attendance-keys-btn` beside it restates it — a two-word label with a glyph in a flex row is
     the shape that clears 44px in both directions and still spills through its own border. */
  .attendance-record-btn { min-height: 44px; min-width: 44px; flex-shrink: 0; }
  /* The two output buttons wear `.class-action-btn` and take their 44px from it; this row is here
     for its spacing. Nothing else in either dialog is tappable — the ✕ is shell.css's
     `.modal-close` — so the rest of the names below are text and tables, listed for the reason
     `.attendance-student-cell` and `.attendance-pass-card-info` are listed: a rule missing from
     this block is a rule the next reader has to go and prove was considered. What they get is one
     step up in size, because a coarse pointer is the device held at arm's length. */
  .attendance-report-actions { gap: 10px; margin-bottom: 18px; }
  .attendance-report-panel { max-width: 900px; }
  .attendance-report-head { gap: 14px; padding-bottom: 14px; }
  .attendance-report-who { min-width: 0; }
  .attendance-report-name { font-size: 17px; }
  .attendance-report-sub { font-size: 13px; }
  .attendance-report-rate { font-size: 19px; padding: 7px 16px; }
  .attendance-report-label { font-size: 11px; margin-top: 20px; }
  .attendance-report-table { font-size: 13px; }
  .attendance-report-table th, .attendance-report-table td { padding: 8px 9px; }
  .attendance-report-num { white-space: nowrap; }
  .attendance-report-row-head { font-weight: 600; }
  .attendance-report-open > * { background: #eef2ff; }
  .attendance-report-dow { font-size: 11px; }
  .attendance-report-date { font-size: 13px; }
  .attendance-report-mark { font-size: 12px; padding: 3px 9px; }
  .attendance-report-empty { font-size: 14px; }
  .attendance-report-note { font-size: 12px; }
  .attendance-report-slice { margin-bottom: 18px; }
  .attendance-report-slice-label { font-size: 12px; }
  /* The day-by-day grid does NOT grow with the rest: it is students by dates, it already scrolls
     sideways inside its own slice, and a step up in padding here costs columns on the screen with
     the least room for them. Named, considered, tightened rather than grown. */
  .attendance-report-grid th, .attendance-report-grid td { padding: 5px 5px; }
  .attendance-report-day { text-align: center; }
  .attendance-report-cell { text-align: center; }
}

/* The tablet. There is no width rule here and there does not need to be one: the coarse block above
   already gives this screen the full width of `.main`, which is the same answer 95vw was reaching
   for at this breakpoint and arrives without a second number to keep in step.

   This comment used to argue the opposite — that 720px "already fits six columns and a long name on
   both orientations". It did not. Six columns are 432px rather than the 324px claimed (the day
   column's floor is the base `min-width: 72px`, not the 54px the coarse block asks for), and a long
   name is 279px, so the grid wanted 711px of a 680px body and clipped the WO-2.10 note panel on
   every iPad in both orientations. The arithmetic was wrong in the direction that made the cap look
   safe, which is the kind of wrong that survives review. */
@media (max-width: 1024px) {
  .attendance-sort { margin-left: 0; }
}

/* The phone. Fewer columns than six (src/attendance.js drops them; this block only makes the row
   that is left readable), and the honest trade is the same one the marks made before: the grid is
   what the screen is for, and this is not the device it is for.

   A phone under 640px is almost always being held UPRIGHT, and since WO-2.12 that means one day
   column rather than the three the width budget used to floor at — which is the first time this
   screen has actually fitted a phone rather than leaning on the valve below. Nothing here was
   changed for it; it is recorded because "three columns" is what this comment used to promise. */
@media (max-width: 640px) {
  .attendance-name { font-size: 12px; padding-right: 4px; }
  /* The column keeps its width here on purpose. Narrowing it below the 54px a 44px cell needs is
     how a row spills sideways, and dropping a column is the answer this screen already has. */
  .attendance-cell-td { padding-left: 4px; padding-right: 4px; }
  .attendance-corner, .attendance-name { padding-left: 4px; padding-right: 6px; }
  /* The pass column keeps its 160px here too, for the same reason the day column keeps its width:
     three 44px targets do not get smaller because the screen did, and a phone is where this grid
     already leans on the wrap's sideways-scroll valve. */
  .attendance-passes, .attendance-pass-td { padding-left: 4px; padding-right: 4px; }
  .attendance-pager-range { display: none; }
}

/* ══════════════════════════ THE PRINTED PAGE (WO-2.6) ══════════════════════════

   The one @media print block in this app, and the only surface it prints is the attendance record
   dialog. It is HERE, in the registry's own sheet, because that dialog belongs to this screen the
   way the ⌨ key list does — and because there is nothing else to print: Planbook has no default
   print surface at all.

   IT IS GATED ON AN ATTRIBUTE, AND THE GATE IS THE POINT. src/attendance-report.js's printRecord()
   puts `data-attendance-print` on <body>, calls window.print(), and takes it off again — Roll Call!'s
   `body[data-modal-print]` idiom (dashboard.html), lifted whole. Without the gate, a Ctrl+P made
   anywhere else in the app would hide the entire page and print a blank sheet, which is the kind of
   thing nobody finds until the day it matters. With it, a keyboard print is the browser's business
   and this block is silent.

   ONE DEPARTURE FROM ROLL CALL!, and it is forced. Over there the DEFAULT print target is the
   registry table, because that table IS the term. Ours is a six-day window (src/attendance.js's
   header says why at length), so printing it would hand a teacher six days and call it a term. The
   record dialog exists to be the thing that prints.

   WHAT MAKES A CLASS FIT. Down the page: one row per student, 8pt, and a roster of ordinary size
   lands on one sheet. Across it: the day-by-day table is already drawn in slices of twenty-four
   date columns by the module (DATES_PER_SLICE, and the millimetres are written down there), and
   each slice starts a new page. `display: table-header-group` on every thead is what repeats the
   column heads when a table does break, which is the one thing a long roster needs and the one
   thing browsers only do if you ask.

   Colours are kept rather than stripped. These are the five mark palettes the teacher reads on
   screen, `print-color-adjust` asks the browser not to drop them, and a monochrome printer renders
   them as five distinguishable greys behind five distinct letters — which is the same belt-and-
   braces the grid itself is built on. */
@media print {
  /* Everything on the page goes, and the record comes back. `body > *` rather than a list of
     chrome class names: this sheet styles its own class names and nothing else (see the header at
     the top of this file), and a list would also be a list to keep in step with every panel a
     later work order adds. */
  body[data-attendance-print] > * { display: none !important; }
  body[data-attendance-print] { background: #fff; }
  body[data-attendance-print] > #attendanceRecordModal {
    display: block !important; position: static !important;
    overflow: visible !important; background: none !important; padding: 0 !important;
  }
  body[data-attendance-print] #attendanceRecordModal .modal-panel {
    display: block !important; width: 100% !important; max-width: none !important;
    max-height: none !important; overflow: visible !important;
    border-radius: 0 !important; box-shadow: none !important; animation: none !important;
  }
  body[data-attendance-print] #attendanceRecordModal .modal-body {
    overflow: visible !important; max-height: none !important; padding: 0 !important;
  }
  /* The dialog's own furniture is not part of the record: the title bar says "Attendance record"
     where the printed header already says which class and which term, the ✕ is a control, and so
     are Print and Download CSV. */
  body[data-attendance-print] #attendanceRecordModal .modal-header,
  body[data-attendance-print] .attendance-report-actions { display: none !important; }

  body[data-attendance-print] .attendance-report-head {
    border-bottom: 2px solid #000; padding-bottom: 6mm; margin-bottom: 4mm;
  }
  body[data-attendance-print] .attendance-report-name { font-size: 14pt; color: #000; }
  body[data-attendance-print] .attendance-report-sub { font-size: 9pt; color: #333; }
  body[data-attendance-print] .attendance-report-label {
    font-size: 8pt; color: #000; margin: 6mm 0 2mm;
  }
  body[data-attendance-print] .attendance-report-table { font-size: 8pt; }
  body[data-attendance-print] .attendance-report-table th,
  body[data-attendance-print] .attendance-report-table td {
    padding: 1mm 1.5mm; border-bottom: 0.2mm solid #999; color: #000;
  }
  body[data-attendance-print] .attendance-report-table thead th {
    font-size: 7pt; color: #000; border-bottom: 0.4mm solid #000;
  }
  /* Repeat the column heads on the second page of a long roster, and never break a student's row
     in half. */
  body[data-attendance-print] .attendance-report-table thead { display: table-header-group; }
  body[data-attendance-print] .attendance-report-table tr { break-inside: avoid; }
  /* The slice IS the page. `overflow: visible` because the on-screen sideways scroll has nothing to
     scroll on paper, and a slice that kept `overflow-x: auto` would print clipped at the sheet edge
     with no way to reach the rest. */
  body[data-attendance-print] .attendance-report-slice {
    overflow: visible !important; break-before: page; margin-bottom: 0;
  }
  body[data-attendance-print] .attendance-report-slice-label { font-size: 8pt; color: #000; }
  body[data-attendance-print] .attendance-report-grid th,
  body[data-attendance-print] .attendance-report-grid td { padding: 0.8mm 0.5mm; }
  body[data-attendance-print] .attendance-report-row-head { max-width: 45mm; overflow: hidden; }
  body[data-attendance-print] .attendance-report-day { width: 6mm; }
  body[data-attendance-print] .attendance-report-dow { font-size: 6pt; min-width: 0; }
  body[data-attendance-print] .attendance-report-date { font-size: 7pt; }
  body[data-attendance-print] .attendance-report-mark {
    min-width: 0; padding: 0.3mm 1mm; font-size: 7.5pt;
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  body[data-attendance-print] .attendance-report-rate {
    font-size: 12pt; -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  body[data-attendance-print] .attendance-report-note { font-size: 7pt; color: #333; }
  body[data-attendance-print] .attendance-report-empty { font-size: 9pt; color: #333; }
}

/* The sheet, and the margin the twenty-four-column arithmetic is against: A4 is 210mm wide, 10mm
   either side leaves 190, a 45mm student column leaves 145, and 24 date columns at 6mm are 144.
   Letter is 6mm wider and therefore has room to spare. Outside the block above on purpose — an
   @page rule inside @media print is honoured unevenly, and this one is inert on screen anyway. */
@page { margin: 10mm; }
