/* ============================================================================
   Brand webfonts — self-hosted, vendored in src/assets/fonts/.

   They are NOT loaded from fonts.googleapis.com. That stylesheet is
   render-blocking and third-party: when a school content filter blackholes the
   host (they drop rather than refuse), nothing on the page paints until the
   request times out — measured at 6.1s to first headline against a 6s stall,
   including the Sign In button. It is also a per-visitor IP disclosure to
   Google, which is the sort of thing that shows up in a district privacy review.

   Served from our own distribution instead: same-origin, no third-party
   request, and no render-block beyond this file. The CSP allows font-src 'self'.

   font-display:swap means text paints immediately in the fallback and swaps when
   the font arrives, so a slow font never blocks the headline. The fallback stacks
   in :root are ordered so the layout holds either way.

   Licence: Mulish, Libre Franklin and Hind are all SIL Open Font License 1.1,
   which permits self-hosting and redistribution. See src/assets/fonts/LICENSE.md.

   To update a font, replace the .woff2 AND give it a new filename — these are
   served with a one-year immutable cache, so a same-named replacement would not
   be picked up by browsers that already have it.
   ============================================================================ */

@font-face {
  font-family: "Mulish";
  font-style: normal;
  font-weight: 800 900;
  font-display: swap;
  src: url(/fonts/mulish-latin.woff2) format("woff2");
}
@font-face {
  font-family: "Libre Franklin";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url(/fonts/libre-franklin-latin.woff2) format("woff2");
}
@font-face {
  font-family: "Hind";
  font-style: normal;
  font-weight: 500 600;
  font-display: swap;
  src: url(/fonts/hind-latin.woff2) format("woff2");
}

:root{
  --navy:#1E407B; --navy-deep:#122A4C; --sky:#1CA0E3;
  --cream:#FDF9EC; --ink:rgba(10,10,10,.85);

  /* CTA blue. Brand swatch #0088C2 is 3.96:1 on white text at this size, which
     fails WCAG AA (needs 4.5:1). #0076A8 is the brand hover shade and measures
     5.05:1, so it becomes the resting colour and hover goes one step darker. */
  --blue:#0076A8; --blue-hover:#005E86;

  --font-body:"Libre Franklin",system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --font-display:"Mulish","Libre Franklin",system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --font-ui:"Hind","Libre Franklin",system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;

  /* Product-shot scale. The width ladder sets --zw, the height caps set --zh, and
     the effective zoom is the SMALLER of the two. Media-query order can no longer
     let a later width rule override a height cap upward. */
  --zw:.62; --zh:1;
  --shot-zoom:min(var(--zw),var(--zh));

  --pad:clamp(16px,2.4vw,40px);

  /* Sample KPI figures — single source of truth, rendered via content:.
     Change here and both the dashboard and the mobile strip update together. */
  --kpi-dev:"842 / 957"; --kpi-cri:"2"; --kpi-war:"4"; --kpi-hea:"18";
}
*{box-sizing:border-box}

/* ---------- single-screen shell ----------
   One screen where it fits, scroll where it doesn't. `min-height` (not `height`)
   plus default overflow means browser zoom, screen magnification and tiny windows
   degrade to scrolling instead of clipping content away. WCAG 1.4.4 / 1.4.10. */
html,body{margin:0;padding:0}
body{
  font-family:var(--font-body);
  color:var(--ink);background:var(--navy-deep);-webkit-font-smoothing:antialiased;
  min-height:100vh;min-height:100dvh;
  display:grid;grid-template-rows:auto minmax(0,1fr) auto;
}
h1,h2,h3{font-family:var(--font-display);margin:0}
a{color:inherit}
:focus-visible{outline:3px solid #7FC6FF;outline-offset:3px;border-radius:4px}

/* ---------- header ---------- */
header{background:var(--navy);z-index:20;box-shadow:0 1px 0 rgba(255,255,255,.09)}
.nav{display:flex;align-items:center;justify-content:space-between;gap:16px;
  height:clamp(52px,7.2vh,74px);padding:0 var(--pad)}
.brand{display:flex;align-items:center;gap:14px;text-decoration:none;min-width:0}
.brand img{height:clamp(26px,3.9vh,42px);width:auto;display:block}
.brand .div{width:1px;height:26px;background:rgba(255,255,255,.28)}
.brand .plat{font-family:"Mulish",sans-serif;font-weight:800;color:#fff;font-size:14px;letter-spacing:.14em;line-height:1}
.brand .plat small{display:block;font-weight:600;font-size:9px;letter-spacing:.18em;color:#9FC7EA;margin-top:3px}
.navright{display:flex;align-items:center;gap:20px}
.navlink{font-family:var(--font-ui);font-size:.85rem;font-weight:500;letter-spacing:.06em;
  text-transform:uppercase;color:#dce8f5;text-decoration:none;white-space:nowrap}
.navlink:hover{color:#fff}
.btn{font-family:var(--font-ui);font-weight:600;letter-spacing:.09em;text-transform:uppercase;
  border-radius:7px;text-decoration:none;display:inline-flex;align-items:center;gap:9px;
  padding:12px 22px;font-size:.85rem;border:0;transition:.15s;white-space:nowrap}
.btn-primary{background:var(--blue);color:#fff}
.btn-primary:hover,.btn-primary:focus-visible{background:var(--blue-hover)}
.btn-ghost{background:transparent;color:#fff;box-shadow:inset 0 0 0 1.5px rgba(255,255,255,.55)}
.btn-ghost:hover{background:rgba(255,255,255,.1)}
.btn-sm{padding:10px 17px;font-size:.8rem}

/* ---------- main: copy + product shot, vertically centred ---------- */
main{
  position:relative;overflow:hidden;color:#fff;
  background:
   radial-gradient(1100px 560px at 80% 4%, rgba(0,143,255,.30), transparent 62%),
   radial-gradient(760px 460px at 4% 96%, rgba(28,160,227,.20), transparent 66%),
   linear-gradient(160deg,#1E407B 0%,#183561 46%,#122A4C 100%);
  display:grid;grid-template-columns:minmax(0,1.02fr) minmax(0,1.18fr);
  align-items:center;gap:clamp(20px,3vw,52px);padding:clamp(14px,3vh,40px) var(--pad);
}
main:before{content:"";position:absolute;inset:0;opacity:.35;pointer-events:none;
  background-image:radial-gradient(rgba(255,255,255,.13) 1px,transparent 1px);
  background-size:22px 22px;
  mask-image:linear-gradient(to bottom,#000,transparent 78%);
  -webkit-mask-image:linear-gradient(to bottom,#000,transparent 78%)}
.copy{position:relative;z-index:2;min-width:0}
.eyebrow{font-family:var(--font-ui);font-weight:600;letter-spacing:.2em;text-transform:uppercase;
  color:#8FC4F0;margin:0 0 clamp(8px,1.6vh,18px);font-size:clamp(.65rem,1.35vh,.8rem)}
h1{font-weight:900;letter-spacing:-.02em;line-height:1.05;
  font-size:clamp(1.55rem,min(3.7vw,5.6vh),3.25rem)}
h1 em{font-style:normal;color:#48B4FF;display:block}
.lede{margin:clamp(10px,2vh,22px) 0 0;color:#DCE8F5;font-weight:400;max-width:34em;
  font-size:clamp(.875rem,min(1.15vw,2vh),1.1rem);line-height:1.6}
.cta{display:flex;flex-wrap:wrap;gap:12px;margin-top:clamp(14px,2.6vh,30px);align-items:center}
.note{font-size:.75rem;color:#A6C3E0;flex-basis:100%;margin-top:2px}

/* ---------- product shot: fixed-size app UI, scaled + cropped ---------- */
.shotbox{position:relative;z-index:2;min-width:0;height:100%;display:flex;align-items:center;
  justify-content:flex-start;overflow:hidden}
.shotclip{width:100%;max-height:100%;overflow:hidden;border-radius:11px;
  box-shadow:0 34px 74px -22px rgba(4,16,38,.7),0 0 0 1px rgba(255,255,255,.13);
  -webkit-mask-image:linear-gradient(to bottom,#000 84%,transparent 100%);
  mask-image:linear-gradient(to bottom,#000 84%,transparent 100%)}
.frame{width:1120px;zoom:var(--shot-zoom);background:#fff;overflow:hidden}

.chrome{background:#EDEFF2;border-bottom:1px solid #DCE0E6;display:flex;align-items:center;gap:10px;padding:9px 12px}
.dots{display:flex;gap:6px}
.dots i{width:9px;height:9px;border-radius:50%;display:block}
.urlbar{flex:1;background:#fff;border:1px solid #DCE0E6;border-radius:20px;height:22px;
  display:flex;align-items:center;justify-content:center;gap:6px;
  font:400 10.5px/1 "Libre Franklin",sans-serif;color:#5a6472;max-width:330px;margin:0 auto}

.app{background:var(--cream);font-family:"Libre Franklin",sans-serif;color:var(--ink)}
.appbar{background:#fff;border-bottom:1px solid #E6E9EE;display:flex;align-items:center;gap:12px;padding:0 14px;height:46px}
.waffle{display:grid;grid-template-columns:repeat(3,3px);gap:2px}
.waffle i{width:3px;height:3px;background:#1E407B;border-radius:.5px;display:block}
.lhlock{line-height:1;margin-left:2px}
.lhlock .l1{font:600 7.5px/1 "Libre Franklin",sans-serif;color:#1E407B;display:block}
.lhlock .l2{font:800 17px/1 "Mulish",sans-serif;color:#0079C2;letter-spacing:-.01em;display:block;margin-top:2px}
.vdiv{width:1px;height:22px;background:#E2E6EC}
.crest{width:19px;height:19px;border-radius:50%;flex-shrink:0;
  background:radial-gradient(circle at 40% 35%,#4a6a97,#16305C);box-shadow:inset 0 0 0 1.5px rgba(255,255,255,.5)}
.district{font:400 11.5px/1 "Libre Franklin",sans-serif;color:#2b3648;display:flex;align-items:center;gap:6px;white-space:nowrap}
.appbar .right{margin-left:auto;display:flex;align-items:center;gap:10px}
.clock{text-align:right;font:400 8.5px/1.35 "Libre Franklin",sans-serif;color:#3b4658}
.avatar{width:20px;height:20px;border-radius:50%;background:linear-gradient(140deg,#7f9cc0,#3d5f8d)}
.user{font:400 11px/1 "Libre Franklin",sans-serif;color:#2b3648;display:flex;align-items:center;gap:5px}
.chev{width:8px;height:8px;border-right:1.4px solid #6b7787;border-bottom:1.4px solid #6b7787;
  transform:rotate(45deg) translateY(-2px);display:block}
.appbody{display:flex}
.rail{width:38px;background:#fff;border-right:1px solid #E6E9EE;padding:7px 0;display:flex;
  flex-direction:column;align-items:center;gap:12px;flex-shrink:0}
.railitem{width:24px;height:24px;border-radius:5px;display:grid;place-items:center}
.railitem.on{background:#1E407B;color:#8FD3F7}
.railitem.off{color:#8b96a6}
.mainpane{flex:1;min-width:0;display:flex;flex-direction:column}
.subbar{background:#fff;border-bottom:1px solid #E6E9EE;display:flex;align-items:center;gap:16px;padding:0 16px;height:36px}
.subbar .ttl{font:600 13px/1 "Libre Franklin",sans-serif;color:#0079C2}
.tab{font:600 10.5px/1 "Libre Franklin",sans-serif;padding:6px 12px;border-radius:4px;color:#7c8798}
.tab.on{color:#0079C2;box-shadow:inset 0 0 0 1px #0079C2}
.canvas{padding:12px;background:var(--cream)}
.card{background:#fff;border:1px solid #EFEADA;border-radius:9px;padding:14px}
.card+.card{margin-top:11px}
.cardhead{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
.h1d{font:400 19px/1.15 "Libre Franklin",sans-serif;color:#3a4250}
.refresh{background:#1A7A3C;color:#fff;border-radius:5px;font:600 10.5px/1 "Libre Franklin",sans-serif;
  padding:7px 11px;display:inline-flex;align-items:center;gap:6px}
.lastref{font:400 9px/1 "Libre Franklin",sans-serif;color:#7b8494;text-align:right;margin-top:6px}
.kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-top:12px}
.kpi{border-radius:6px;padding:10px;display:flex;align-items:center;gap:9px;border-left:4px solid}
.kpi .ico{width:20px;height:20px;flex-shrink:0}
.kpi .mid{flex:1;text-align:center}
.pill{display:inline-block;color:#fff;font:700 10.5px/1 "Libre Franklin",sans-serif;padding:4px 10px;border-radius:20px}
.pill.circ{min-width:19px;height:19px;padding:0;border-radius:50%;display:inline-grid;place-items:center}
.kpi .lbl{font:700 10px/1 "Libre Franklin",sans-serif;margin-top:6px;display:block}
.v-dev::before{content:var(--kpi-dev)}
.v-cri::before{content:var(--kpi-cri)}
.v-war::before{content:var(--kpi-war)}
.v-hea::before{content:var(--kpi-hea)}
.k-dev{border-color:var(--sky);background:#E8F4FC}   .k-dev .lbl{color:#17416f}
.k-cri{border-color:#D64545;background:#FBEAE7}      .k-cri .lbl{color:#4a2020}
.k-war{border-color:#F2711C;background:#FDEFE3}      .k-war .lbl{color:#4a3018}
.k-hea{border-color:#4C9A2A;background:#E9F4E8}      .k-hea .lbl{color:#22421f}
.tblhead{display:flex;justify-content:flex-end;margin-bottom:8px}
.search{border:1px solid #DEE3EA;border-radius:5px;height:24px;width:180px;display:flex;align-items:center;
  justify-content:space-between;padding:0 8px;font:400 9.5px/1 "Libre Franklin",sans-serif;color:#98a1ae}
table.sh{width:100%;border-collapse:collapse}
table.sh th{font:700 9px/1.2 "Libre Franklin",sans-serif;color:#3d4757;text-align:left;padding:0 6px 8px;
  border-bottom:1px solid #E9ECF1;white-space:nowrap}
table.sh th .ar{color:#9aa3b1;font-weight:400;margin-left:2px}
table.sh td{padding:8px 6px;border-bottom:1px solid #F1F3F6;vertical-align:middle;
  font:400 9.5px/1.3 "Libre Franklin",sans-serif;color:#2f3846;white-space:nowrap}
.site{font:700 9.5px/1.25 "Libre Franklin",sans-serif;color:#1c2634}
.sn{font:400 8.5px/1.25 "Libre Franklin",sans-serif;color:#7e8794;display:block;margin-top:1px}
.spill{display:inline-flex;align-items:center;gap:4px;color:#fff;border-radius:20px;
  font:700 8.5px/1 "Libre Franklin",sans-serif;padding:4px 9px 4px 5px}
.s-off{background:#D93025} .s-deg{background:#ED7014} .s-on{background:#3FA23F}
.spill .g{width:11px;height:11px;border-radius:50%;background:rgba(255,255,255,.95);display:grid;place-items:center}
.badge{width:16px;height:16px;border-radius:50%;display:inline-grid;place-items:center;color:#fff;
  font:700 8.5px/1 "Libre Franklin",sans-serif}
.b-red{background:#C1121F} .b-org{background:#ED7014}
.pct-bad{color:#C1121F;font-weight:700}
.iconbtn{width:17px;height:17px;border-radius:3.5px;display:inline-grid;place-items:center;margin-left:3px}
.ib-navy{background:#1E4E8C} .ib-blue{background:#1878C8}
.rowsel td{background:#E7F2FB}
.expand{background:#fff;border:1px solid #EDEFF3;border-radius:8px;padding:12px 14px;margin:2px 0 4px}
.expand h4{font:400 12px/1 "Libre Franklin",sans-serif;color:#3a4250;margin:0 0 9px}
.notices{display:grid;grid-template-columns:1.35fr 1fr;gap:26px}
.nrow{display:flex;align-items:center;gap:8px;padding:4px 0;font:400 9.5px/1.3 "Libre Franklin",sans-serif}
.nrow .txt{flex:1}
.opill{border-radius:20px;font:600 8px/1 "Libre Franklin",sans-serif;padding:3.5px 7px}
.o-cri{color:#C1121F;box-shadow:inset 0 0 0 1px #E9A0A0}
.o-war{color:#D2650B;box-shadow:inset 0 0 0 1px #F0BE8A}
.ago{color:#5c6675;font-size:9px;min-width:52px}
.meter{display:grid;grid-template-columns:78px 30px 1fr;align-items:center;gap:8px;padding:5px 0;
  font:400 9.5px/1 "Libre Franklin",sans-serif}
.meter b{font-weight:700;text-align:right}
.bar{height:7px;border-radius:20px;background:#E7EAEE;overflow:hidden}
.bar i{display:block;height:100%;border-radius:20px}
.dot{width:9px;height:9px;border-radius:50%;display:inline-block;vertical-align:middle}

/* ---------- compact stat strip used instead of the shot on small screens ---------- */
.mini{display:none;position:relative;z-index:2;background:rgba(255,255,255,.07);
  border:1px solid rgba(255,255,255,.16);border-radius:12px;padding:12px}
.mini .mh{display:flex;align-items:center;gap:8px;margin-bottom:10px}
.mini .mh .l1{display:block;font:700 .55rem/1 var(--font-body);color:#B4D4F0;letter-spacing:.04em}
.mini .mh .l2{display:block;font:800 .95rem/1 var(--font-display);color:#fff;margin-top:3px}
.mini .mg{display:grid;grid-template-columns:repeat(2,1fr);gap:8px}
.mini .mt{background:rgba(255,255,255,.94);border-radius:7px;padding:9px 8px;text-align:center;border-left:3px solid}
.mini .mt .v{font:800 15px/1 "Mulish",sans-serif;display:block}
.mini .mt .k{font:700 8.5px/1 "Libre Franklin",sans-serif;color:#42506a;display:block;margin-top:5px}

/* ---------- footer: one line ---------- */
footer{background:var(--navy);color:#CBDCEE;font-size:.72rem;line-height:1.45;
  padding:clamp(8px,1.3vh,13px) var(--pad);display:flex;flex-wrap:wrap;gap:6px 20px;
  align-items:center;justify-content:space-between}
footer a{color:#dbe8f5;text-decoration:none}
footer a:hover{text-decoration:underline}
.flinks{display:flex;gap:16px;font-family:var(--font-ui);letter-spacing:.05em;text-transform:uppercase;font-size:.72rem}
.fmeta{white-space:nowrap}
.fdis{width:100%;font-size:.63rem;color:#A8C0D8;line-height:1.5}

/* ================= responsive =================
   Two independent ladders. The width ladder sets --zw (the 1120px frame must fit
   the right-hand column); the height ladder sets --zh. --shot-zoom is min() of the
   two, so no rule can ever raise a cap set by the other axis and media-query
   ORDER NO LONGER MATTERS. Verified by test/verify-layout.mjs.
   Nothing here hides copy — the lede and the legal disclaimer stay on the page at
   every size. When space runs out the page scrolls; content is never deleted. */

/* --- width ladder --- */
@media (min-width:2100px){ :root{--zw:.88} }
@media (min-width:1800px) and (max-width:2099px){ :root{--zw:.80} }
@media (min-width:1600px) and (max-width:1799px){ :root{--zw:.70} }
@media (min-width:1500px) and (max-width:1599px){ :root{--zw:.65} }
@media (min-width:1400px) and (max-width:1499px){ :root{--zw:.60} }
@media (min-width:1330px) and (max-width:1399px){ :root{--zw:.56} }
@media (min-width:1250px) and (max-width:1329px){ :root{--zw:.53} }
@media (min-width:1140px) and (max-width:1249px){ :root{--zw:.48} }
@media (min-width:1000px) and (max-width:1139px){ :root{--zw:.42} }
@media (max-width:999px){  :root{--zw:.62} }
@media (max-width:860px){  :root{--zw:.52} }
@media (max-width:719px){  :root{--zw:.40} }

/* --- height ladder --- */
@media (max-height:760px){ :root{--zh:.68} }
@media (max-height:680px){ :root{--zh:.62} }
@media (max-height:600px){ :root{--zh:.54} }
@media (max-height:540px){ :root{--zh:.46} }
@media (max-height:460px){ :root{--zh:.38} }

/* Tablets / narrow: stack copy over shot. */
@media (max-width:999px){
  main{grid-template-columns:minmax(0,1fr);gap:clamp(14px,2.4vh,26px);align-content:center}
  .shotbox{justify-content:center;max-height:46vh}
}

/* Phones: the full dashboard has nowhere to go, so show the compact strip instead.
   Both read their figures from the same --kpi-* custom properties. */
@media (max-width:719px){
  .shotbox{display:none}
  .mini{display:block}
  main{grid-template-columns:minmax(0,1fr)}
  .navlink{display:none}
  .nav .btn-sm svg{display:none}
  .brand .div,.brand .plat{display:none}
  .flinks{gap:12px}
  .fmeta{white-space:normal}
}
/* Narrow phones (375x812 class): reclaim ~10px of vertical space so the page
   still fits one screen. Nothing is hidden — only padding and gaps tighten. */
@media (max-width:400px){
  .mini{padding:10px}
  .mini .mg{gap:6px}
  .mini .mh{margin-bottom:8px}
  .mini .mt{padding:7px 6px}
  .cta{gap:10px}
  footer{gap:4px 14px}
}

@media (max-width:719px) and (max-height:720px){
  .mini .mg{grid-template-columns:repeat(4,1fr)}
  .mini .mt .k{font-size:.5rem}
  .mini .mt .v{font-size:.82rem}
}
/* Very short viewports (landscape phones, 200%+ browser zoom): the decorative
   product visual is the only thing that goes. Copy and CTAs always survive. */
@media (max-height:440px){
  .mini,.shotbox{display:none}
}

/* Reduced motion: the only transitions are button hovers, but be explicit. */
@media (prefers-reduced-motion:reduce){
  *{transition:none!important;animation:none!important}
}

/* ---------- utility classes (hoisted out of inline style attributes so the CSP
   can be style-src 'self' with no 'unsafe-inline') ---------- */
.ta-r{text-align:right}
.w22{width:22px}
.chev-dn{border-color:#5b6472}
.chev-up{transform:rotate(-135deg) translateY(-2px);border-color:#1E4E8C}
.c-grn{color:#3FA23F}.c-grn2{color:#2E7D32}.c-red{color:#C1121F}.c-org{color:#D2650B}.c-sky{color:#0079C2}
.bg-red{background:#C1121F}.bg-org{background:#ED7014}.bg-grn{background:#2E7D32}.bg-sky{background:#1CA0E3}
.bd-sky{border-color:#1CA0E3}.bd-red{border-color:#D64545}.bd-org{border-color:#F2711C}.bd-grn{border-color:#4C9A2A}
.d-r{background:#FF5F57}.d-y{background:#FEBC2E}.d-g{background:#28C840}
.expandcell{background:#E7F2FB;padding:0 8px 8px}
.bar-grn{background:#4C9A2A}.bar-org{background:#F26522}
.w45{width:45%}.w32{width:32%}.w71{width:71%}

/* ---------- 404 page ----------
   These rules were an inline <style> block in 404.html. The deployed CSP is
   style-src 'self' with no 'unsafe-inline', so inline rules are BLOCKED and the
   page rendered unstyled. They live here instead, scoped to body.notfound so the
   flex-centering cannot leak onto index.html, which shares this stylesheet. */
body.notfound{
  display:flex;align-items:center;justify-content:center;text-align:center;
  background:linear-gradient(160deg,#1E407B,#122A4C);color:#fff;padding:8vh 6vw;
}
.notfound .b{max-width:34em}
.notfound .b h1{font-size:clamp(1.6rem,4vw,2.6rem);margin-bottom:.6em}
.notfound .b p{color:#DCE8F5;font-size:1rem;line-height:1.6;margin:0 0 1.8em}
.notfound .b .cta{justify-content:center}
