/* The Schedule screen.
 *
 * It spent a while named `overview.css`, after a camp-wide timetable this file
 * used to style — that screen is gone (four territories run four different
 * clocks, so its rows said nothing), and the file carried the old name only
 * because index.html belonged to another phase at the time. It doesn't any
 * more, so the file sits where it should: `schedule.css`, beside
 * `schedule.js`.
 *
 * Every colour is a token from tokens.css (guardrail 10). Territory colours are
 * not written here at all — they are rows in the `territory` table, so the
 * screen paints them as inline styles straight from the API. Nothing here may
 * hold a raw colour value; `npm run check` enforces it.
 *
 * app.css owns the shared shapes (`.grid`, `.gcell`, `.agenda`, `.terr-box`)
 * and is loaded first, so what follows are the Schedule's own corrections to
 * them.
 */

/* --- the header ------------------------------------------------------------
 *
 * `.sched-head` is app.css's; during the burn it grows the design's Live door
 * on the right, which needs the row to be a row. */

.sched-head--burn {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: flex-end;
  justify-content: space-between;
}

.sched-livecta { margin-top: 0; }

/* --- the full-territory banner ---------------------------------------------
 *
 * Not a warning and not an error: a territory with every seat taken is the best
 * news the app has. Live green, and it says the same thing the schedule under
 * it shows. */
.banner--full {
  background: var(--live-bg);
  color: var(--ink);
}

/* --- the grid is not a scroll box ------------------------------------------
 *
 * This is the correction the whole screen hangs on. The grid used to live in
 * `overflow: auto; max-height: 66vh` — a scrollable pane inside a scrollable
 * page, which on a 900px window meant the week was read through a 594px slot
 * while two thirds of the screen sat still. Two scrollbars, two scroll
 * positions, and a mouse wheel whose meaning depended on where the pointer was.
 *
 * So: no scroll container anywhere between the sticky cells and the viewport,
 * and the page scrolls. `position: sticky` resolves against the nearest
 * *scrollport*, which is now the viewport, and — this is the part worth knowing
 * — a sticky grid item is held inside its **grid container**, not inside its own
 * grid area, so a header cell can travel the whole height of the table and a day
 * cell the whole width. Measured in Chrome before relying on it.
 *
 * Two rules make that true and both matter:
 *
 *   `overflow: clip` rather than `auto`. Clip is the one overflow value that
 *   does not create a scroll container, so it keeps the rounded corners off the
 *   square cells underneath without taking sticky away. It can only clip what
 *   overflows, and nothing does, because —
 *
 *   `width: fit-content` with `max-width: none`. `fit-content` already stops at
 *   the room available, so the seat tracks shrink from 150px toward their 112px
 *   floor and a seven-column admin grid still fits 1280px. Past that floor the
 *   box grows and pushes the *page* sideways — where the day column's `left: 0`
 *   catches it — rather than being clipped unreachably, which is what a
 *   `max-width: 100%` would have done to it. Nothing is ever cut off; the worst
 *   case is a sideways scroll with the day column pinned.
 */
/* The scrolling rules moved into app.css, where `.grid-wrap` is defined — they
 * were written here as overrides because that file belonged to another
 * workstream at the time, and an override that only exists to undo the rule
 * above it is a rule waiting to be read wrong. */

/* How far down the header row parks is not written here: the app bar is itself
 * sticky at the top of the viewport, so the table's header has to stop
 * underneath it rather than slide behind it, and how tall that bar is belongs to
 * app.css and changes at the phone breakpoint. schedule-grid.js measures it and
 * writes `top` on the header cells. app.css's `top: 0` is the fallback if that
 * ever fails, which puts the header at the top of the window rather than losing
 * it — the safe way round.
 *
 * The corner cell holds both edges, so it has to out-rank both. Every sticky
 * cell is already opaque — app.css paints the header `--card`, the day column
 * `--panel` and schedule-grid.js paints the header its territory's tint — which
 * is what lets them float over the rows they pass. */
.gcell--corner { z-index: 6; }
.gcell--colhead { z-index: 5; }
.gcell--rowhead { z-index: 4; }

/* --- the two header controls an admin gets ---------------------------------
 *
 * A row head opens or closes every unheld seat on that shift; a column head
 * opens or closes that seat all week. They are on the *schedule* because you
 * open capacity when you can see a gap, and the coverage bars that tell you
 * there is one are two centimetres to the left of the button.
 *
 * A camper never gets these: `schedule-grid.js` draws the same heads as plain
 * `<div>`s without the handlers, so there is nothing here for them to match.
 *
 * app.css's `button.gcell { cursor: pointer }` already carries the pointer, and
 * `.gcell` already resets the border and the font, so a `<button class="gcell">`
 * is pixel-identical to the div it replaces. All this adds is the hover and the
 * focus ring — an admin has to be able to tell a header they can press from one
 * they cannot, and the underline goes under the *label* rather than the whole
 * cell, which on a row head also carries the coverage meter.
 */
/* The mark that says a header is pressable. It is drawn in the corner rather
 * than written into the sub line, and that is measured: an admin's grid is seven
 * columns wide, so at 1280 `fit-content` has already shrunk every track and the
 * day column sits at ~186px. A " · seats…" on the sub line took the premium row
 * to 156px inside a 137px box and ellipsed away `✦2×` — the one mark that says
 * the shift counts twice. A corner glyph costs no inline width, so nothing else
 * on the row moves.
 *
 * `position: sticky` is already a positioned element, so the mark anchors to the
 * cell without any extra rule. The button carries an `aria-label` that spells
 * the gesture out, which is what a screen reader announces instead of this. */
.gcell--scale::after {
  content: '⋯';
  position: absolute;
  top: 1px;
  right: 6px;
  font-size: var(--size-kicker);
  line-height: 1;
  opacity: 0.55;
}

.gcell--scale:hover .gcell-text { text-decoration: underline; }
/* An inset rule rather than a background: a column head is painted inline from
 * its territory row, and an inline style beats any stylesheet — so a hover that
 * changed the background would work on one of the two heads and silently not on
 * the other. `currentColor` picks up whichever of the two it lands on. */
.gcell--scale:hover { box-shadow: inset 0 -3px 0 currentColor; }
.gcell--scale:hover::after { opacity: 1; }
.gcell--scale:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }

/* --- the agenda, on a phone ------------------------------------------------
 *
 * Days are open (see schedule-agenda.js), so outside the burn a day heading is
 * a heading and not a button. app.css's `.day-head` already resets the font and
 * the margin for it; all that is left is not pretending it is pressable. */
h3.day-head { cursor: default; }

/* --- the rest of camp's year -----------------------------------------------
 *
 * Build and strike, under the burn week. One section, two blocks, and the only
 * difference between a phone and a desktop is how many columns the blocks sit
 * in — the markup is drawn once (`schedule.js#campYear`), because neither is an
 * arrangement of the week's shifts and so neither belongs to the grid or the
 * agenda.
 *
 * Every class here is prefixed `sched-year-` or `yr-`: this stylesheet is loaded
 * into the SPA alongside eleven others, and `check-ui.js` refuses a bare class
 * defined in two of them for a reason — `live.css` and `admin.css` both owning
 * `.kicker` silently repainted eight of Live's headings.
 */

.sched-year { margin-top: var(--space-6); }

.sched-year-lede { max-width: 62ch; }

.sched-year-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
  align-items: start;
}

/* The one breakpoint the whole app spells this way (`check-ui.js` §5). Build and
 * strike are independent lists of comparable weight, so above it they sit side
 * by side rather than one under three screens of the other. */
@media (min-width: 768px) {
  .sched-year-grid { grid-template-columns: 1fr 1fr; gap: var(--space-4); }
}

/* The accent stripe is painted inline from the territory's own row — the same
 * indirection every other card on this screen uses, so `--t-build` and
 * `--t-strike` can desaturate for night mode while the stored hex cannot. */
.yr-card {
  padding: var(--space-4);
  background: var(--panel);
  border: 1px solid var(--line);
  border-left: var(--border-accent) solid var(--line);
  border-radius: var(--radius-card);
}

.yr-card-title {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--size-h2);
  font-weight: var(--weight-semibold);
  color: var(--ink);
}

.yr-card-emoji { font-size: var(--size-body); }

.yr-card-times {
  margin: var(--space-1) 0 0;
  font-size: var(--size-small);
  color: var(--soft);
}

.yr-card-lede {
  margin: var(--space-2) 0 var(--space-3);
  font-size: var(--size-small);
  color: var(--soft);
}

.yr-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.yr-item {
  padding: var(--space-2) var(--space-3);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
}

/* Compound, never a bare `.is-mine`: this file is linked into the SPA beside
 * eleven others and a global state class repaints all of them (`check-ui.js` §2). */
.yr-item.is-mine { border-color: var(--ink); }

.yr-item-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-1) var(--space-2);
}

.yr-item-title {
  font-weight: var(--weight-semibold);
  color: var(--ink);
}

/* The credit an arrival earns, and the hours a strike shift runs. Right-aligned
 * on a wide row, and allowed to wrap under the title on a narrow one rather than
 * squeezing the title into two words. */
.yr-item-credit {
  margin-left: auto;
  font-size: var(--size-small);
  color: var(--soft);
  font-variant-numeric: tabular-nums;
}

.yr-item-meta {
  margin: var(--space-1) 0 0;
  font-size: var(--size-small);
  color: var(--soft);
}

.yr-item-tasks {
  margin: var(--space-1) 0 0;
  font-size: var(--size-micro);
  color: var(--soft);
}

/* Names run in as many columns as the card is wide enough for, with no
 * breakpoint of its own: a phone card is ~330px and gets one, a desktop card in
 * the two-column layout above is ~590px and gets two. Measured, because it is
 * the difference between a build week of 33 arrivals adding 1,600px to this
 * screen and adding 800px — and this project has shipped a 12,500px phone page
 * before. Each row is still `personRow`, so the 48px tap floor is untouched. */
.yr-people {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--space-1);
  margin: var(--space-2) 0 0;
  padding: 0;
  list-style: none;
}

.yr-crew { margin-top: var(--space-4); }

/* The control that opens a list of names. Its own row so the button keeps the
 * whole of its 48px and does not sit shoulder to shoulder with the list under
 * it — the gap between two things a dusty thumb has to tell apart. */
.yr-toggle { margin: 0 0 var(--space-2); }

.yr-refund {
  margin: var(--space-3) 0 0;
  padding: var(--space-2) var(--space-3);
  font-size: var(--size-small);
  color: var(--ink);
  background: var(--card);
  border-radius: var(--radius-control);
}

.yr-refund.is-earned {
  background: var(--live-bg);
  color: var(--ink);
}

.yr-note {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-3) 0 0;
  font-size: var(--size-small);
  color: var(--soft);
}

.yr-note--gap { color: var(--gap); }

/* The door to build and strike.
 *
 * They live under the burn week because neither has seats, and on a phone that
 * put them 6,394px down an 8,416px page — rendered and unreachable, which camp
 * reported as "I still don't see the strike and build schedule". This is the
 * pointer at the top; the section itself has not moved. */
.sched-yearjump {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin: var(--space-3) 0;
}

.sched-yearjump-note { flex: 1 1 12rem; }
