/* grantsheets — light mode follows the STAR Education portal (purple chrome,
   green actions, Open Sans); dark mode keeps the Google/Material palette. */
:root{
  --bg:#f4f4f6;--surface:#fff;--line:#e3e3e8;--ink:#333;--muted:#6b6b73;
  --blue:#2c6fbb;--blue-bg:#e7f0f9;--green:#3f9c4a;--green-bg:#e9f5ea;--red:#c9302c;--red-bg:#fbeaea;
  --purple:#8e44ad;--purple-bg:#f4e9f8;--teal:#17796f;--teal-bg:#e4f5f3;--orange:#c26100;--orange-bg:#fdefe1;
  --yellow:#e8a400;--chip:#f2f1f5;--stripe:#faf9fb;--hover:#f3eefa;
  --shadow:0 1px 2px 0 rgba(40,20,60,.12),0 1px 3px 1px rgba(40,20,60,.08);
  /* STAR brand + the chrome it paints */
  --brand:#5b2d8e;--brand-ink:#fff;--brand-bg:#f3eefa;
  --topbar-bg:var(--brand);--topbar-ink:#fff;--topbar-line:#4a2475;
  --thead-bg:var(--brand);--thead-ink:#fff;
}
/* ---- dark mode (overrides the variables; everything else uses them) ---- */
html.dark{
  --bg:#202124;--surface:#2a2b2e;--ink:#e8eaed;--muted:#9aa0a6;
  --blue:#8ab4f8;--blue-bg:#1f3b5f;--green:#81c995;--green-bg:#1e3a29;--red:#f28b82;--red-bg:#3f2726;
  --purple:#c58af9;--purple-bg:#33234a;--teal:#78d9ec;--teal-bg:#123b40;--orange:#fcad70;--orange-bg:#43301c;
  --chip:#3c4043;--stripe:#36373b;--hover:#3e4147;
  /* Row separation has to be carried by the border on a dark surface: the
     zebra stripe alone was 1.03:1 against the panel, which is invisible. */
  --line:#45494f;
  --shadow:0 1px 2px 0 rgba(0,0,0,.6),0 1px 3px 1px rgba(0,0,0,.4);
  /* Dark mode keeps its own chrome: plain surface bar, muted table headers. */
  --brand:#c58af9;--brand-ink:#202124;--brand-bg:#33234a;
  --topbar-bg:var(--surface);--topbar-ink:var(--ink);--topbar-line:var(--line);
  --thead-bg:var(--chip);--thead-ink:var(--muted);
}
html.dark .ribbon{color:#8ab4f8;border-bottom-color:#28425f}
html.dark .flag-card{border-color:#5c3a37}
html.dark .flag-banner{border-color:#5c3a37}

*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);
  font-family:'Open Sans',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:15px}
/* Figures stay tabular so columns line up, but in the body face rather than a
   mono one -- the portal shows plain numerals. */
.px{font-weight:600;font-variant-numeric:tabular-nums}

/* ---- top bar ---- */
.topbar{display:flex;justify-content:space-between;align-items:center;
  padding:0 24px;min-height:56px;gap:8px;flex-wrap:wrap;
  background:var(--topbar-bg);border-bottom:1px solid var(--topbar-line)}
.wordmark{display:flex;align-items:center;gap:10px;font-size:18px;font-weight:700;color:var(--topbar-ink)}
.dot{width:12px;height:12px;border-radius:50%;background:var(--topbar-ink);opacity:.9}
.link{background:none;border:0;color:var(--topbar-ink);cursor:pointer;font-size:14px;font-weight:600;padding:8px}
.link:hover{text-decoration:underline}
.topbar-actions{display:flex;align-items:center;gap:4px;flex-wrap:wrap}
.linkdanger{color:var(--red)}
.up{color:var(--green);font-weight:600}.down{color:var(--red);font-weight:600}

.ribbon{background:var(--blue-bg);color:#174ea6;padding:10px 24px;font-size:13px;border-bottom:1px solid #d2e3fc}
.ribbon b{font-weight:700}

.wrap{max-width:1440px;margin:0 auto;padding:28px 32px}
.label{font-size:11px;font-weight:700;letter-spacing:.8px;color:var(--muted);margin-bottom:14px;text-transform:uppercase}
h1{font-size:28px;font-weight:400;margin:0 0 22px;color:var(--ink)}
/* Content links (in hints, prose, table cells) had no rule of their own, so they
   fell back to the browser default #0000EE -- effectively unreadable on the dark
   panel background. Components with their own colour (.link, .back, .tab) still
   win on specificity. */
a{color:var(--blue)}
.hint{font-size:14px;color:var(--muted);margin:0 0 14px}
.hint a{font-weight:500}

/* ---- KPI tiles ---- */
/* ---- total-enrolled banner ---- */
.enroll-hero{display:flex;gap:28px;align-items:center;flex-wrap:wrap;
  background:var(--surface);border:1px solid var(--line);border-left:5px solid var(--green);
  border-radius:12px;padding:20px 24px;box-shadow:var(--shadow);margin-bottom:16px}
.enroll-total{min-width:150px}
.enroll-num{font-size:42px;line-height:1;color:var(--green)}
.total-hero{border-left-color:var(--blue)}
.total-hero .enroll-num{color:var(--blue)}
.enroll-cap{font-size:12px;color:var(--muted);font-weight:500;margin-top:6px}
.enroll-split{flex:1;min-width:260px}
.enroll-bar{display:flex;height:18px;border-radius:9px;overflow:hidden;background:var(--chip)}
.seg{height:100%}.seg-teal{background:var(--teal)}.seg-purple{background:var(--purple)}
.enroll-legend{display:flex;gap:24px;margin-top:10px;font-size:13px;color:var(--ink)}
.sw{display:inline-block;width:11px;height:11px;border-radius:2px;margin-right:6px;vertical-align:middle}
.sw-teal{background:var(--teal)}.sw-purple{background:var(--purple)}

.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:14px;margin-bottom:8px}
.kpis-4{grid-template-columns:repeat(4,1fr)}
.kpi.cl-red{border-top:4px solid var(--red)}.kpi.cl-red .kpi-num{color:var(--red)}
a.kpi.klink{text-decoration:none;color:inherit;cursor:pointer;transition:box-shadow .1s}
a.kpi.klink:hover{box-shadow:var(--shadow),0 0 0 2px var(--blue)}
.kpi{background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:18px;box-shadow:var(--shadow)}
.kpi-num{font-size:30px;color:var(--ink)}
.kpi-label{font-size:12px;color:var(--muted);margin-top:8px;font-weight:500}
@media(max-width:820px){.kpis{grid-template-columns:repeat(2,1fr)}}

/* ---- narrow screens ----
   The top bar used to be a fixed 56px with wrapping links, so on a phone the
   second row of links was clipped and unreachable. It now grows instead, and
   below 720px the wordmark sits on its own line with the links wrapping under
   it. */
@media(max-width:720px){
  .topbar{padding:8px 14px;align-items:flex-start;flex-direction:column}
  .topbar-actions{width:100%;gap:2px;padding-bottom:6px}
  .link{padding:7px 8px;font-size:13px}
  .wordmark{font-size:16px;padding-top:8px}
  .wrap{padding:16px 14px}
  h1{font-size:24px}
  .panel{padding:16px;border-radius:10px}
  .enroll-hero{flex-direction:column;align-items:stretch}
  .tabs{gap:2px}
  .tab{padding:8px 12px;font-size:13px}
}
@media(max-width:520px){
  .kpis{grid-template-columns:1fr}
  .roll{font-size:14px}
  .roll tbody td{padding:12px 10px}
}
/* ---- download buttons (outlined, like the portal's Excel / Pdf pair) ---- */
.delta{display:inline-block;margin-left:8px;padding:1px 7px;border-radius:999px;
  font-size:12px;font-weight:700;vertical-align:1px}
.delta.up{color:var(--green);background:var(--green-bg)}
.delta.down{color:var(--red);background:var(--red-bg)}
.staffbox{width:64px;padding:5px 6px;border:1px solid var(--line);border-radius:6px;
  background:var(--surface);color:var(--ink);font:inherit;font-size:14px;text-align:right}
.staffbox:focus{outline:2px solid var(--brand);outline-offset:1px}
.staffbox.derived{color:var(--muted);background:var(--chip);font-style:italic}
.derived-key{display:inline-block;width:11px;height:11px;border-radius:3px;
  background:var(--chip);border:1px solid var(--line);vertical-align:-1px}
.staffbox.saved{border-color:var(--green);background:var(--green-bg)}
.staffbox.failed{border-color:var(--red);background:var(--red-bg)}

.contact-sub{font-size:13px;color:var(--ink);margin-top:2px}
.contact-phone{display:block;font-size:13px;color:var(--brand);text-decoration:none;margin-top:4px;font-weight:600}
.contact-phone:hover{text-decoration:underline}

.picks{display:flex;gap:14px;flex-wrap:wrap}
.pick{display:inline-flex;align-items:center;gap:6px;padding:8px 12px;border:1px solid var(--line);
  border-radius:8px;background:var(--surface);font-size:14px;cursor:pointer}
.pick:hover{border-color:var(--brand)}

.exports{display:flex;gap:10px;margin:0 0 16px;flex-wrap:wrap}
.xbtn{display:inline-block;padding:9px 18px;border:1px solid var(--brand);border-radius:6px;
  background:transparent;color:var(--brand);font:inherit;font-size:14px;font-weight:600;
  cursor:pointer;text-decoration:none}
.xbtn:hover{background:var(--brand-bg)}

/* ---- print / save-as-PDF ----
   Drop the app chrome and print the content on white. */
@media print{
  .topbar,.ribbon,.exports,.tabs,.back,.upload-row,.linkdanger{display:none!important}
  body{background:#fff;color:#000;font-size:11pt}
  .wrap{padding:0;max-width:none}
  .panel{border:1px solid #bbb;box-shadow:none;break-inside:avoid;overflow:visible;margin-top:12px}
  .enroll-hero,.total-hero{box-shadow:none;border:1px solid #bbb}
  /* ** ⚠ A CAPPED PANEL PRINTS AS A CLIPPED TABLE, AND SILENTLY. ** This app is
     printed -- the panel rule above already says `overflow:visible` for exactly
     that reason -- and a paper page that quietly loses rows is the worst failure
     either app has. The cap and the sticky header both come off here: paper does
     not scroll and needs no header that follows. */
  .panel:has(table.roll){max-height:none!important;overflow:visible!important}
  .roll thead th{background:#eee!important;color:#000!important;position:static!important;
    -webkit-print-color-adjust:exact;print-color-adjust:exact}
  /* ** A LONG TABLE REPEATS ITS HEADER ON EVERY SHEET OF PAPER. ** 46 ELOP
     schools do not fit on one page, and page two of "10  27  48" with no
     headings is four columns nobody can tell apart -- a printed page that
     cannot be read is the same failure as one that loses rows, which is why
     the cap above comes off here. `table-header-group` is what makes the
     browser redraw thead after each page break. */
  .roll thead{display:table-header-group}
  .roll tr{break-inside:avoid}
  .roll{font-size:10pt}
  .roll tbody td{padding:6px 8px}
  .chart{overflow:visible}
  .bar{-webkit-print-color-adjust:exact;print-color-adjust:exact}
  a{color:#000;text-decoration:none}
  h1{font-size:18pt;margin:0 0 8px}
}

/* Tables are the one thing that cannot usefully reflow -- let them scroll
   inside their panel rather than pushing the page sideways. */
.panel{overflow-x:auto}
/* ** THE COLUMN HEADERS FOLLOW YOU DOWN A LONG TABLE (2026-09-28). **
   Asked: "also in grantsheet make the heardds follow you too".

   ** `.roll thead th` HAS CARRIED `position:sticky;top:0` ALL ALONG AND IT DID
   NOTHING. ** Sticky sticks to the nearest SCROLLING ANCESTOR, and the rule above
   makes every panel one: `overflow-x:auto` computes the other axis from `visible`
   to `auto`. With no height the panel is exactly as tall as its contents, so it
   never scrolls, so the header has nowhere to stick however the page moves. The
   rule was present, inert, and looked done.

   A HEIGHT is what turns that on: a panel holding a table scrolls INSIDE itself
   and the header stays above the rows. `:has()` keeps it to the panels that
   actually hold one -- a panel of prose must not become a scroll box. Anything
   shorter than the cap never reaches it and looks exactly as it did.

   The 32 tables in 13 views have no shared wrapper to hang this on, which is why
   it is the panel rather than the table. */
.panel:has(table.roll){max-height:82vh}
.roll thead th{z-index:2}
/* colored KPI accents */
.kpi.cl-blue{border-top:4px solid var(--blue)}.kpi.cl-blue .kpi-num{color:var(--blue)}
.kpi.cl-green{border-top:4px solid var(--green)}.kpi.cl-green .kpi-num{color:var(--green)}
.kpi.cl-teal{border-top:4px solid var(--teal)}.kpi.cl-teal .kpi-num{color:var(--teal)}
.kpi.cl-purple{border-top:4px solid var(--purple)}.kpi.cl-purple .kpi-num{color:var(--purple)}
.kpi.cl-orange{border-top:4px solid var(--orange)}.kpi.cl-orange .kpi-num{color:var(--orange)}

/* ---- search ---- */
.search{width:100%;padding:12px 16px;border:1px solid var(--line);border-radius:24px;
  background:var(--surface);color:var(--ink);font-size:15px;box-shadow:var(--shadow)}
.search:focus{outline:none;border-color:var(--blue);box-shadow:0 0 0 1px var(--blue)}
.results{margin:8px 0}
.s-item{display:flex;justify-content:space-between;align-items:center;padding:12px 16px;
  border:1px solid var(--line);border-radius:10px;margin-top:8px;text-decoration:none;color:var(--ink);background:var(--surface)}
.s-item:hover{background:var(--hover)}

/* ---- cards ---- */
.panel{background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:22px;margin-top:18px;box-shadow:var(--shadow)}

/* ---- flag cards ---- */
.flags{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px}
.flag-card{display:flex;flex-direction:column;gap:6px;padding:14px 16px;border:1px solid #f3c2bd;
  background:var(--red-bg);border-radius:10px;text-decoration:none;color:var(--ink)}
.flag-card b{color:var(--red)}
.reason{font-size:12px;color:var(--red)}
.ok{color:var(--green);font-weight:500}

/* ---- chips ---- */
.tag{display:inline-block;font-size:12px;font-weight:500;color:var(--muted);background:var(--chip);
  border-radius:16px;padding:4px 12px}
.tag.cl-blue{background:var(--blue-bg);color:var(--blue)}
.tag.cl-green{background:var(--green-bg);color:var(--green)}
.tag.cl-purple{background:var(--purple-bg);color:var(--purple)}
.tag.cl-orange{background:var(--orange-bg);color:var(--orange)}
.tag.cl-teal{background:var(--teal-bg);color:var(--teal)}
.tag.cl-grey{background:var(--chip);color:var(--muted)}
.tag.warn{background:var(--orange-bg);color:var(--orange)}
.viewonly{color:#fff;background:var(--muted)}

/* ---- tables: zebra rows + colored header + hover ---- */
.roll{width:100%;border-collapse:collapse;font-size:15px;color:var(--ink)}
.roll thead th{background:var(--thead-bg);color:var(--thead-ink);font-weight:600;font-size:13px;
  text-align:left;padding:13px 14px;border-bottom:2px solid var(--line);position:sticky;top:0}
/* ** ⚠ THIS USED TO PUT `cursor:pointer` ON EVERY HEADING WHILE ONE TABLE
   SORTED. ** The dashboard's `#schooltable` had an inline sorter; the other 31
   tables in this app had the pointer and did nothing. A heading that looks
   clickable and is not is worse than no affordance at all -- it reads as a broken
   page rather than a plain one.
   `public/sort.js` sets `.sortable-th` on the headings it actually attaches to,
   so the cursor now means something. Asked 2026-09-28: "alls table sort by
   clicking and show when one is be pushed and whar direction its going". */
.roll thead th.sortable-th{cursor:pointer;user-select:none}
.roll thead th.sortable-th:hover{text-decoration:underline}
.roll thead th.sortable-th:focus-visible{outline:2px solid var(--brand);outline-offset:-2px}
/* colored header blocks help the eye map each metric column */
/* Program-coloured header cells. The colour is an underline rather than a fill,
   so they read as part of the header bar instead of gaps punched in it -- and
   the same rule works on the light purple bar and the dark muted one. */
.roll th.col-green{box-shadow:inset 0 -3px 0 var(--green)}
.roll th.col-teal{box-shadow:inset 0 -3px 0 var(--teal)}
.roll th.col-purple{box-shadow:inset 0 -3px 0 var(--purple)}
.roll th.col-orange{box-shadow:inset 0 -3px 0 var(--orange)}
.roll th.col-blue{box-shadow:inset 0 -3px 0 var(--blue)}
/* light vertical dividers so columns separate cleanly */
.roll td,.roll th{border-right:1px solid var(--line)}
.roll td:last-child,.roll th:last-child{border-right:0}
.roll th:first-child{border-top-left-radius:8px}.roll th:last-child{border-top-right-radius:8px}
.roll tbody td{padding:16px 14px;border-bottom:2px solid var(--line)}
.roll tbody tr:nth-child(even){background:var(--stripe)}
.roll tbody tr:hover{background:var(--hover)}
.roll tbody tr{transition:background .1s}
.roll tfoot td{padding:12px;border-top:2px solid var(--line);font-weight:500;background:var(--surface)}
.roll a{color:var(--blue);font-weight:500;text-decoration:none}
.roll a:hover{text-decoration:underline}
.bad{color:var(--red);font-weight:500}

/* ---- reports: upload + trend chart ---- */
.upload-row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}
.file{flex:1;min-width:220px;padding:10px;border:1px solid var(--line);border-radius:8px;background:var(--surface);color:var(--ink)}
.btn-inline{width:auto;padding:11px 20px}
.muted{color:var(--muted);font-size:13px}
/* padding-top is HEADROOM FOR THE HOVER TIP, not decoration: a box that
   scrolls on one axis clips on the other, so the tip is drawn inside this
   padding. Shrink it and the tooltip vanishes behind the panel. */
.chart{display:flex;align-items:flex-end;gap:16px;min-height:190px;padding:52px 4px 10px;overflow-x:auto}
.bar-col{display:flex;flex-direction:column;align-items:center;gap:6px;min-width:64px}
.bar-val{font-size:12px;color:var(--ink)}
.bar{width:44px;border-radius:6px 6px 0 0;background:var(--muted)}
.bar.cl-green{background:var(--green)}.bar.cl-blue{background:var(--blue)}.bar.cl-grey{background:var(--muted)}
.bar.cl-orange{background:var(--orange)}.bar.cl-purple{background:var(--purple)}
/* Program switcher used on the /program and /growth pages. */
.tabs{display:flex;gap:6px;margin:0 0 18px;border-bottom:1px solid var(--line);flex-wrap:wrap}
.tab{padding:9px 18px;font-size:14px;font-weight:500;color:var(--muted);text-decoration:none;
  border:1px solid transparent;border-bottom:0;border-radius:8px 8px 0 0;position:relative;top:1px}
.tab:hover{color:var(--ink);background:var(--surface)}
.tab.active{color:var(--ink);background:var(--surface);border-color:var(--line);font-weight:700}
.tab.active.cl-green{box-shadow:inset 0 3px 0 var(--green)}
.tab.active.cl-blue{box-shadow:inset 0 3px 0 var(--blue)}
.tab.active.cl-purple{box-shadow:inset 0 3px 0 var(--purple)}
.bar.cl-purple{background:var(--purple)}.bar.cl-orange{background:var(--orange)}.bar.cl-teal{background:var(--teal)}
.bar-label{font-size:11px;color:var(--muted);text-align:center;line-height:1.5}

/* ---- contacts ---- */
.contact-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:14px}
.site-cluster{margin-bottom:20px}.site-cluster .tag{margin-bottom:12px;display:inline-block}
.contact-card{display:flex;flex-direction:column;gap:8px;align-items:flex-start;padding:16px;
  border:1px solid var(--line);border-radius:10px;background:var(--surface);text-decoration:none;color:var(--ink)}
.contact-card:hover{background:var(--hover);border-color:var(--blue)}
.contact-name{font-weight:500;font-size:16px}
.contact-email{font-size:14px;color:var(--blue);word-break:break-all}

/* ---- site page ---- */
.hide-form{display:inline;margin-left:auto}
.hide-form .link{font-size:13px;color:var(--muted)}
.hide-form .link:hover{color:var(--red)}
.site-head{display:flex;align-items:center;gap:10px;margin:6px 0 18px;flex-wrap:wrap}
.site-name{font-size:22px;font-weight:500;color:var(--ink)}
.c-row{display:flex;justify-content:space-between;padding:15px 0;border-bottom:2px solid var(--line)}
.c-row:last-child{border-bottom:0}
.c-row b{color:var(--ink)}
.back{color:var(--blue);text-decoration:none;font-size:14px;font-weight:500}
.back:hover{text-decoration:underline}

.flag-banner{background:var(--red-bg);border:1px solid #f3c2bd;color:var(--red);
  border-radius:10px;padding:12px 16px;margin-bottom:10px}

/* ---- login ----
   Warm and plain-spoken on purpose -- this is the first thing a site office
   sees, not a dashboard. STAR purple (--brand) is the same purple the rest of
   the app (and starflow beside it) already uses, so the sign-in page and the
   app it opens into feel like one thing. Legible in both themes: every colour
   here is a token that already has a dark-mode override above, and nothing
   below is a new hardcoded colour. Usable at 400px: the card is fluid up to
   its max-width and the body's side padding never collapses to zero.

   ** `.login` MUST STAY THAT EXACT CLASS NAME. ** starflow's grantsheets/access.js
   recognises a refused request by `<div class="login">` in the returned HTML --
   see the long comment above loginPage() in auth.js before touching this. */
body.login-body{min-height:100vh;margin:0;display:flex;align-items:center;justify-content:center;
  padding:24px;box-sizing:border-box;
  background:radial-gradient(circle at 50% -10%,var(--brand-bg) 0%,var(--bg) 65%)}
.login{width:100%;max-width:360px;margin:0;text-align:center;padding:36px 30px 32px;
  background:var(--surface);border:1px solid var(--line);border-radius:18px;box-shadow:var(--shadow)}
.login-badge{width:46px;height:46px;margin:0 auto 14px;border-radius:50%;
  background:var(--brand-bg);display:flex;align-items:center;justify-content:center;font-size:22px;line-height:1}
.brand{font-size:22px;font-weight:700;color:var(--ink);margin-bottom:4px;display:block}
.tagline{color:var(--muted);font-size:13px;margin:0 0 22px;line-height:1.4}
.field{width:100%;padding:12px 14px;border:1px solid var(--line);border-radius:10px;margin-bottom:14px;
  font-size:15px;font-family:inherit;background:var(--surface);color:var(--ink);box-sizing:border-box}
.field::placeholder{color:var(--muted)}
.field:focus{outline:none;border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-bg)}
.btn{width:100%;padding:12px;border:0;border-radius:10px;cursor:pointer;font-weight:700;font-size:15px;
  font-family:inherit;color:var(--brand-ink);background:var(--brand)}
.btn:hover{filter:brightness(1.12);box-shadow:var(--shadow)}
.btn:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.err{color:var(--red);background:var(--red-bg);border-radius:8px;padding:8px 12px;font-size:13px;margin:0 0 16px}

/* Sign in with Google -- an outlined button beside the password box, not a
   second primary action: the password is still how the people list gets set up.
   `.btn` is width:100% and an <a> is inline, so this needs display:block. */
.btn.google{display:block;background:var(--surface);color:var(--ink);
  border:1px solid var(--line);text-decoration:none;text-align:center;margin-bottom:6px;
  box-sizing:border-box}
.btn.google:hover{filter:none;background:var(--chip);box-shadow:var(--shadow)}
.btn.google:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
/* A divider with a rule on either side reads calmer than bare text between
   two buttons -- both rules are `--line`, which already has a dark override. */
.or{display:flex;align-items:center;gap:10px;color:var(--muted);font-size:11px;
  letter-spacing:.08em;text-transform:uppercase;margin:16px 0 14px}
.or::before,.or::after{content:'';flex:1;height:1px;background:var(--line)}
/* Who you are signed in as, in the top bar beside Sign out. */
.whoami{color:var(--muted);font-size:13px;margin-right:10px}

/* ---- in-app help ---- */
details.help summary{cursor:pointer;list-style:none;padding:2px 0}
details.help summary::-webkit-details-marker{display:none}
details.help summary .label{margin:0}
details.help summary::before{content:'▸ ';color:var(--muted)}
details.help[open] summary::before{content:'▾ '}
.faq{border-top:1px solid var(--line);padding:8px 0}
.faq:first-of-type{border-top:0}
.faq summary{cursor:pointer;font-size:14px;font-weight:600;padding:4px 0;list-style:none}
.faq summary::-webkit-details-marker{display:none}
.faq summary::before{content:'+ ';color:var(--brand);font-weight:700}
.faq[open] summary::before{content:'– '}
.faq p{margin:6px 0 8px 14px}
@media print{details.help{display:none}}

/* ---- home page: the two big figures side by side ----
   Stacked, they pushed everything else below the fold. */
.hero-pair{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px}
.hero-pair .enroll-hero{margin-bottom:0;height:100%}
@media(max-width:1100px){.hero-pair{grid-template-columns:1fr}}

/* Reference the page carries but nobody acts on daily. */
details.quiet{margin:0 0 14px}
details.quiet summary{cursor:pointer;list-style:none}
details.quiet summary::-webkit-details-marker{display:none}
details.quiet summary::before{content:'▸ ';color:var(--muted)}
details.quiet[open] summary::before{content:'▾ '}

/* ---- home page stats ----
   Four full-width banners and a KPI row used the whole first screen before any
   data. One panel, grouped, with every figure kept. */
.stats-panel{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:20px 28px}
.statgroup .label{margin-bottom:8px}
.stats{display:flex;gap:18px;flex-wrap:wrap;align-items:flex-start}
.stat{display:flex;flex-direction:column;gap:1px;text-decoration:none;color:inherit;position:relative}
/* Forces the next figure onto a new line -- see the grouping in dashboard.js. */
.stats-break{flex-basis:100%;height:0;margin:0}
/* A supporting count, not a headline -- it sits beside the figures it qualifies
   rather than claiming a line of its own. */
.stat.mini .stat-num{font-size:14px;line-height:1.5;color:var(--muted)}
.stat.mini .stat-label{font-size:10px}
a.stat{padding-right:12px;border-radius:6px}
a.stat:hover .stat-num{text-decoration:underline}
.stat-num{font-size:21px;font-weight:600;line-height:1.15}
.stat-label{font-size:11px;color:var(--muted);font-weight:600;letter-spacing:.3px}
.stat-go{position:absolute;right:0;top:2px;font-size:13px;color:var(--muted)}
.stat.cl-green .stat-num{color:var(--green)}
.stat.cl-blue .stat-num{color:var(--blue)}
.stat.cl-purple .stat-num{color:var(--purple)}
.stat.cl-teal .stat-num{color:var(--teal)}
.stat.cl-orange .stat-num{color:var(--orange)}
.stat.cl-red .stat-num{color:var(--red)}
.stat-bar{display:flex;height:6px;border-radius:3px;overflow:hidden;background:var(--chip);margin:10px 0 0}
.stat-note{font-size:12px;color:var(--muted);margin:8px 0 0}
/* A long note is a footnote to the whole panel, not to one 345px column --
   trapped in a cell it wraps to nine lines and stretches every group beside it. */
.stat-note.wide{grid-column:1/-1;margin:0}
@media(max-width:640px){.stats{gap:14px}.stat-num{font-size:18px}}

/* ---- staff directory ----
   Forty-eight sites expanded is 744 rows on one page. It is a directory: you
   come to look one site up, not to read all of them. */
details.panel.site{padding:0}
details.panel.site > summary{cursor:pointer;list-style:none;padding:14px 18px;
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:0}
details.panel.site > summary::-webkit-details-marker{display:none}
details.panel.site > summary::before{content:'▸';color:var(--muted);font-size:13px}
details.panel.site[open] > summary::before{content:'▾'}
details.panel.site > summary:hover{background:var(--hover)}
details.panel.site[open] > summary{border-bottom:1px solid var(--line)}
details.panel.site .site-body{padding:4px 18px 16px}
details.panel.site .site-name{margin:0}
@media print{details.panel.site{padding:14px 18px}details.panel.site .site-body{display:block}}

/* Finding a school is the main thing people come to do, so it leads. */
.findbar{margin:0 0 14px}
.findbar .search{font-size:16px;padding:14px 18px;border-width:2px}

/* The school-year history table on /site/:id (asked 2026-09-11).
   Day rows are rendered OPEN and folded by script, so a script that never runs
   leaves every reading visible rather than hiding it with no way back. */
.hist .fold {
  background: none; border: 0; padding: 0; font: inherit; color: var(--blue);
  cursor: pointer; text-align: left;
}
.hist .fold::before { content: '▾ '; display: inline-block; width: 1em; }
.hist .fold[aria-expanded="false"]::before { content: '▸ '; }
.hist .fold:hover { text-decoration: underline; }
.hist .m-row > td { font-weight: 600; }
.hist .m-row.empty > td { font-weight: 400; color: var(--muted); }
.hist .d-row > td { font-weight: 400; }
.hist .d-row .day { padding-left: 2.2em; color: var(--muted); }
.hist.js-fold .d-row { display: none; }
.hist.js-fold .d-row.shown { display: table-row; }

/* WHICH COLUMN THE TABLE IS SORTED BY, AND WHICH WAY (2026-09-18).
   Asked for because the table gave no indication at all -- and clicking a
   heading twice did nothing, so the order was both invisible and unchangeable.
   The arrow is written by the script; this only makes the sorted heading
   readable as the sorted one. */
#schooltable th[onclick] { cursor: pointer; user-select: none; }
#schooltable th[onclick]:hover { text-decoration: underline; }

/* ** NO COLOUR CHANGES WHEN YOU SORT. THE ARROW IS THE WHOLE INDICATOR. **
   Asked 2026-09-18: "can we not keep the color change when I pick a new sort".
   Two earlier attempts both made it worse. A 10% tint could not be told apart
   from the PERMANENT coloured bars six of these headings carry (th.col-teal and
   friends, `inset 0 -3px 0`), which mark the column group, never move, and look
   exactly like a selected-tab underline. Making the tint solid only added a
   third competing colour.
   So: nothing about the colour of a heading changes when you sort. Only one
   heading can ever hold an arrow, which makes it unambiguous on its own, and
   the coloured bars are left to mean the one thing they always meant. */
#schooltable th.sorted { font-weight: 700; }
.sort-arrow { font-size: .85em; opacity: .9; }

/* The People table sorts by CLICKING A HEADER, and the headers are LINKS rather
   than an onclick -- the sort lives in the URL like the type filter and the
   search, so it survives a reload and can be sent to somebody. They must still
   READ as headers: the header colour, not the link blue. */
#peopletable th a { color: inherit; text-decoration: none; display: inline-block; }
#peopletable th a:hover { text-decoration: underline; }
#peopletable th.sorted { font-weight: 700; }


/* THE REPORTS MENU (asked 2026-09-23: "put the auto pull under the reports
   tab"). ** A DELIBERATE COPY of starflow's nav group **, which has carried its
   four menus since 2026-09-12 -- same markup, same behaviour, and it works with
   no JavaScript at all. If you change one, change the other in the same breath.
   The phone rule at the bottom is the important half: a dropdown pinned to the
   viewport edge runs off it, so on a narrow screen it sits in the flow. */
.navgroup{position:relative;display:inline-block}
.navgroup>summary{list-style:none;cursor:pointer;user-select:none}
.navgroup>summary::-webkit-details-marker{display:none}
.navgroup .caret{opacity:.6;font-size:11px}
.navgroup[open]>summary{background:rgba(255,255,255,.18);border-radius:6px}
.navmenu{position:absolute;top:100%;left:0;z-index:40;min-width:220px;
  background:var(--surface);border:1px solid var(--line);border-radius:10px;
  box-shadow:0 10px 30px rgba(0,0,0,.18);padding:6px;margin-top:6px}
.navmenu a{display:block;padding:9px 12px;border-radius:7px;color:var(--ink);
  text-decoration:none;font-size:14px;white-space:nowrap}
.navmenu a:hover{background:var(--stripe)}
/* On a phone the bar wraps, so a dropdown pinned to the viewport edge would run
   off it. Full width and in the flow instead. */
@media (max-width:720px){
  .navgroup{display:block;width:100%}
  .navmenu{position:static;box-shadow:none;min-width:0}
}

/* ---- CHARTS (2026-09-24) -------------------------------------------------
   "bring back some charts or pie graphs", and a better ELOP attendance cell
   than a plain average. NO CHARTING LIBRARY — see views/charts.js for why.   */

/* The spark: five bars inside a table cell, showing direction. Decoration over
   a figure that is already printed beside it. */
.spark{display:inline-flex;align-items:flex-end;gap:2px;height:20px;margin:3px 0 1px;vertical-align:bottom}
.spark i{display:block;width:5px;border-radius:2px 2px 0 0;background:var(--orange);opacity:.85}
.cell-lead{display:block;white-space:nowrap}
.cell-note{display:block;font-size:11px;color:var(--muted);line-height:1.4;white-space:nowrap}
/* A thin average must LOOK thin — that is the whole point of the change. */
.cell-note.warn{color:var(--orange);font-weight:600}
.up{color:var(--green)}.down{color:var(--orange)}

/* A bar split into the two bands, so the chart answers "who" as well as
   "how many". --k is the TK/K share. */
.bar.split{background:linear-gradient(to top,var(--teal) 0,var(--teal) var(--k),var(--purple) var(--k),var(--purple) 100%)}

/* The pie is one element and a conic-gradient — no script, no SVG. */
.pie-wrap{display:flex;align-items:center;gap:22px;flex-wrap:wrap}
.pie{width:150px;height:150px;border-radius:50%;flex:0 0 auto;
  background:conic-gradient(var(--teal) 0 var(--k),var(--purple) var(--k) 100%)}
.pie-key{min-width:220px;flex:1 1 220px}
.key{display:inline-block;width:11px;height:11px;border-radius:3px;vertical-align:-1px;margin-right:5px}
.key.k-tkk{background:var(--teal)}
.key.k-up{background:var(--purple)}

@media print{
  .spark i,.bar.split,.pie,.key{-webkit-print-color-adjust:exact;print-color-adjust:exact}
}
@media (max-width:700px){
  .pie{width:112px;height:112px}
  .spark{height:16px}
}

/* ---- HOVER A BAR AND READ THE NUMBER (2026-09-24) ----------------------
   Asked for: "allow hover and show number on graph". Only every third bar
   prints its value, so the ones between were unreadable without guessing at
   the height. CSS only -- no script draws or positions this.                */
.bar-col{position:relative}
.bar-tip{
  position:absolute;bottom:100%;left:50%;transform:translateX(-50%) translateY(4px);
  margin-bottom:4px;padding:7px 10px;border-radius:8px;
  background:var(--ink,#241b3a);color:#fff;font-size:12px;line-height:1.45;
  white-space:nowrap;text-align:center;pointer-events:none;
  opacity:0;visibility:hidden;transition:opacity .12s ease,transform .12s ease;
  box-shadow:0 6px 18px rgba(0,0,0,.22);z-index:6}
.bar-tip b{display:block;font-size:15px}
.bar-tip i{display:block;font-style:normal;opacity:.85}
.bar-tip u{display:block;text-decoration:none;opacity:.7;font-size:11px;margin-top:2px}
/* the little arrow */
.bar-tip::after{content:"";position:absolute;top:100%;left:50%;margin-left:-5px;
  border:5px solid transparent;border-top-color:var(--ink,#241b3a)}
.bar-col:hover .bar-tip,
.bar-col:focus-within .bar-tip{opacity:1;visibility:visible;transform:translateX(-50%) translateY(0)}
/* A tip on the first or last bar would hang off the chart; nudge it inward. */
.bar-col:first-child .bar-tip{left:0;transform:translateX(0) translateY(4px)}
.bar-col:first-child:hover .bar-tip{transform:translateX(0) translateY(0)}
.bar-col:last-child .bar-tip{left:auto;right:0;transform:translateX(0) translateY(4px)}
.bar-col:last-child:hover .bar-tip{transform:translateX(0) translateY(0)}
@media print{ .bar-tip{display:none} }
