/* PlanEd AI - Velsena's Teachers Schedule Planner. Primrose Schools is the
   first client instance; the crest and school name are per-instance, the brand
   is not. --blue and --deep are now sampled from the delivered PlanEd AI logo
   (the earlier hexes were eyedropped off a compressed letterhead screenshot);
   every pair below is measured, not chosen:
     --deep  #012A6F  13.44:1 on white   navy. Header, headings, ramp end.
     --blue  #0A5CC8   6.22:1 on white   the brand blue. Safe as text AND fill.
     --ok    #1F9D55   3.49:1 on white   FAILS as a text surface. A mark, a dot,
                                         a bar, a chip -- never white-on-green.
     --ai    #B5179E   5.86:1 on white   AI actions only. Chosen by simulating
                                         protan/deutan/tritan: dE 23.8 from the
                                         brand blue and 20.4 from the status
                                         green. The old violet #6C4DD8 clears AA
                                         but collapses into THIS brighter blue
                                         under tritanopia (dE 5.1) -- the logo
                                         blue moved, so the accent had to. The
                                         logo draws its own "AI" in green, which
                                         is reserved for on-track, so the UI's AI
                                         accent can never match the logo's.
   Status green/amber/red are reserved for on-track/lagging/behind and are never
   brand colours. --ai is never a status colour. */
:root{
  --deep:#103B8C; --blue:#2563EB; --brand:#2563EB; --brand-deep:#103B8C;
  --ai:#B5179E;
  --sky:#F7F9FC; --ink:#0F172A; --soft:#64748B; --rule:#E8EDF5;
  /* An 8px spacing scale and one radius/shadow set, so spacing and
     elevation are chosen from a list rather than typed per rule. */
  --s1:8px; --s2:16px; --s3:24px; --s4:32px; --s5:40px;
  --r-card:20px; --r-inner:12px; --r-pill:999px;
  --sh-1:0 1px 2px rgba(16,59,140,.04),0 1px 3px rgba(16,59,140,.05);
  --sh-2:0 4px 16px rgba(16,59,140,.07),0 1px 3px rgba(16,59,140,.04);
  --sh-3:0 12px 32px rgba(16,59,140,.10),0 2px 6px rgba(16,59,140,.05);
  /* The delivery-technique ramp: one hue, regenerated in LCh at the new brand
     blue's hue (288 deg), monotone light->dark, min adjacent dL* 9.9, lightest
     2.06:1 on white, step-3 IS the brand blue, ending on the brand navy. */
  --step-0:#8CB0F2; --step-1:#7C9EE0; --step-2:#4E7BD6; --step-3:#2563EB; --step-4:#103B8C;
  /* A pulli kolam: loops around a dot grid, meeting corner-to-corner so the
     lattice is seamless. Line art only, thin strokes, no fabric colours. */
  --kolam-navy:url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%2748%27%20height%3D%2748%27%20viewBox%3D%270%200%2048%2048%27%3E%3Cg%20fill%3D%27none%27%20stroke%3D%27%23ffffff%27%20stroke-width%3D%271%27%20opacity%3D%27.13%27%3E%3Crect%20x%3D%227.029%22%20y%3D%227.029%22%20width%3D%2233.941%22%20height%3D%2233.941%22%20rx%3D%2210%22%20transform%3D%22rotate%2845%2024.0%2024.0%29%22%2F%3E%3Crect%20x%3D%22-16.971%22%20y%3D%22-16.971%22%20width%3D%2233.941%22%20height%3D%2233.941%22%20rx%3D%2210%22%20transform%3D%22rotate%2845%200.0%200.0%29%22%2F%3E%3Crect%20x%3D%2231.029%22%20y%3D%22-16.971%22%20width%3D%2233.941%22%20height%3D%2233.941%22%20rx%3D%2210%22%20transform%3D%22rotate%2845%2048.0%200.0%29%22%2F%3E%3Crect%20x%3D%22-16.971%22%20y%3D%2231.029%22%20width%3D%2233.941%22%20height%3D%2233.941%22%20rx%3D%2210%22%20transform%3D%22rotate%2845%200.0%2048.0%29%22%2F%3E%3Crect%20x%3D%2231.029%22%20y%3D%2231.029%22%20width%3D%2233.941%22%20height%3D%2233.941%22%20rx%3D%2210%22%20transform%3D%22rotate%2845%2048.0%2048.0%29%22%2F%3E%3C%2Fg%3E%3Cg%20fill%3D%27%23ffffff%27%20opacity%3D%27.22%27%3E%3Ccircle%20cx%3D%2224.0%22%20cy%3D%2224.0%22%20r%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%220%22%20cy%3D%220%22%20r%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%220%22%20r%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%220%22%20cy%3D%2248%22%20r%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%2248%22%20r%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%2224.0%22%20cy%3D%220%22%20r%3D%221%22%2F%3E%3Ccircle%20cx%3D%220%22%20cy%3D%2224.0%22%20r%3D%221%22%2F%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%2224.0%22%20r%3D%221%22%2F%3E%3Ccircle%20cx%3D%2224.0%22%20cy%3D%2248%22%20r%3D%221%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
  --kolam-sky:url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%2748%27%20height%3D%2748%27%20viewBox%3D%270%200%2048%2048%27%3E%3Cg%20fill%3D%27none%27%20stroke%3D%27%232563EB%27%20stroke-width%3D%271%27%20opacity%3D%27.06%27%3E%3Crect%20x%3D%227.029%22%20y%3D%227.029%22%20width%3D%2233.941%22%20height%3D%2233.941%22%20rx%3D%2210%22%20transform%3D%22rotate%2845%2024.0%2024.0%29%22%2F%3E%3Crect%20x%3D%22-16.971%22%20y%3D%22-16.971%22%20width%3D%2233.941%22%20height%3D%2233.941%22%20rx%3D%2210%22%20transform%3D%22rotate%2845%200.0%200.0%29%22%2F%3E%3Crect%20x%3D%2231.029%22%20y%3D%22-16.971%22%20width%3D%2233.941%22%20height%3D%2233.941%22%20rx%3D%2210%22%20transform%3D%22rotate%2845%2048.0%200.0%29%22%2F%3E%3Crect%20x%3D%22-16.971%22%20y%3D%2231.029%22%20width%3D%2233.941%22%20height%3D%2233.941%22%20rx%3D%2210%22%20transform%3D%22rotate%2845%200.0%2048.0%29%22%2F%3E%3Crect%20x%3D%2231.029%22%20y%3D%2231.029%22%20width%3D%2233.941%22%20height%3D%2233.941%22%20rx%3D%2210%22%20transform%3D%22rotate%2845%2048.0%2048.0%29%22%2F%3E%3C%2Fg%3E%3Cg%20fill%3D%27%232563EB%27%20opacity%3D%27.11%27%3E%3Ccircle%20cx%3D%2224.0%22%20cy%3D%2224.0%22%20r%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%220%22%20cy%3D%220%22%20r%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%220%22%20r%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%220%22%20cy%3D%2248%22%20r%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%2248%22%20r%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%2224.0%22%20cy%3D%220%22%20r%3D%221%22%2F%3E%3Ccircle%20cx%3D%220%22%20cy%3D%2224.0%22%20r%3D%221%22%2F%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%2224.0%22%20r%3D%221%22%2F%3E%3Ccircle%20cx%3D%2224.0%22%20cy%3D%2248%22%20r%3D%221%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
}
*{box-sizing:border-box;margin:0}
/* The kolam lives on the page plane. Cards are opaque, so it never sits behind
   a chart, a table or a data grid. */
body{font-family:'Inter','Segoe UI',system-ui,-apple-system,sans-serif;
  -webkit-font-smoothing:antialiased;letter-spacing:-.005em;background-color:var(--sky);
  background-image:var(--kolam-sky);color:var(--ink);font-size:14px;
  min-height:100vh;display:flex;flex-direction:column}   /* sticky footer */
/* min-width:0 is load-bearing. body is a column flex container, so a flex
   item's cross size is clamped to its min-content width unless this says
   otherwise -- meaning one wide table stretched the whole page instead of
   scrolling inside itself, and every screen scrolled sideways on a phone.
   Found by measuring in a browser; it is invisible to a template test. */
main.wrap{flex:1 0 auto;min-width:0}
.topbar{background-image:var(--kolam-navy),linear-gradient(118deg,#0D2350,var(--deep));
  border-bottom:3px solid var(--blue);
  color:#fff;display:flex;justify-content:space-between;align-items:center;
  padding:14px 28px;border-bottom-width:2px}
/* The crest is the square PlanEd AI icon on its own white ground; on the navy
   bar it reads as an app tile. Fixed square so a non-square drop-in can't stretch
   the header; the white is the mark's, not painted here. */
.crest{height:42px;width:42px;object-fit:contain;background:#fff;border-radius:9px;
  display:block;flex:none}
@media (forced-colors:active){body,.topbar{background-image:none}}
@media print{body,.topbar{background-image:none}}
.brand{display:flex;align-items:center;gap:11px;color:#fff;text-decoration:none}
.brand:hover .brand-name{text-decoration:underline}
/* Product wordmark, then the client instance. The instance is lighter and
   smaller: whose system this is, not what it is. */
.brand-name{font-weight:800;font-size:18px;letter-spacing:.2px}
.brand-inst{font-weight:600;font-size:14px;opacity:.85}
/* Fallback when no logo file is present: a white tile the size of the crest, so
   the header keeps its footprint. */
.brand-badge{height:42px;width:42px;flex:none;border-radius:9px;background:#fff;
  color:var(--deep);font-weight:800;font-size:22px;
  display:flex;align-items:center;justify-content:center}
.who{margin-right:12px;font-size:13px;color:#DCE8FF}
.linkbtn{background:none;border:1px solid rgba(255,255,255,.4);color:#fff;border-radius:8px;padding:5px 12px;cursor:pointer}
/* width:100% matters as much as max-width. `margin: … auto` makes the cross
   margins auto, which turns this flex item from stretch-to-fill into
   shrink-to-fit -- and it then took its widest child's size rather than the
   viewport's, so the DS grid dragged the whole page sideways on a phone.
   Global box-sizing is border-box, so 100% already includes the padding. */
.wrap{width:100%;max-width:1000px;margin:24px auto;padding:0 20px}
/* A max-width alone does not guarantee a margin: it only caps how wide the
   column may grow. On a window narrower than the cap the column is the window,
   the `auto` side margins resolve to zero, and the shell sits flush against
   both edges -- which reads as "stretched" even though the content is the same
   width it would be on a larger screen. It bites hardest at a viewport just
   under the cap, and Windows display scaling lands people there routinely: a
   1920px panel at 150% is a 1280px CSS viewport, exactly .wrap.wide. Reserving
   the gutter in the width keeps the same framed look at every size. */
@media (min-width:820px){.wrap{width:calc(100% - 64px)}}
h1{font-size:20px;margin-bottom:14px}
/* A warm, time-of-day greeting above a dashboard title; the name stands out. */
.greeting{font-size:15px;color:var(--soft);margin-bottom:2px}
.greeting strong{color:var(--deep);font-weight:700}
/* Card chrome is a whisper, not a drop shadow. Six rounded rectangles each
   floating 18px off the page is most of what read as "clumsy". */
.card{background:#fff;border:1px solid var(--rule);border-radius:12px;padding:16px 18px;
  box-shadow:0 1px 2px rgba(18,46,99,.06)}
.login{max-width:360px;margin:60px auto}
.login label{display:block;margin:12px 0;font-weight:600}
.login input{display:block;width:100%;padding:9px;margin-top:4px;border:1.5px solid var(--rule);border-radius:9px}
/* Login hero: a navy brand panel beside the form. The white lockup carries the
   wordmark + tagline; the panel wears the kolam like the topbar does. */
.loginpage{max-width:920px}
.loginhero{display:grid;grid-template-columns:1.05fr .95fr;margin:44px auto;
  border-radius:16px;overflow:hidden;border:1px solid var(--rule);
  box-shadow:0 10px 30px rgba(18,46,99,.14)}
.hero-panel{background-image:var(--kolam-navy),linear-gradient(150deg,#0D2350,var(--deep));
  color:#fff;padding:46px 42px;display:flex;flex-direction:column;justify-content:center;gap:16px}
.hero-logo{width:100%;max-width:300px;display:block}
.hero-sub{color:#DCE8FF;font-size:13.5px;line-height:1.6;max-width:330px}
.hero-form{background:#fff;padding:46px 44px;display:flex;flex-direction:column;justify-content:center}
.hero-form h1{font-size:22px;margin-bottom:4px}
.hero-form label{display:block;margin:14px 0;font-weight:600}
.hero-form input{display:block;width:100%;padding:10px;margin-top:5px;border:1.5px solid var(--rule);border-radius:9px}
.hero-form .btn{margin-top:16px;width:100%}
@media (max-width:720px){
  .loginhero{grid-template-columns:1fr}
  .hero-panel{padding:32px;align-items:center;text-align:center}
  .hero-logo{max-width:230px}
}
.btn{background:var(--blue);color:#fff;border:0;border-radius:9px;padding:10px 20px;font-weight:600;cursor:pointer;margin-top:8px}
.err{color:#DE4638;font-size:13px}
.grid{border-collapse:collapse;width:100%;background:#fff;border-radius:10px;overflow:hidden;box-shadow:0 1px 2px rgba(18,46,99,.06)}
.grid th{background:var(--blue);color:#fff;text-align:left;font-size:12px;text-transform:uppercase;letter-spacing:.05em}
.grid th,.grid td{padding:9px 12px;border-bottom:1px solid var(--rule)}
.grid tr:hover td{background:#E9F0FC}
/* A real footer, pinned to the bottom (body is a flex column, main grows). */
.foot{flex-shrink:0;margin-top:36px;padding:16px 20px;border-top:1px solid var(--rule);
  background:#fff;color:var(--soft);font-size:12.5px;
  display:flex;justify-content:center;align-items:center;flex-wrap:wrap;gap:9px}
.foot-copy{font-weight:600;color:var(--ink)}
.foot-dot{color:var(--rule)}
.foot-brand{font-weight:800;color:#D62828;letter-spacing:.2px}
.foot-tsp{font-weight:700;color:var(--deep)}
.foot-inst{color:var(--soft)}
@media print{.foot{display:none}}

/* --- M2: LIC calendar ------------------------------------------------ */
[x-cloak]{display:none!important}
h1 small,h2 small{font-weight:400;color:var(--soft);font-size:13px;margin-left:8px}
h2{font-size:15px;font-weight:700;margin:20px 0 10px}
.muted{color:var(--soft)}
.nowrap{white-space:nowrap}
.navlink{color:#DCE8FF;text-decoration:none;margin-right:14px;font-size:13px;border-bottom:1px solid rgba(255,255,255,.35)}
.messages{list-style:none;padding:0;margin:0 0 14px}
.msg{padding:10px 14px;border-radius:10px;margin-bottom:8px;font-size:13px}
.msg.success{background:#E6F4EA;color:#1B5E36;border:1px solid #B7E1C6}
.msg.warning{background:#FFF6E5;color:#7A4F01;border:1px solid #FFE0A3}
.msg.error{background:#FDECEA;color:#8B1D13;border:1px solid #F5C4BF}
.toolbar{display:flex;justify-content:space-between;align-items:flex-end;gap:16px;margin-bottom:14px;flex-wrap:wrap}
.inline{display:inline}
.toolbar label{font-weight:600;font-size:12px;color:var(--soft);margin-right:12px}
.toolbar select{margin-left:6px;padding:7px;border:1.5px solid var(--rule);border-radius:9px;background:#fff}
.pubbox{display:flex;align-items:center;gap:12px}
.pubbox .btn{margin-top:0}
.chip{display:inline-block;padding:3px 9px;border-radius:999px;font-size:11px;font-weight:700;background:var(--rule);color:var(--soft)}
.chip.pub{background:#E6F4EA;color:#1B5E36}
.calendar td{padding:5px 7px;vertical-align:top}
.calendar input,.calendar select,.calendar textarea{width:100%;min-width:78px;padding:6px;border:1.5px solid var(--rule);border-radius:7px;font:inherit;background:#fff}
.calendar select{min-width:112px}
.calendar td.wide{min-width:220px}
.calendar tr.group td{background:var(--deep);color:#fff;font-weight:700;font-size:12px;text-transform:uppercase;letter-spacing:.05em;padding:6px 12px}
.calendar tr.group:hover td{background:var(--deep)}
.xbtn{background:none;border:0;color:#DE4638;font-size:18px;line-height:1;cursor:pointer;padding:0 4px}
.addcard{margin-top:12px}
.addform{margin-top:14px}
.addform .fields{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:12px}
.addform label{display:block;font-weight:600;font-size:12px;color:var(--soft)}
.addform input,.addform select,.addform textarea{width:100%;margin-top:4px;padding:8px;border:1.5px solid var(--rule);border-radius:9px;font:inherit}

/* --- M2 5.2: teacher DS grid ----------------------------------------- */
.chip.ok{background:#E6F4EA;color:#1B5E36}
.chip.warn{background:#FFF6E5;color:#7A4F01}
.chip.bad{background:#FDECEA;color:#8B1D13}
.totals{display:flex;gap:26px;align-items:center;padding:14px 20px;margin-bottom:14px;flex-wrap:wrap}
.tot{display:flex;flex-direction:column;gap:2px}
.tot.grow{flex:1;min-width:200px}
.tot .lbl{font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--soft);font-weight:700}
.tot .val{font-size:22px;font-weight:800}
.tot .val.good{color:#1B5E36}.tot .val.mid{color:#7A4F01}.tot .val.poor{color:#8B1D13}
.bar{height:9px;background:var(--rule);border-radius:999px;overflow:hidden;margin-top:6px}
.bar .fill{height:100%;border-radius:999px;transition:width .25s}
.bar .fill.good{background:#1F9D55}.bar .fill.mid{background:#E0A106}.bar .fill.poor{background:#DE4638}
.dstable td.num{width:74px}
.dstable td.dates{max-width:210px}
.dstable input:disabled,.dstable textarea:disabled{background:#F1F4FA;color:var(--soft);cursor:not-allowed}
.dstable td.num input{text-align:center}
.actions{display:flex;align-items:center;gap:14px;margin-top:16px}
.actions .btn{margin-top:0}
a{color:var(--blue)}

/* --- M2 5.3: LP-04 chapter + periods --------------------------------- */
.crumb{margin-bottom:10px;font-size:13px}
.chead .fields{display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:12px}
.chead label{display:block;font-weight:600;font-size:12px;color:var(--soft)}
.chead input{width:100%;margin-top:4px;padding:8px;border:1.5px solid var(--rule);border-radius:9px;font:inherit}
.chead input:disabled{background:#F1F4FA;color:var(--soft);cursor:not-allowed}
.chead-meta{display:flex;align-items:center;gap:12px;margin-top:14px}
.chead-meta .btn{margin-top:0;margin-left:auto}
.lpperiod{margin-bottom:14px}
.phead{display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid var(--rule);padding-bottom:8px;margin-bottom:12px}
.phead h3{font-size:14px;text-transform:uppercase;letter-spacing:.06em;color:var(--blue)}
.pfields{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:14px}
.pfields label,.pdates label,label.block{display:block;font-weight:600;font-size:12px;color:var(--soft)}
label.block{margin-top:12px}
.pfields input,.pfields textarea,.pdates input,.pdates select,label.block textarea{
  width:100%;margin-top:4px;padding:8px;border:1.5px solid var(--rule);border-radius:9px;font:inherit}
.pfields input:disabled,.pfields textarea:disabled,.pdates input:disabled,.pdates select:disabled,label.block textarea:disabled{
  background:#F1F4FA;color:var(--soft);cursor:not-allowed}
.ticks{display:grid;grid-template-columns:repeat(5,1fr);gap:12px;background:var(--sky);border:1px solid var(--rule);border-radius:11px;padding:12px}
.tickgroup h4{font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--deep);margin-bottom:7px}
label.tick{display:flex;align-items:flex-start;gap:6px;font-size:12.5px;font-weight:400;color:var(--ink);margin-bottom:3px;cursor:pointer}
label.tick input{margin-top:2px}
label.tick input:disabled{cursor:not-allowed}
label.tick input:disabled+span{color:var(--soft)}
.pdates{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-top:14px}
@media (max-width:1000px){.ticks{grid-template-columns:repeat(2,1fr)}.pfields,.pdates{grid-template-columns:1fr}}

/* --- M3 5.4: LIC approval queue --------------------------------------- */
.qcard{margin-bottom:16px}
.qhead{display:flex;justify-content:space-between;align-items:flex-start;gap:16px;flex-wrap:wrap}
.qhead h3{font-size:15px;color:var(--deep)}
.qhead p{font-size:12.5px;margin-top:3px}
.qstats{display:flex;gap:8px;flex-wrap:wrap}
.qpeek{margin-top:12px;box-shadow:none;border:1px solid var(--rule)}
.qpeek th{background:var(--sky);color:var(--deep)}
.qpeek td,.qpeek th{padding:6px 10px;font-size:12.5px}
.qpeek tr:hover td{background:#F7F9FE}
.qactions{display:flex;gap:10px;align-items:center;margin-top:14px}
.qactions .btn{margin-top:0}
.btn.ghost{background:#fff;color:var(--blue);border:1.5px solid var(--rule)}
.btn.danger{background:#DE4638}
.qreturn{margin-top:14px;padding-top:14px;border-top:1px solid var(--rule)}
.qreturn label{display:block;font-weight:600;font-size:12px;color:var(--soft)}
.qreturn textarea{width:100%;margin-top:5px;padding:9px;border:1.5px solid var(--rule);border-radius:9px;font:inherit}

/* --- Principal plan review (read-before-sign) ------------------------- */
.reviewhead h1{margin:0}
/* The signature chain: four provenance lines the Principal reads before signing. */
.chain{list-style:none;margin:14px 0 0;padding:14px 0 0;border-top:1px solid var(--rule);
  display:flex;flex-wrap:wrap;gap:6px 26px;font-size:13px}
.chain li{position:relative;padding-left:16px}
.chain li::before{content:"";position:absolute;left:0;top:6px;width:7px;height:7px;
  border-radius:50%;background:var(--rule)}
.chain li b{color:var(--deep)}
/* The action bar. It is the only thing on this page that is not read-only. */
.signbar{display:flex;align-items:center;gap:14px;margin-top:16px;padding-top:14px;
  border-top:1px solid var(--rule)}
.signbar.bottom{justify-content:flex-end;border:0;margin:20px 0}
.signbar .btn{margin-top:0}
.signed-note{margin-top:14px;padding-top:12px;border-top:1px solid var(--rule);font-size:13px}
.lpblock{margin-bottom:16px}
/* Methods a period ticked, as quiet inline pills -- reference, never a verdict. */
.methods{max-width:280px}
.tickset{display:inline-block;background:var(--sky);border:1px solid var(--rule);
  border-radius:20px;padding:1px 9px;margin:2px 3px 2px 0;font-size:11.5px}
.grid tfoot th,.grid tfoot td{background:var(--sky);color:var(--deep);font-weight:700;
  border-top:2px solid var(--rule)}
.dates{font-size:12px;color:var(--soft);max-width:180px}

/* --- M7: Basic Appropriation ------------------------------------------ */
.apprform{margin:6px 0}
/* The three inputs, across the top like the sheet's header block. */
.apprinputs{display:flex;gap:22px;flex-wrap:wrap;margin-bottom:14px}
.apprinputs label{font-weight:600;font-size:13px;color:var(--soft);display:flex;
  flex-direction:column;gap:5px}
.apprinputs input{width:110px;padding:8px;border:1.5px solid var(--rule);border-radius:9px;font:inherit}
/* The computed grid: metric rows, term columns. Labels left, numbers right. */
.apprgrid th{background:var(--sky);color:var(--deep);text-transform:none;letter-spacing:0;font-size:13px}
.apprgrid td.num,.apprgrid th{white-space:nowrap}
.apprgrid tr.hi td,.apprgrid tr.hi th{background:#EEF3FC;font-weight:600}
.apprgrid small{font-weight:400}
.apprnote{margin:10px 0 0}
.apprnote.soft{background:#FFF7E6;border-color:#F0D48A}
.apprfinal{margin-top:16px}
.apprfinal legend{font-weight:700;color:var(--deep);padding:0 6px}
.apprfinal input,.apprfinal textarea{width:100%;padding:7px;border:1.5px solid var(--rule);border-radius:8px;font:inherit}
.apprfinal .grid td{vertical-align:top}
/* A locked (approved) appropriation: the whole fieldset is disabled server-side;
   dim it so the read-only state reads at a glance. */
fieldset[disabled]{opacity:.75}
/* LIC "effective weeks" setup grid. */
.twgrid input{width:82px;padding:7px 8px;border:1.5px solid var(--rule);border-radius:8px;font:inherit;text-align:right}
.twgrid .tw-total{font-weight:700;color:var(--deep)}
.twgrid th.nowrap{color:var(--deep)}

/* --- M3 5.5: weekly actuals ------------------------------------------- */
.dstable tr.rowerr td{background:#FDECEA}
.dstable tr.rowerr:hover td{background:#FBDFDB}
.dstable tr.rowerr textarea[name=remarks]{border-color:#DE4638}
.dstable td .err{margin-top:4px;font-size:11.5px;line-height:1.35}

/* --- M3 5.6: term close + signature ----------------------------------- */
.reflect .fields{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}
.reflect label{display:block;font-weight:600;font-size:12px;color:var(--soft)}
.reflect textarea{width:100%;margin-top:4px;padding:9px;border:1.5px solid var(--rule);border-radius:9px;font:inherit}
.reflect textarea:disabled{background:#F1F4FA;color:var(--soft);cursor:not-allowed}
.licnote{margin-top:14px;padding:12px 14px;background:var(--sky);border:1px solid var(--rule);border-radius:11px}
.licnote strong{font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--deep)}
.licnote p{margin-top:5px;font-size:13px}
.signed{margin-top:12px;color:#1B5E36;font-weight:600;font-size:13px}
.btn.small{padding:6px 13px;font-size:12px;margin-top:0}
@media (max-width:900px){.reflect .fields{grid-template-columns:1fr}}

/* --- M4 5.7: principal dashboard -------------------------------------- */
/* Colours below were computed, not chosen. Against the white card surface:
   blue 7.77:1, green 3.41:1, red 4.17:1 all clear the 3:1 mark floor; amber
   is 2.27:1 and carries a visible number as its relief. Worst red<->green CVD
   separation is dE 16.8 under protanopia, clear of the >=12 target. */
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:14px;margin-bottom:8px}
.kpi{display:flex;flex-direction:column;gap:2px;padding:12px 14px;position:relative;overflow:hidden}
.kpi::before{content:"";position:absolute;left:0;right:0;top:0;height:2px;background:var(--accent,var(--blue))}
.kpi.ok{--accent:#1F9D55} .kpi.warn{--accent:#E0A106} .kpi.bad{--accent:#DE4638}
.kpi .lbl{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--soft);font-weight:600}
.kpi .big{font-size:36px;font-weight:700;line-height:1.1;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.kpi .big.ok{color:#1B5E36}.kpi .big.warn{color:#B07405}.kpi .big.bad{color:#B4231A}
.kpi .big.muted{color:var(--soft)}
.kpi .sub{font-size:11.5px;color:var(--soft);line-height:1.35}

/* Hero figure: the one number the dashboard leads with. Same sans as
   everything else, proportional figures (tabular-nums looks loose at size). */
.hero{display:flex;gap:26px;align-items:center;flex-wrap:wrap;padding:20px 22px}
.hero .fig{font-size:44px;font-weight:700;line-height:1;letter-spacing:-.025em;color:var(--deep);font-variant-numeric:tabular-nums}
.hero .fig.ok{color:#1B5E36}.hero .fig.warn{color:#B07405}.hero .fig.bad{color:#B4231A}
.hero .fig.muted{color:var(--soft)}
.hero .side{flex:1;min-width:240px}
.hero .cap{font-size:13px;color:var(--soft);margin-bottom:9px}

/* Meter: the fill carries severity, the track is a lighter step of the fill's
   own hue, so state reads across the whole bar. */
.meter{height:12px;border-radius:999px;overflow:hidden;background:var(--track,#E3EAF7)}
.meter>span{display:block;height:100%;border-radius:999px;background:var(--fill,var(--blue))}
.meter.ok{--fill:#1F9D55;--track:#DCEFE4} .meter.warn{--fill:#E0A106;--track:#F7EAD0}
.meter.bad{--fill:#DE4638;--track:#F7DDDA}
.meter-foot{display:flex;justify-content:space-between;font-size:11.5px;color:var(--soft);margin-top:6px}

/* Pareto: one series, nominal categories -> a single hue for every bar.
   Colouring each bar by its value would re-encode length as hue. */
.pareto{list-style:none;padding:0;margin:6px 0 0}
/* The value gets its own column. Sharing one with the bar meant the longest
   bar -- always present, since the bars scale to it -- pushed its own label out
   of the track and into the card's padding, where a narrow window clipped it. */
/* Capped: on a full-width panel the 1fr track stretched a 10-week bar across
   1,300px, which reads as a magnitude rather than as ten. */
.pareto{max-width:880px}
.pareto li{display:grid;grid-template-columns:minmax(150px,220px) 1fr auto;
  gap:14px;align-items:center;margin-bottom:11px}
.pareto .name{font-size:13px;color:var(--ink)}
/* Both are spans: without display:block an inline box ignores width and height,
   and the bars silently vanish. */
.pareto .track{display:block;min-width:0}
.pareto .bar{display:block;height:20px;border-radius:0 4px 4px 0;background:var(--blue);min-width:3px}
.pareto .val{display:flex;align-items:baseline;gap:6px;justify-content:flex-end;
  min-width:112px}
.pareto .n{font-size:12.5px;font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums}
.pareto .u{font-size:11.5px;color:var(--soft)}

/* Delivery mix. The five LP-04 techniques are ORDERED -- Lecture is all
   teacher, Activity is all child, the middle three are the blend -- so they
   take a one-hue ramp that darkens along that order, not five unrelated hues
   asserting the order is arbitrary. Steps validated with the dataviz ordinal
   checks against a white card: monotone lightness, adjacent dL >= 0.06,
   lightest step 2.07:1 over the surface. No single ink clears 4.5:1 on all
   five steps, so no number is ever printed inside a segment; the legend below
   carries them. 2px surface gaps do the separating, never a border. */

.mix{display:grid;grid-template-columns:minmax(64px,auto) 1fr auto;gap:8px 14px;align-items:center}
.mix .who{font-size:13px;font-weight:600;color:var(--ink)}
.mix .who.all{color:var(--deep)}
.mix .stack{display:flex;gap:2px;height:20px;border-radius:4px;overflow:hidden;
  background:var(--rule)}
.mix .seg{min-width:2px}
.mix .seg[data-step="0"]{background:var(--step-0)}
.mix .seg[data-step="1"]{background:var(--step-1)}
.mix .seg[data-step="2"]{background:var(--step-2)}
.mix .seg[data-step="3"]{background:var(--step-3)}
.mix .seg[data-step="4"]{background:var(--step-4)}
.mix .tot{font-size:11.5px;color:var(--soft);font-variant-numeric:tabular-nums}
.mix .rule{grid-column:1/-1;height:1px;background:var(--rule);margin:2px 0}
/* Identity is never colour alone: every step is named, and read out in order. */
.mixkey{display:flex;flex-wrap:wrap;gap:6px 18px;margin-top:14px}
.mixkey span{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--soft)}
.mixkey i{width:12px;height:12px;border-radius:3px;flex:none}
.mixkey i[data-step="0"]{background:var(--step-0)}
.mixkey i[data-step="1"]{background:var(--step-1)}
.mixkey i[data-step="2"]{background:var(--step-2)}
.mixkey i[data-step="3"]{background:var(--step-3)}
.mixkey i[data-step="4"]{background:var(--step-4)}
.mixkey b{color:var(--ink);font-variant-numeric:tabular-nums}
@media (forced-colors:active){
  .mix .stack{background:Canvas}
  .mix .seg{forced-color-adjust:none;border:1px solid CanvasText}
}

.scroller{overflow-x:auto}
/* 2px surface gaps do the separating; never a border drawn round a mark. */
/* Size to content: with one subject a stretched cell becomes a 600px slab. */
.heat{border-collapse:separate;border-spacing:2px;background:transparent;box-shadow:none;width:auto}
.heat th{border:0}
.heat td{border:0;border-radius:8px}
.heat td.cell{text-align:center;font-weight:700;font-size:12.5px;width:104px;
  font-variant-numeric:tabular-nums}
.heat td.cell.ok{background:#E6F4EA;color:#1B5E36}
.heat td.cell.warn{background:#FFF6E5;color:#7A4F01}
.heat td.cell.bad{background:#FDECEA;color:#8B1D13}
.heat td.cell.none{background:#F7F9FC;color:#B6BFCE}
/* Taught, but nobody has typed an actual yet. Not the same as 0%. */
.heat td.cell.blank{background:#EEF2F8;color:#8894A8;letter-spacing:2px}
.heat td.rowhead{background:#fff;font-weight:700;min-width:120px}
.heat tr:hover td.cell{outline:2px solid rgba(18,46,99,.16);outline-offset:-2px}

/* Status is never colour alone: swatch + label, every time. */
.keys{display:flex;gap:16px;flex-wrap:wrap;margin-top:12px;font-size:12px;color:var(--soft)}
.keys span{display:inline-flex;align-items:center;gap:7px}
.sw{width:14px;height:14px;border-radius:4px;display:inline-block}
.sw.ok{background:#E6F4EA;box-shadow:inset 0 0 0 2px #1F9D55}
.sw.warn{background:#FFF6E5;box-shadow:inset 0 0 0 2px #E0A106}
.sw.bad{background:#FDECEA;box-shadow:inset 0 0 0 2px #DE4638}
.sw.blank{background:#EEF2F8;box-shadow:inset 0 0 0 2px #C6CFDC}
.sw.none{background:#F7F9FC;box-shadow:inset 0 0 0 2px var(--rule)}

.drills{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:14px}
.drill h3{font-size:13px;text-transform:uppercase;letter-spacing:.05em;color:var(--deep);margin-bottom:10px}
.drill .qpeek{margin-top:0}
/* Coverage-band donut: how many sections sit in each band. Parts of a whole, so
   a donut is honest; the percentages themselves never get a pie. */
.donut{display:flex;align-items:center;gap:16px;margin-bottom:12px;padding:2px 0}
.donut-svg{width:104px;height:104px;flex:none}
.donut-ring{stroke:var(--rule)}
.donut-num{font-size:9px;font-weight:800;fill:var(--deep)}
.donut-sub{font-size:3.1px;font-weight:700;fill:var(--soft);text-transform:uppercase;letter-spacing:.08em}
.donut-legend{display:flex;flex-direction:column;gap:5px;font-size:12.5px;color:var(--ink)}
.donut-legend .dl{display:inline-flex;align-items:center;gap:7px}
.donut-legend .dl b{margin-left:auto;font-weight:700;color:var(--deep);padding-left:10px}
.donut-legend .sw{width:12px;height:12px}

/* ------------------------------------------------- dashboard shell (M5)
   A dashboard is read, not scrolled. Eight full-width sections stacked in an
   1100px column made the Principal's page 4,900px tall, and put the heatmap and
   the signature queue -- the only two things anyone acts on -- four screens
   apart. A twelve-column grid sets them side by side; the long reference tables
   go behind a <details> whose summary states how many rows it holds, so nothing
   is hidden, only folded. */
/* 1280 was set for a Mac window with room to spare either side. On a Windows
   panel at 150% scaling the CSS viewport is ~1271, so that cap was never
   reached: the column ran the full width of the screen and the hero's greeting
   sat a third of a metre from the message it belongs to. 1140 is under the
   common scaled viewports, so the frame is visible on those too. */
.wrap.wide{max-width:1140px}
.dashgrid{display:grid;grid-template-columns:repeat(12,1fr);gap:16px;align-items:start}
.dashgrid>*{min-width:0}                 /* else a wide table stretches its track */
.dashgrid .span-5{grid-column:span 5}
.dashgrid .span-6{grid-column:span 6}
.dashgrid .span-7{grid-column:span 7}
.dashgrid .span-12{grid-column:span 12}
/* Below this the columns are narrower than the tables they hold. Mobile is out
   of scope, but a half-width window must not shred the layout. */
@media (max-width:1180px){.dashgrid>*{grid-column:1/-1}}


.panel{background:#fff;border:1px solid var(--rule);border-radius:12px;
  box-shadow:0 1px 2px rgba(18,46,99,.06);padding:14px 16px}
.panel>header{display:flex;align-items:baseline;justify-content:space-between;
  gap:14px;margin-bottom:12px}
.panel>header h2{font-size:14px;margin:0}
.panel>header .note{font-size:11.5px;color:var(--soft);text-align:right;flex:none}
.panel .grid{box-shadow:none}
.panel>p.muted{font-size:12.5px}

/* Reference tables keep every row; they just stop owning the whole page. */
.tablebox{max-height:320px;overflow:auto;border:1px solid var(--rule);border-radius:10px}
.tablebox .grid{box-shadow:none;border-radius:0}
.tablebox .grid thead th{position:sticky;top:0;z-index:1}
.drill .tablebox{max-height:286px}

details.more{margin-top:12px}
details.more>summary{cursor:pointer;list-style:none;padding:7px 0;
  font-size:12px;font-weight:600;color:var(--blue)}
details.more>summary::-webkit-details-marker{display:none}
details.more>summary::before{content:"\25B8\00a0"}
details.more[open]>summary::before{content:"\25BE\00a0"}
details.more>summary:hover{text-decoration:underline}

/* ---------------------------------------------------------- cockpit (M5)
   Two temperaments, two territories. Left: judgement -- dense, read-only, calm.
   Right: action -- sparse, urgent, the only things on this page wearing a
   button. The rail is sticky, so what needs the Principal's pen is never more
   than zero clicks away. Tabs did the opposite: they hid her own to-do behind a
   tab she might never open, and for a glance-reader hidden state is missing
   state. */
/* --- Teacher desk head: this week / at a glance / needs attention ------- */
.deskhead{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:18px}
@media (max-width:1080px){.deskhead{grid-template-columns:1fr}}
.dpanel{background:#fff;border:1px solid var(--rule);border-radius:12px;padding:14px 16px;
  box-shadow:0 1px 2px rgba(18,46,99,.06)}
.dpanel h3{font-size:12px;text-transform:uppercase;letter-spacing:.05em;color:var(--soft);margin-bottom:10px}
.dpanel .none{font-size:13px;color:var(--soft)}
.dpanel.clear{background:#F4FBF7;border-color:#CDE9D8}
.dpanel .tw{list-style:none;display:flex;flex-direction:column;gap:8px}
.dpanel .tw li{font-size:13px;line-height:1.4}
.dpanel .tw a{color:var(--deep);text-decoration:none}
.dpanel .tw a:hover{text-decoration:underline}
.dpanel .tw span{display:block;color:var(--soft);font-size:12px}
.today-lp{margin-top:10px;font-size:12.5px;border-top:1px solid var(--rule);padding-top:8px}
/* The day's plan, big and first: a full-width hero so a teacher opens the desk
   and focuses on today. Navy side (the day) + the periods, large, on white. */
.todayhero{display:grid;grid-template-columns:180px 1fr;margin-bottom:18px;
  border-radius:16px;overflow:hidden;border:1px solid #D6E2F6;
  box-shadow:0 8px 24px rgba(18,46,99,.12)}
.th-side{background:linear-gradient(150deg,#0D2350,var(--deep));color:#fff;
  padding:22px 24px;display:flex;flex-direction:column;justify-content:center}
.th-kicker{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.14em;color:#AFC5EE}
.th-date{font-size:27px;font-weight:800;line-height:1.05;margin-top:5px}
.th-date2{font-size:13px;color:#DCE8FF;margin-top:3px}
.th-body{background:#fff;padding:20px 28px;display:flex;align-items:center;
  justify-content:space-between;gap:24px}
.th-plans{flex:1;min-width:0}
.th-greet{flex:none;text-align:right;font-size:14px;color:var(--soft);line-height:1.5;white-space:nowrap}
.th-greet strong{display:block;color:var(--deep);font-size:18px;font-weight:800}
/* The count, in words, above the sample. */
.th-lead{font-size:13px;color:var(--soft);margin:0 0 10px}
.th-lead strong{color:var(--deep);font-size:17px;font-weight:800;
  font-variant-numeric:tabular-nums;margin-right:2px}
/* Two columns rather than one. Four stacked items made this banner ~200px
   tall and pushed the dashboard below the fold exactly on the days it was
   busiest; two rows of two is half that, and the height no longer moves with
   the workload -- the cap and the grid together make it constant. */
.th-list{list-style:none;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:10px 26px;margin:0}
.th-list li{position:relative;padding-left:18px;min-width:0}
.th-list li::before{content:"";position:absolute;left:2px;top:7px;width:8px;height:8px;
  border-radius:50%;background:var(--blue)}
.th-topic{font-size:15px;font-weight:700;color:var(--deep);text-decoration:none;
  line-height:1.3;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The way through to the rest, rather than a sentence saying they exist. */
.th-more{display:inline-block;margin-top:11px;font-size:13px;font-weight:600;
  color:var(--blue);text-decoration:none}
.th-more:hover{text-decoration:underline}
.th-more.plain{color:var(--soft);font-weight:400}
.th-topic:hover{text-decoration:underline}
.th-ctx{display:block;font-size:12.5px;color:var(--soft);margin-top:1px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.th-empty{font-size:14px;color:var(--soft);line-height:1.6;margin:0}
@media (max-width:900px){.th-list{grid-template-columns:1fr}}
@media (max-width:640px){.todayhero{grid-template-columns:1fr}.th-side{padding:16px 20px}
  .th-body{flex-direction:column;align-items:flex-start}
  .th-greet{text-align:left;white-space:normal;margin-top:6px}
  .th-topic,.th-ctx{white-space:normal}}
.covtop{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:7px}
.covtop .l{font-size:12.5px;font-weight:600;color:var(--soft)}
.covtop b{font-size:20px;color:var(--deep)}
.glancenums{list-style:none;display:flex;gap:18px;margin-top:14px}
.glancenums li{display:flex;flex-direction:column}
.glancenums b{font-size:19px;color:var(--ink)}
.glancenums span{font-size:10.5px;text-transform:uppercase;letter-spacing:.04em;color:var(--soft)}
.attn{list-style:none;display:flex;flex-direction:column;gap:8px}
.attn li{font-size:13px;padding-left:14px;position:relative;line-height:1.35}
.attn li::before{content:"";position:absolute;left:0;top:6px;width:7px;height:7px;border-radius:50%;background:var(--soft)}
.attn li.poor::before{background:#DE4638}.attn li.mid::before{background:#E0A106}
.attn b,.glancenums b.poor{font-weight:700}
.glancenums b.poor{color:#DE4638}
.dpanel .attn b{color:var(--deep)}

.zonehead{font-size:15px;color:var(--deep);margin-bottom:12px}
/* LIC "waiting on your decision": a focal count, a breakdown of chips, a CTA. */
.pending-hero{display:flex;align-items:baseline;gap:10px;margin-bottom:12px}
.pending-num{font-size:40px;font-weight:800;line-height:1;color:var(--deep)}
.pending-cap{font-size:12.5px;font-weight:600;color:var(--soft);line-height:1.2}
.pending-chips{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:14px}
.pchip{font-size:12px;font-weight:600;color:var(--deep);background:var(--sky);
  border:1px solid var(--rule);border-radius:20px;padding:3px 11px}
/* Collapse the queue's detail tables so the page is a scannable list, not a
   scroll. Review-toggle sits with the card's stat chips. */
.peektoggle{background:#fff;border:1px solid var(--blue);color:var(--blue);
  font-size:12px;font-weight:700;border-radius:8px;padding:4px 11px;cursor:pointer}
.peektoggle:hover{background:var(--sky)}
/* Compact queue card: a single tight row, action inline in the header. */
.qcard.compact{padding:11px 16px;margin-bottom:10px}
.qcard.compact .qhead{align-items:center}
.qcard.compact h3{font-size:14px}
.small{font-size:12px}
/* Pager for long queues. */
.pager{display:flex;align-items:center;justify-content:center;gap:16px;margin:22px 0 6px}
.pageof{font-size:13px;color:var(--soft)}
.btn.small.disabled{opacity:.4;pointer-events:none}
/* Premium touch, applied consistently: the deskhead panels align to a common
   height, and interactive cards lift a touch on hover. */
.deskhead .dpanel{min-height:158px}
.plancard,.dpanel{transition:box-shadow .15s ease}
.plancard:hover{box-shadow:0 6px 18px rgba(18,46,99,.10)}
.cockpit{display:grid;grid-template-columns:minmax(0,8fr) minmax(290px,4fr);
  gap:18px;align-items:start}       /* `start` is load-bearing: a stretched grid
                                       item fills its area, and sticky then has
                                       nowhere left to travel. */
.cockpit .zone{display:flex;flex-direction:column;gap:16px;min-width:0}
.rail{position:sticky;top:16px;display:flex;flex-direction:column;gap:12px}
@media (max-width:1080px){
  .cockpit{grid-template-columns:1fr}
  .rail{position:static;flex-direction:row;flex-wrap:wrap}
  .rail>*{flex:1 1 270px}
}

/* One card, five segments, hairline rules. Five separate floating cards, each
   with its own coloured rail, were most of the "elongated". */
.kpiband{padding:14px 18px}
.kpiband .cells{display:grid;grid-template-columns:1.35fr repeat(4,1fr)}
.kpiband .cell{display:flex;flex-direction:column;gap:2px;padding:2px 16px;
  border-left:1px solid var(--rule)}
.kpiband .cell:first-child{border-left:0;padding-left:0}
.kpiband .lbl{font-size:11px;text-transform:uppercase;letter-spacing:.06em;
  color:var(--soft);font-weight:600;display:flex;align-items:center;gap:6px;order:-1}
.kpiband b{font-size:31px;font-weight:700;line-height:1.05;letter-spacing:-.02em;
  color:var(--ink);font-variant-numeric:tabular-nums}
.kpiband .fig{font-size:44px;color:var(--deep)}
.kpiband .fig.ok{color:#1B5E36}.kpiband .fig.warn{color:#B07405}.kpiband .fig.bad{color:#B4231A}
.kpiband b.warn{color:#B07405}.kpiband b.bad{color:#B4231A}.kpiband b.muted{color:var(--soft)}
.kpiband .sub{font-size:11.5px;color:var(--soft);line-height:1.35}
.kpiband .meter{margin-top:14px}
.kpiband .cap{font-size:12px;color:var(--soft);margin-top:9px;line-height:1.45}
/* Status as a 6px dot beside the label, never as a card background: the bands
   are reserved, and a tinted card would out-shout the heatmap below it. */
.dot{width:6px;height:6px;border-radius:50%;display:inline-block;flex:none}
.dot.ok{background:#1F9D55}.dot.warn{background:#E0A106}.dot.bad{background:#DE4638}
@media (max-width:1080px){
  .kpiband .cells{grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px 0}
  .kpiband .cell{border-left:0}
}

/* Queue cards. A gold top rule means "act here", and appears nowhere else. */
.qcard{background:#fff;border:1px solid var(--rule);border-radius:12px;padding:12px 14px;
  box-shadow:0 1px 2px rgba(18,46,99,.06);position:relative;overflow:hidden}
.qcard.act::before{content:"";position:absolute;left:0;right:0;top:0;height:2px;
  background:var(--blue)}
.qcard h3{display:flex;align-items:center;justify-content:space-between;gap:10px;
  font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--soft);
  font-weight:600;margin:0 0 9px}
.qcard h3 .n{font-size:14px;font-weight:800;color:var(--deep);font-variant-numeric:tabular-nums}
.qcard ul{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;
  gap:6px;max-height:196px;overflow:auto}
.qcard li{display:flex;align-items:center;justify-content:space-between;gap:10px;
  font-size:12.5px;padding:7px 9px;background:var(--sky);border:1px solid var(--rule);
  border-radius:9px}
.qcard .what{display:flex;flex-direction:column;gap:1px;min-width:0}
.qcard .what b{font-weight:700;color:var(--ink)}
.qcard .what span{color:var(--soft);font-size:11.5px}
.qcard .btn{margin-top:0;padding:6px 13px;font-size:12px}
.qcard .none{font-size:12.5px;color:var(--soft);padding:10px 11px;background:#F7F9FC;
  border:1px dashed var(--rule);border-radius:9px}
.qcard .foot-note{margin-top:8px;font-size:11px;color:var(--soft);line-height:1.4}

/* The reference tail: read on prompting, never on arrival. */
.secnav{position:sticky;top:0;z-index:5;display:flex;align-items:center;gap:4px;
  margin:22px 0 14px;padding:8px 2px;background:var(--sky);
  border-bottom:1px solid var(--rule)}
.secnav .lbl{font-size:10.5px;text-transform:uppercase;letter-spacing:.07em;
  color:var(--soft);font-weight:700;margin-right:10px}
.secnav a{font-size:12px;font-weight:600;color:var(--soft);text-decoration:none;
  padding:6px 12px;border-radius:8px}
.secnav a:hover,.secnav a:focus-visible{color:var(--deep);background:#fff}
.refsec{margin-bottom:16px;scroll-margin-top:58px}   /* clears the sticky nav */

/* Paper has no sticky, and no jump links. */
@media print{
  .rail{position:static}
  .secnav{display:none}
  .cockpit{display:block}
  .qcard,.panel,.card{break-inside:avoid}
  /* A clean sheet: drop the app chrome and the on-screen-only controls. */
  .topbar,.foot,.noprint,nav{display:none!important}
  .sheet{box-shadow:none;border:0;margin:0;max-width:none;padding:0}
}
@page{size:A4;margin:14mm}

/* --- APP-01 print sheet ------------------------------------------------- */
.printbar{display:flex;justify-content:space-between;align-items:center;margin-bottom:14px}
.printbar .btn{margin-top:0}
.rightlink{float:right}
.sheet{background:#fff;border:1px solid var(--rule);border-radius:10px;
  padding:26px 30px;max-width:900px;margin:0 auto}
.sheet-head{display:flex;justify-content:space-between;align-items:flex-start;
  border-bottom:2px solid var(--deep);padding-bottom:12px;margin-bottom:16px}
.sheet-head h1{font-size:22px;color:var(--deep);margin:0}
.sheet-head p{font-size:13px;color:var(--soft);margin-top:3px}
.sheet-head .fmt{font-size:11px;color:var(--soft);text-align:right;line-height:1.4}
.sheet-head .fmt b{font-size:15px;color:var(--deep)}
.sheet .meta{width:100%;border-collapse:collapse;margin-bottom:18px;font-size:13px}
.sheet .meta th,.sheet .meta td{border:1px solid var(--rule);padding:7px 10px;text-align:left}
.sheet .meta th{background:var(--sky);color:var(--deep);font-weight:600;width:15%;white-space:nowrap}
/* Three signature blocks, each a rule line the ink sits on, then the role. */
.sign-chain{display:grid;grid-template-columns:repeat(3,1fr);gap:34px;margin-top:40px}
.sign{display:flex;flex-direction:column;gap:3px}
.sign .line{border-top:1px solid var(--ink);margin-bottom:6px;height:30px}
.sign b{font-size:13px;color:var(--deep)}
.sign span{font-size:12px;color:var(--soft)}
.sign .when{font-size:11px}

/* Where the term stands, one cell per workflow state. Zeroes are printed, not
   omitted: a missing DRAFT column reads as "none outstanding" only to someone
   who already knew the column should be there. */
.tally{list-style:none;padding:0;margin:0;display:grid;
  grid-template-columns:repeat(auto-fit,minmax(88px,1fr));gap:8px}
.tally li{display:flex;flex-direction:column;gap:2px;padding:10px 12px;
  background:var(--sky);border:1px solid var(--rule);border-radius:10px}
.tally b{font-size:19px;font-weight:800;color:var(--deep);
  line-height:1.1;font-variant-numeric:tabular-nums}
.tally span{font-size:10.5px;text-transform:uppercase;letter-spacing:.04em;
  color:var(--soft);font-weight:700}

/* ------------------------------------------------------ teacher desk (M5)
   Same cockpit anatomy as the Principal, but the ratio flips: she is a worker,
   not a scanner. The work zone is the destination and the rail is the
   conscience. One card per assignment; one button per card, and never a button
   on a plan she cannot act on -- a SUBMITTED plan is sitting with her Level
   Incharge, and offering her a button for it would blame her for the wait. */
.plancard{background:#fff;border:1px solid var(--rule);border-radius:12px;
  box-shadow:0 1px 2px rgba(18,46,99,.06);padding:11px 14px;position:relative;
  overflow:hidden}
/* The blue rule means "you can act here". On the left now rather than the top:
   a 2px cap read as a divider once the cards were a dense stack. */
.plancard.act{border-left:3px solid var(--blue)}

/* One line: who it is, the four numbers, the week strip, the action. The
   whole point of the row is that a plan going well costs ~64px instead of the
   ~200px the stacked card cost -- eleven subjects made that desk 3,359px tall
   and the busiest teachers hold nineteen. */
/* Two lines, not one. The work zone is 8 of the cockpit's 12 columns -- about
   700px at any realistic window -- and identity, four stats, a ten-week strip
   and a button labelled "Enter this week's actuals" do not fit across it: the
   first attempt clipped the button and truncated every subject name to an
   ellipsis. Line one is who and what to do, line two is the numbers. It costs
   ~20px over the clipped version and nothing is cut off. */
.pc-row{display:grid;grid-template-columns:minmax(0,1fr) auto;
  align-items:center;gap:9px 14px}
.pc-id{grid-column:1;grid-row:1}
.pc-act{grid-column:2;grid-row:1}
.pc-stats{grid-column:1;grid-row:2}
.pc-row .weekstrip{grid-column:2;grid-row:2;justify-self:end}
.pc-id{min-width:0}
/* No ellipsis: the subject name is the one thing on the row that has to be
   readable, and it now has a full line to itself. */
.plancard .title{font-size:14.5px;font-weight:700;color:var(--deep);
  text-decoration:none;display:block}
.plancard .title:hover{text-decoration:underline}
.plancard .term{display:block;font-size:11px;color:var(--soft);margin-top:1px}

/* Value over label, tight. Tabular numerals so the columns line up down the
   stack -- a row of numbers that jitters cannot be scanned vertically. */
.pc-stats{display:flex;align-items:flex-end;gap:16px}
.pc-stats>span{display:flex;flex-direction:column;align-items:flex-start;gap:1px}
.pc-stats b{font-size:16px;font-weight:700;color:var(--ink);line-height:1.1;
  font-variant-numeric:tabular-nums}
.pc-stats b.muted{color:var(--soft)}
.pc-stats .l{font-size:9.5px;text-transform:uppercase;letter-spacing:.05em;
  color:var(--soft);font-weight:600}
.pc-act{display:flex;align-items:center;gap:9px;justify-self:end;white-space:nowrap}

/* The lines below the row are exceptions, not furniture: a plan that is simply
   going well shows none of them. */
.plancard .thisweek{font-size:12.5px;color:var(--soft);margin-top:9px}
.plancard .owed{font-size:12px;color:#7A4F01;background:#FFF6E5;border:1px solid #FFE0A3;
  border-radius:8px;padding:5px 9px;margin-top:9px}
.plancard .comment{font-size:12.5px;color:var(--ink);background:var(--sky);
  border-left:3px solid var(--deep);border-radius:0 8px 8px 0;
  padding:7px 10px;margin-top:9px}
.plancard .waiting{margin:8px 0 0;color:var(--soft);font-size:11.5px}

/* Below this the row cannot hold four columns; it becomes two lines rather
   than squeezing the subject name to a letter at a time. */
/* Narrow: everything stacks and the strip drops below the numbers. */
@media (max-width:700px){
  .pc-row{grid-template-columns:1fr}
  .pc-id,.pc-act,.pc-stats,.pc-row .weekstrip{grid-column:1}
  .pc-act{grid-row:auto;justify-self:start;flex-wrap:wrap;white-space:normal}
  .pc-stats{grid-row:auto;justify-content:space-between;gap:10px}
  .pc-row .weekstrip{grid-row:auto;justify-self:start}
}

/* One square per week. Green/amber/red are the variance bands -- the same three
   the DS grid and the heatmap use. `blank` is unknown, `future` has not run. */
.weekstrip{display:inline-flex;gap:2px;align-items:center}
.weekstrip .wk{width:11px;height:18px;border-radius:2px;display:block}
.weekstrip .wk.ok{background:#8FD3AB} .weekstrip .wk.warn{background:#F3CE7A}
.weekstrip .wk.bad{background:#EFA79E}
.weekstrip .wk.blank{background:repeating-linear-gradient(45deg,#E8EDF5 0 3px,#F7F9FC 3px 6px)}
.weekstrip .wk.future{background:#fff;box-shadow:inset 0 0 0 1px var(--rule)}

/* Workflow chips. The states are ORDERED, so they take one hue darkening along
   the progression -- never the reserved status green/amber/red, which would make
   "Approved" green mean the same thing as "on track" green. RETURNED is off the
   path, so its dot is hollow. */
.wchip{display:inline-flex;align-items:center;gap:6px;flex:none;white-space:nowrap;
  background:#EDF2F9;border:1px solid var(--rule);color:var(--deep);
  border-radius:999px;padding:4px 11px;font-size:11px;font-weight:700}
.wchip i{width:7px;height:7px;border-radius:50%;flex:none;background:var(--deep)}
.wchip i[data-step="0"]{background:var(--step-0)} .wchip i[data-step="1"]{background:var(--step-1)}
.wchip i[data-step="2"]{background:var(--step-2)} .wchip i[data-step="3"]{background:var(--step-3)}
.wchip i[data-step="4"]{background:var(--step-4)}
.wchip.back{background:#fff;border-color:var(--deep)}
.wchip.back i{background:transparent;box-shadow:inset 0 0 0 2px var(--deep)}

.qcard .due{padding:8px 10px;background:var(--sky);border:1px solid var(--rule);
  border-radius:9px;margin-bottom:7px;font-size:12.5px}
.qcard .due b{display:block;color:var(--ink)}
.qcard .due span{color:var(--soft);font-size:11.5px}
.qcard .due .late{color:#B4231A;font-weight:700}
.qcard .btn.small{margin-top:0;padding:6px 12px;font-size:12px;text-decoration:none;
  display:inline-block}

/* --- M4 5.9: error pages ---------------------------------------------- */
.errpage{max-width:520px;margin:60px auto;text-align:center}
.errpage h1{font-size:20px;margin-bottom:8px}
.errpage p{margin-top:8px;font-size:13.5px}

/* --- 5.2: DS grid Status column --------------------------------------- */
/* LP-04 labels are fixed and long ("Partial completion & need more time"), so the
   select must be wide enough not to clip the chosen one. */
.dstable td.status{width:196px}
.dstable td.status select{min-width:186px}
/* One category is not a bar chart — the number is the chart. */
.onereason{display:flex;align-items:baseline;gap:12px}
.onereason .n{font-size:34px;font-weight:800;color:var(--deep);line-height:1}
.onereason .name{font-size:13.5px;color:var(--soft)}

/* --- M9: handover strip + staffing ------------------------------------ */
/* Provenance, not status: the strip says who answers for which weeks, so it
   is deliberately quiet -- it must not compete with the coverage chips. */
.handover{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin:-6px 0 14px;
  font-size:12.5px;color:var(--soft)}
.handover-lbl{font-size:11px;text-transform:uppercase;letter-spacing:.05em;
  font-weight:700;color:var(--deep)}
.handover-span{background:var(--sky);border:1px solid var(--rule);border-radius:999px;
  padding:3px 10px;color:var(--ink)}
.handover-dot{color:var(--rule)}
.levelcard .fields{max-width:640px}
.levelcard h2{margin:0}

/* --- M8: tasks + the bell --------------------------------------------- */
.bell{position:relative;display:inline-flex;align-items:center;color:#DCE8FF;
  margin-right:14px;text-decoration:none;padding:4px}
.bell svg{width:18px;height:18px;display:block}
.bell:hover{color:#fff}
/* The count is the signal, so it keeps a status colour; the glyph does not. */
.bell-n{position:absolute;top:-2px;right:-6px;min-width:16px;height:16px;padding:0 4px;
  border-radius:999px;background:#DE4638;color:#fff;font-size:10px;font-weight:800;
  line-height:16px;text-align:center}
.tasklist{list-style:none;padding:0;margin:8px 0 0}
.tasklist li{padding:8px 0;border-bottom:1px solid var(--rule)}
.tasklist li:last-child{border-bottom:0}
.tasklist a{font-weight:600;color:var(--deep)}
.tmeta{display:block;font-size:12px;color:var(--soft);margin-top:2px}
.mytasks h3{font-size:12px;text-transform:uppercase;letter-spacing:.05em;
  color:var(--soft);margin-top:14px}
.notelist{list-style:none;padding:0;margin:0}
.notelist li{padding:11px 14px;border:1px solid var(--rule);border-radius:10px;
  background:#fff;margin-bottom:8px}
.notelist li.unread{border-left:4px solid var(--blue);background:var(--sky)}
.notelist a{color:var(--deep);font-weight:600;text-decoration:none}
.notelist a:hover{text-decoration:underline}
/* The dashboard's "assign this" affordance: quiet until hovered, because the
   number is what the Principal came to read, not the button. */
.assignbtn{font-size:11px;color:var(--soft);text-decoration:none;white-space:nowrap}
.assignbtn:hover{color:var(--blue);text-decoration:underline}
/* A heatmap cell that is also an affordance. At rest it must look exactly like
   the cells that are not links, or the grid stops reading as a grid. */
.cellink{color:inherit;text-decoration:none}
.cellink:hover,.cellink:focus{text-decoration:underline}

/* --- M10: the two screens teachers live in, on a phone ----------------- */

/* Base rules first, media query after. The other order looks identical in a
   diff and is not: same specificity means the later rule wins, so a
   `display:flex` inside the media query loses to a `display:none` written
   below it and the pager renders invisible while the rows stay hidden -- one
   week reachable out of eight. Found in a browser; no Django test can see it. */
.weekpager{display:none;align-items:center;justify-content:space-between;gap:12px;
  margin-bottom:12px;padding:8px 10px;background:#fff;border:1px solid var(--rule);
  border-radius:12px}
.weekpager .btn.small{margin-top:0;min-height:44px}
.weekpager .btn.small:disabled{opacity:.45;cursor:default}
.weekpager-at{font-size:12.5px;font-weight:700;color:var(--deep);text-align:center;
  min-width:0}

@media (max-width:700px){
  /* The pager, shown only where it means something. [hidden] still wins: the
     script sets it when there is nothing to page through. */
  .weekpager{display:flex}
  .weekpager[hidden]{display:none}

  /* The DS grid stops being a grid. Same <tr> rows, same HTMX endpoints --
     only the box model changes, so there is one set of inputs and the header
     totals cannot double. */
  .dstable, .dstable tbody, .dstable tr, .dstable td{display:block;width:auto}
  .dstable thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
  .dstable{box-shadow:none;background:transparent}
  .dstable tr{background:#fff;border:1px solid var(--rule);border-radius:12px;
    padding:6px 4px;margin-bottom:12px;box-shadow:0 1px 2px rgba(18,46,99,.06)}
  .dstable tr:hover td{background:transparent}
  .dstable td{border-bottom:0;padding:8px 12px;min-width:0}
  /* The column heading has to come back as a label: on the desktop grid the
     <thead> was carrying that job, and a stack of bare inputs is unreadable. */
  /* Keyed on the cell's class, not its position: Terms II and III insert the
     FA and IA columns, which would shift every nth-child label by two and
     silently mislabel the whole card. */
  .dstable td::before{display:block;font-size:11px;font-weight:700;color:var(--soft);
    text-transform:uppercase;letter-spacing:.05em;margin-bottom:3px}
  .dstable td.dates::before{content:"Instructional dates"}
  .dstable td.chapter::before{content:"Chapter & concept"}
  .dstable td.planned::before{content:"Periods planned"}
  .dstable td.actual::before{content:"Periods actual"}
  .dstable td.status::before{content:"Status"}
  .dstable td.fa::before{content:"Formative assessment"}
  .dstable td.ia::before{content:"Internal assessment"}
  .dstable td.remarks::before{content:"Reason for lead or lag"}
  .dstable td.var::before{content:"Variance"}
  .dstable td.status{width:auto}
  .dstable td.wide{min-width:0}
  .dstable td:first-child{background:var(--sky);border-radius:9px;
    font-size:15px;padding:10px 12px}
  .dstable td.num{text-align:left}
  /* 44px is the tap-target floor, and 16px is the font size below which iOS
     zooms the whole page on focus -- which is itself how a viewport ends up
     scrolled sideways. */
  .dstable input, .dstable select, .dstable textarea{min-height:44px;font-size:16px;
    min-width:0;width:100%}

  /* LP-04 tick boxes: five groups of small labels is the hardest thing on the
     product to tap. One column, full-width rows, real hit areas. */
  .ticks{grid-template-columns:1fr}
  .tickgroup{border:1px solid var(--rule);border-radius:10px;padding:8px 10px;
    background:#fff}
  .tick{display:flex;align-items:center;gap:10px;min-height:44px;padding:2px 4px;
    border-radius:8px}
  .tick:active{background:var(--sky)}
  .tick input[type="checkbox"]{width:22px;height:22px;flex:none}
  .tick span{font-size:14.5px}
  .lpperiod input, .lpperiod select, .lpperiod textarea{min-height:44px;font-size:16px;
    min-width:0}
  .lpperiod textarea{min-height:66px}

  /* Every other table on these pages scrolls inside itself. The page body must
     never scroll sideways -- .dstable is excluded because it is cards here,
     not a table. */
  .grid:not(.dstable){display:block;overflow-x:auto;-webkit-overflow-scrolling:touch}

  /* A flex row whose items cannot shrink is the commonest cause of a sideways
     page: min-width defaults to auto, so a long unwrapped caption sets the
     width of the viewport. */
  .actions, .toolbar, .totals, .qhead, .pubbox{flex-wrap:wrap}
  .actions>*, .toolbar>*, .totals>*{min-width:0}
  .actions .btn{width:100%}

  /* Header controls are tap targets too. */
  .navlink{margin-right:10px;display:inline-flex;align-items:center;min-height:44px}
  .bell{min-height:44px;min-width:44px;justify-content:center}
  .btn, .btn.small, .linkbtn{min-height:44px}
  .topbar{flex-wrap:wrap;gap:8px}
  .wrap{padding:0 14px}
  .scroller{overflow-x:auto;-webkit-overflow-scrolling:touch}
}

@media (max-width:700px){
  /* Leftovers from the desktop grid that still set a floor once the rows are
     cards: the Status column was sized for the longest LP-04 label, which on a
     360px screen is a 186px floor nothing else can shrink past. */
  .dstable td.status select{min-width:0}
  .dstable td.dates{min-width:0}
  .dstable td, .dstable td *{max-width:100%}
  /* Long unbroken strings -- an e-mail username, a chapter reference typed
     without spaces -- are the other way a card sets the page width. */
  .card, .lpperiod, .msg, .tasklist li, .notelist li{overflow-wrap:anywhere}
}
  /* The LP chapter header is a 2fr/1fr/1fr/1fr row. Four columns plus three
     gaps on a 360px screen leaves the last field 18px wide and still eight
     pixels over the edge -- the fields stack instead. */
@media (max-width:700px){
  .chead .fields{grid-template-columns:1fr 1fr}
  .chead .fields>label:first-child{grid-column:1/-1}
  .chead-meta{flex-wrap:wrap}
  .chead-meta .btn{margin-left:0}
}
@media (max-width:700px){
  /* `hidden` is a UA style (display:none) on an attribute selector, so the
     `.dstable tr{display:block}` above beat it and the pager hid nothing --
     the attribute was set, the DOM said one row, and all eight still drew.
     Only a screenshot catches that; querySelector(':not([hidden])') agrees
     with the attribute, not with the pixels. */
  .dstable tr[hidden]{display:none}
}

/* --- M6: set-your-password ------------------------------------------- */
.pwcard{max-width:460px;margin:40px auto}
.pwcard h1{font-size:20px;margin-bottom:6px}
.pwcard label{display:block;margin:14px 0;font-weight:600}
.pwcard input{display:block;width:100%;margin-top:5px;padding:10px;min-height:44px;
  border:1.5px solid var(--rule);border-radius:9px;font:inherit}
.pwcard small{display:block;font-weight:400;margin-top:5px;line-height:1.5}
.pwcard .btn{width:100%}
.pwout{margin-top:14px;text-align:center}
.linkbtn.dark{border-color:var(--rule);color:var(--soft);min-height:44px}
/* The client's crest, a step smaller than the product mark it sits beside. */
.crest-school{height:34px;width:34px;margin-left:4px}
/* A second, quieter way into the same card: the primary action still reads as
   the primary one. */
.plancard .cta.ghost{background:#fff;color:var(--deep);border:1px solid var(--rule)}

/* --- The approval stamp ------------------------------------------------ */
/* Drawn, not photographed. A PNG would be one more asset to lose on a LAN
   with no internet, would blur on a retina screen and would print grey; this
   recolours with the palette and stays crisp at any size.
   Colour is never the only signal -- the word is always there, and the
   aria-label carries who and when for a screen reader. */
.stamp{display:inline-flex;flex-direction:column;align-items:center;gap:2px;
  padding:7px 18px;border:3px double currentColor;border-radius:8px;
  transform:rotate(-6deg);transform-origin:left center;
  font-weight:800;letter-spacing:.14em;text-transform:uppercase;
  /* Slightly translucent so it reads as ink on the page rather than a badge
     sitting on top of it. */
  opacity:.92}
.stamp-word{font-size:17px;line-height:1.1}
.stamp-meta{font-size:9.5px;font-weight:700;letter-spacing:.04em;
  text-transform:none;opacity:.85}
.stamp-approved{color:#1B7A3E}
.stamp-signed{color:#12467F}
.stamp-rejected,.stamp-returned{color:#B4231A}
.stamp-pending{color:#8A6100;border-style:dashed;transform:rotate(-3deg)}
.stamp-closed{color:#4B5563}
/* Where a stamp sits beside a heading rather than over a sheet.
   The rotation makes the painted box taller than the laid-out one, so the row
   needs vertical room it does not appear to need -- without it the corners of
   the stamp strike through the line above, which looks like a rendering bug
   rather than a flourish. */
.stampbar{display:flex;align-items:center;gap:22px;flex-wrap:wrap;
  margin:18px 0 20px;padding:6px 0}
@media (max-width:700px){
  .stamp{transform:rotate(-4deg);padding:6px 14px}
  .stamp-word{font-size:15px}
}
@media print{.stamp{opacity:1}}
/* Handover queue */
.hoverow .who2{font-weight:700;color:var(--deep)}
/* Who typed a figure, when that is not who holds the subject now. Quiet on
   purpose: it is provenance, not a status, and it appears on a handful of rows
   in a handful of plans. */
.byline{display:block;font-size:10px;font-weight:700;color:var(--soft);
  letter-spacing:.02em;margin-top:3px;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;max-width:100%}
@media (max-width:700px){.byline{display:inline-block;margin:4px 0 0}}

/* --- Where things stand: one full-width row per dimension --------------- */
/* Three columns at a third of the page each clipped the teacher table's own
   action column and wrapped every name onto two lines. Nobody compares a class
   against a subject, so the side-by-side bought nothing the stacking costs. */
.standrow{display:grid;grid-template-columns:210px minmax(0,1fr);gap:24px;
  align-items:start;padding:18px 0;border-top:1px solid var(--rule)}
.standrow:first-of-type{border-top:0;padding-top:6px}
.standside h3{font-size:12px;text-transform:uppercase;letter-spacing:.06em;
  color:var(--deep);margin-bottom:10px}
/* "On track" was wrapping to two lines in a 210px rail, which made the legend
   read as five items instead of three. */
.standside .donut-legend .dl{white-space:nowrap}
/* One name column across all three tables, so the bars start on a common line
   and the three panels read as one instrument rather than three. */
.standtable th:first-child, .standtable td:first-child{width:190px}
.standmain{min-width:0}
.standtable{margin-top:0}
.standtable td{vertical-align:middle}
.standname{font-weight:600;color:var(--ink);white-space:nowrap}
.standtable .covcol{width:44%;min-width:190px}

/* The bullet bar. One hue for magnitude -- the band is carried by the number
   beside it and by the word in the donut legend, never by the bar alone, so
   the fill is not doing double duty as a value ramp. */
.covbar{display:inline-block;position:relative;width:calc(100% - 52px);
  max-width:190px;height:8px;vertical-align:middle;
  background:var(--rule);border-radius:999px;overflow:hidden}
.covfill{display:block;height:100%;border-radius:999px;background:var(--blue)}
/* Status tints, kept muted: the bar is magnitude and the chip is the verdict.
   A saturated red bar would shout the same thing twice. */
.covfill.ok{background:#1F9D55}.covfill.warn{background:#E0A106}.covfill.bad{background:#DE4638}
/* The on-track line at 95%. A hairline, not a dash: dashing reads as a
   projection when it is a fixed threshold. */
.covtarget{position:absolute;left:95%;top:-3px;bottom:-3px;width:1.5px;
  background:var(--deep);opacity:.5;border-radius:1px}
/* The number stays in ink. The bar already carries the band and the variance
   chip carries it again in words -- a third copy in coloured type was the same
   fact said three times, and it is the copy that has to survive a monochrome
   printout, so it is the one that should not depend on hue. */
.covpct{display:inline-block;min-width:42px;margin-left:10px;text-align:right;
  font-size:12.5px;font-weight:700;color:var(--ink);
  font-variant-numeric:tabular-nums;vertical-align:middle}

@media (max-width:900px){
  .standrow{grid-template-columns:1fr;gap:12px}
  .standside{display:flex;align-items:center;gap:16px}
  .standside h3{margin-bottom:0}
  .standtable .covcol{min-width:150px}
  .covbar{max-width:120px}
}


/* --- Phone: the two panel pieces that refused to shrink ----------------- */
@media (max-width:700px){
  /* `.note` is flex:none so it holds its full sentence -- 406px of legend on a
     360px screen, which dragged the whole page sideways. It gets its own line
     rather than being truncated: it is the key to the colours. */
  .panel>header{flex-wrap:wrap}
  .panel>header .note{flex:1 1 100%;text-align:left;margin-top:5px}

  /* The Pareto's three columns need 150 + bar + 112 before gaps. Stack the
     label above its own bar instead of squeezing all three onto one line. */
  .pareto li{grid-template-columns:1fr auto;gap:4px 10px;margin-bottom:14px}
  .pareto .name{grid-column:1/-1}
  .pareto .val{min-width:0}
}
@media (max-width:700px){
  /* Five jump links plus a label need more than 360px on one line. They wrap
     rather than scroll: a sticky bar that scrolls sideways hides its own last
     item behind an edge nobody thinks to drag. */
  .secnav{flex-wrap:wrap;row-gap:2px;padding-bottom:6px}
  .secnav a{min-height:32px;display:inline-flex;align-items:center}
}

/* --- Show/hide password ------------------------------------------------ */
/* The wrapper is created by the script, so a page without JS has a plain
   password field and no empty button sitting beside it. */
.pwwrap{position:relative;display:block}
.pwwrap input{width:100%;padding-right:46px}
.pwtoggle{position:absolute;right:5px;top:50%;transform:translateY(-50%);
  display:flex;align-items:center;justify-content:center;
  width:36px;height:36px;padding:0;margin:0;
  background:none;border:0;border-radius:8px;color:var(--soft);cursor:pointer}
.pwtoggle:hover,.pwtoggle:focus-visible{color:var(--deep);background:var(--sky)}
.pwtoggle svg{width:19px;height:19px;display:block}
@media (max-width:700px){
  .pwtoggle{width:44px;height:44px}
  .pwwrap input{padding-right:52px}
}

/* --- Who has it, who decided, by when ---------------------------------- */
/* Four columns of fact per step. The name is the point -- a teacher asking
   "where is my plan?" needs a person to go to, not a state. */
.chain3{list-style:none;padding:0;margin:14px 0;display:grid;gap:2px}
.chain3 li{display:grid;grid-template-columns:120px minmax(0,1fr) auto auto;
  gap:12px;align-items:baseline;padding:9px 12px;border-radius:9px;
  background:#fff;border:1px solid var(--rule);font-size:13px}
.chain3 .c-role{font-size:11px;text-transform:uppercase;letter-spacing:.05em;
  font-weight:700;color:var(--soft)}
.chain3 .c-who{font-weight:700;color:var(--ink);min-width:0;
  overflow:hidden;text-overflow:ellipsis}
.chain3 .c-when{font-size:12px;color:var(--soft);white-space:nowrap}
/* Done is settled, `now` is whose desk it is on. Neither uses a status colour:
   being in the queue is not good or bad, it is just where the thing is. */
.chain3 li.done{background:var(--sky)}
.chain3 li.done .c-who{color:var(--deep)}
.chain3 li.now{border-color:var(--blue);border-left:4px solid var(--blue)}
@media (max-width:700px){
  .chain3 li{grid-template-columns:1fr auto;row-gap:2px}
  .chain3 .c-role{grid-column:1/-1}
}

/* --- drill-through headline numbers ------------------------------------ */
.bignum{font-size:32px;font-weight:800;color:var(--deep);margin:2px 0 4px;
  font-variant-numeric:tabular-nums;line-height:1}
.bignum.ok{color:#146B3A}.bignum.warn{color:#7A4F01}.bignum.bad{color:#8B1D13}
.bignum.muted{color:var(--soft)}
/* The heatmap's row label is a link now; keep it reading as a label. */
.rowhead a{color:inherit;text-decoration:none;border-bottom:1px solid transparent}
.rowhead a:hover,.rowhead a:focus-visible{border-bottom-color:currentColor}

/* --- coverage charts ----------------------------------------------------- */
/* Three readings of the figure the KPI band states once: which way it is
   going, what shape the school is, and which level carries it. Inline SVG and
   CSS -- there is no chart library here and no build step to add one, and a
   CDN script is a blank panel on the day the school LAN has no internet. */
.charts{display:grid;grid-template-columns:1.35fr 1fr 1.15fr;gap:16px;
  margin:0 0 22px;align-items:stretch}
@media (max-width:1080px){.charts{grid-template-columns:1fr}}
.chart{background:#fff;border:1px solid var(--rule);border-radius:12px;
  padding:13px 15px 12px;display:flex;flex-direction:column}
.chart>header{display:flex;align-items:baseline;justify-content:space-between;
  gap:10px;margin-bottom:12px}
.chart h3{font-size:12.5px;font-weight:800;color:var(--deep);margin:0;
  text-transform:uppercase;letter-spacing:.05em}
.chart .note{font-size:11.5px;color:var(--soft)}
.chart .small{font-size:12.5px;margin:0}

/* 1 -- the trend line. The plot is a positioned box: the SVG stretches to it
   (preserveAspectRatio="none", so the drawing is resolution-free) and the
   markers and labels sit on top in HTML, where a circle stays a circle. */
.trend .plot{position:relative;height:120px;margin-bottom:2px}
.trendsvg{width:100%;height:100%;display:block;overflow:visible}
.trendsvg .fill{fill:rgba(10,92,200,.08)}
.trendsvg .line{fill:none;stroke:var(--blue);stroke-width:2;
  stroke-linejoin:round;stroke-linecap:round}
.trendsvg .target{stroke:#9BB6DE;stroke-width:1;stroke-dasharray:3 3}
/* 8px is the floor for a hit target and for a mark anyone can see; the white
   ring keeps a dot legible where the line passes under it. */
.trend .dot{position:absolute;width:9px;height:9px;border-radius:50%;
  transform:translate(-50%,-50%);background:var(--blue);
  box-shadow:0 0 0 2px #fff;cursor:default}
.trend .dot.ok{background:#1F9D55}
.trend .dot.warn{background:#E0A106}
.trend .dot.bad{background:#DE4638}
.trend .tlabel{position:absolute;font-size:11px;font-weight:700;
  font-variant-numeric:tabular-nums;transform:translate(-50%,-160%);
  color:var(--deep);white-space:nowrap;pointer-events:none}
.trend .tlabel.low{color:#B4231A;transform:translate(-50%,60%)}
/* Left, because the last point's value label owns the right-hand end and the
   two were drawing on top of each other. */
.trend .tlabel.target-lbl{left:0;transform:translate(0,-130%);
  font-weight:600;color:#7C93B8;font-size:10.5px}
.trend .xaxis{display:flex;justify-content:space-between;
  font-size:10.5px;color:var(--soft);margin-top:9px}
.chart.trend .xaxis{margin-top:auto;padding-top:9px}

/* 2 -- the stacked bar. A 2px surface gap between segments, so two adjacent
   fills never read as one longer one. */
.spread .stack{display:flex;gap:2px;height:22px;border-radius:6px;overflow:hidden;
  background:var(--rule)}
.spread .seg{display:block;height:100%}
.spread .seg.ok{background:#1F9D55}
.spread .seg.warn{background:#E0A106}
.spread .seg.bad{background:#DE4638}
.spread .seg.blank{background:#C7D2E4}
.seglegend{list-style:none;padding:0;margin:11px 0 0;display:flex;
  flex-wrap:wrap;gap:5px 14px}
.seglegend li{display:flex;align-items:center;gap:6px;font-size:11.5px;color:var(--soft)}
.seglegend b{color:var(--ink);font-variant-numeric:tabular-nums}

/* 3 -- coverage by level. Horizontal, because the labels are words of uneven
   length and nobody should have to turn their head to read a chart. */
/* padding:0 is load-bearing. A <ul> carries a 40px inline start padding by
   default; with it, the fixed bar and value columns filled the card and the
   grid gave the name column 0px -- every level name was in the DOM and none
   of them were on screen. */
.lbars{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:11px}
/* Two rows per level: the name and its number on top, the bar full width
   underneath. A 218px card cannot hold name, track and value side by side
   without starving one of them, and the name is the one that was starved. */
.lbars li{display:grid;grid-template-columns:1fr auto;gap:2px 8px;align-items:baseline}
.lb-name{font-size:12px;color:var(--ink);min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lb-track{grid-column:1/-1;position:relative;height:9px;border-radius:999px;
  background:var(--rule);overflow:hidden}
.lb-fill{display:block;height:100%;border-radius:999px}
.lb-fill.ok{background:#1F9D55}.lb-fill.warn{background:#E0A106}.lb-fill.bad{background:#DE4638}
.lb-val.none{font-size:11px;font-weight:600;color:var(--soft)}
/* The value is always printed. The amber mark is 2.21:1 on white -- legible as
   a mark, not as the only carrier of the number. */
.lb-val{font-size:12.5px;font-weight:800;text-align:right;
  font-variant-numeric:tabular-nums;color:var(--deep)}
.lb-val.ok{color:#1B5E36}.lb-val.warn{color:#B07405}.lb-val.bad{color:#B4231A}

/* --- coverage heatmap, one table per level ------------------------------- */
/* Each level is its own small table with its own subjects. The header carries
   the level's coverage and how much of it is actually reported on, so a table
   full of blanks cannot be mistaken for a table full of zeros. */
.heatlevel + .heatlevel{margin-top:20px;padding-top:18px;border-top:1px solid var(--rule)}
.hl-head{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-bottom:9px}
.hl-head h3{font-size:14px;font-weight:800;color:var(--deep);margin:0;
  text-transform:uppercase;letter-spacing:.04em}
.hl-note{font-size:12px;color:var(--soft);margin-left:auto}
@media (max-width:700px){.hl-note{margin-left:0;width:100%}}

/* --- teacher desk: returned to you --------------------------------------- */
/* Red, because returned work is the only thing on the desk that is already
   late by definition: it was planned, reviewed, and sent back. It sat in the
   same pale card as everything else.
   Two strengths. With something in it, a full red card. Empty, a tint only --
   an alarm over "nothing has been returned to you" is an alarm that gets
   ignored by the time one matters. The red is the variance red the whole app
   uses for "behind", not a fourth red. */
.qcard.returned{background:#FEF3F2;border-color:#F3C9C4}
.qcard.returned h3{color:#8B1D13}
.qcard.returned h3 .n{color:#8B1D13}
.qcard.returned li{background:#fff;border-color:#F3C9C4}
.qcard.returned .none{color:#8B4A42}
/* The calm state: the card still reads as the returned-work card, but nothing
   about it says act now. */
.qcard.returned.empty{background:#FEF9F8;border-color:#F0DCDA}
.qcard.returned.empty h3,
.qcard.returned.empty h3 .n{color:var(--deep)}
.qcard.returned.empty .none{color:var(--soft)}

/* --- not started, grouped by level --------------------------------------- */
/* Folded by default: the count and the whose-move chips are on the summary, so
   the panel answers "which level is stuck, and on whom?" without opening
   anything. The rows are for after that question is answered. */
.nsgroups{display:flex;flex-direction:column;gap:8px}
.nsgroup{border:1px solid var(--rule);border-radius:10px;background:#fff}
.nsgroup summary{display:grid;grid-template-columns:1fr auto auto auto 18px;
  gap:12px;align-items:center;padding:11px 14px;cursor:pointer;font-size:13px;
  list-style:none}
.nsgroup summary::-webkit-details-marker{display:none}
.nsgroup summary::marker{content:""}
.nsgroup summary:hover{background:var(--sky);border-radius:10px}
.nsgroup[open] summary{border-bottom:1px solid var(--rule);border-radius:10px 10px 0 0}
.nsgroup summary::after{content:"";justify-self:end;width:8px;height:8px;
  border-right:2px solid var(--soft);border-bottom:2px solid var(--soft);
  transform:rotate(45deg) translate(-2px,-2px);transition:transform .15s}
.nsgroup[open] summary::after{transform:rotate(225deg) translate(-2px,-2px)}
.ns-level{font-weight:700;color:var(--deep);min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ns-lic{color:var(--soft);font-size:12.5px;white-space:nowrap}
.ns-count{font-weight:800;color:var(--deep);font-variant-numeric:tabular-nums}
.ns-owners{display:flex;gap:5px;flex-wrap:wrap;justify-content:flex-end}
.nsgroup .scroller{padding:0 2px 2px}
@media (max-width:900px){
  .nsgroup summary{grid-template-columns:1fr auto;row-gap:4px}
  .ns-lic,.ns-owners{grid-column:1/-1;justify-content:flex-start;white-space:normal}
}

/* --- return history on a queue card ------------------------------------- */
/* What happened to this item before it reached the reader. Folded when it has
   been sent back once, open when more than once: twice returned is a pattern,
   and a pattern should not need a click to be noticed. */
.rethist{margin-top:8px}
.rethist summary{cursor:pointer;display:flex;align-items:center;gap:8px;
  font-size:12.5px;list-style:none}
.rethist summary::-webkit-details-marker{display:none}
.rethist summary::marker{content:""}
.rethist summary:hover .chip{filter:brightness(.97)}
.rethist-list{list-style:none;margin:10px 0 2px;padding:0 0 0 14px;
  border-left:2px solid #F0D9A8;display:flex;flex-direction:column;gap:10px}
/* `.qcard li` is a space-between flex row and these sit inside a .qcard, so
   without display:block the name, the date and the quote spread across the
   card in three columns instead of stacking. Same trap as the rail panel. */
.rethist-list li{display:block;font-size:12.5px;background:none;border:0;padding:0}
.rh-who{font-weight:600;color:var(--deep)}
.rh-when{color:var(--soft);margin-left:6px;font-variant-numeric:tabular-nums}
.rethist-list blockquote{margin:4px 0 0;padding:7px 10px;background:#FFFBF3;
  border:1px solid #F0D9A8;border-radius:8px;color:#5A4108;line-height:1.5;
  overflow-wrap:anywhere}

/* --- rail: returned by the Principal ------------------------------------ */
/* Amber, like the queue banner it precedes, because it is the same fact at an
   earlier moment: read this before you decide. Not the variance red -- nobody
   here is behind on teaching. */
.qcard.preturns{border-color:#F0D9A8;background:#FFFBF3}
.qcard.preturns h3{display:flex;align-items:center;gap:8px;color:#7A4F01}
/* `.qcard li` is a space-between flex row -- right for the one-line rail lists
   it was written for, wrong here, where each item is a stacked block of title,
   meta and quote. Overriding `display` is the whole fix; without it the three
   children sit in three columns and the subject name wraps a letter at a time.
   The height cap goes too: these items are three lines, not one. */
/* Bounded and scrolled, not capped. When this lived in a narrow sticky rail
   an unbounded list was merely long; in the wide grid it made a 4,000px card
   and pushed everything beside it into a column of empty page. Scrolling keeps
   every return reachable -- a cap would hide some of them, and the whole point
   of this panel is that none of them is hidden. */
.qcard.preturns ul{list-style:none;display:flex;flex-direction:column;gap:10px;
  margin-top:10px;max-height:340px;overflow-y:auto;padding-right:4px;
  scrollbar-width:thin}
.qcard.preturns li{display:block;background:#fff;border:1px solid #F0D9A8;
  border-radius:9px;padding:9px 11px;font-size:12.5px}
.qcard.preturns li>a{display:block;color:var(--deep);font-weight:600;
  font-size:13px;text-decoration:none;overflow-wrap:anywhere}
.qcard.preturns li>a:hover{text-decoration:underline}
.pr-meta{display:block;font-size:11.5px;color:var(--soft);margin-top:2px}
.qcard.preturns blockquote{margin:7px 0 0;padding:6px 9px;background:#FFFBF3;
  border-left:3px solid #C79A4B;border-radius:0 6px 6px 0;
  font-size:12.5px;line-height:1.5;color:#5A4108;overflow-wrap:anywhere}

/* --- pending board: find a teacher -------------------------------------- */
/* Fifty teachers make the board a scroll, so it needs a way in by name. The
   label is present but off-screen: a placeholder is not a label, and it is
   gone the moment anyone types. */
.visually-hidden{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.pendsearch{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:0 0 16px}
.pendsearch input{flex:0 1 320px;min-width:0;padding:8px 12px;font:inherit;
  font-size:13.5px;color:var(--ink);background:#fff;
  border:1px solid var(--rule);border-radius:8px}
.pendsearch input:focus-visible{outline:2px solid var(--blue);outline-offset:1px;
  border-color:var(--blue)}
.pendcount{font-size:12.5px}
@media (max-width:700px){.pendsearch input{flex:1 1 100%}}

/* --- what I sent back --------------------------------------------------- */
/* One row per return, its comment folded underneath. The summary is a grid so
   the date, the subject and the teacher line up down the list -- a return
   history is read by scanning one column, not by reading each row. */
.retsum{margin:-6px 0 14px}
.retlist{list-style:none;display:flex;flex-direction:column;gap:8px}
.retlist>li{background:#fff;border:1px solid var(--rule);border-radius:10px;
  box-shadow:0 1px 2px rgba(18,46,99,.06)}
.retlist summary{display:grid;grid-template-columns:96px 1fr 150px auto 18px;
  gap:12px;align-items:center;padding:11px 14px;cursor:pointer;font-size:13.5px}
/* The default triangle is replaced, not removed: a row that opens has to look
   like a row that opens, or the comment -- the reason the page exists -- stays
   folded because nobody knows it is there. */
.retlist summary::-webkit-details-marker{display:none}
.retlist summary::marker{content:""}
.retlist summary::after{content:"";justify-self:end;width:8px;height:8px;
  border-right:2px solid var(--soft);border-bottom:2px solid var(--soft);
  transform:rotate(45deg) translate(-2px,-2px);transition:transform .15s}
.retlist details[open] summary::after{transform:rotate(225deg) translate(-2px,-2px)}
.retlist summary:hover{background:var(--sky)}
.retlist details[open] summary:hover{background:transparent}
.ret-when{color:var(--soft);font-variant-numeric:tabular-nums}
.ret-what{color:var(--deep);font-weight:600;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ret-who{color:var(--soft);min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rettype{font-weight:400;color:var(--soft);font-size:12px;margin-left:6px}
.retlist details[open] summary{border-bottom:1px solid var(--rule)}
.ret-body{padding:12px 14px 14px}
.ret-label{font-size:11.5px;text-transform:uppercase;letter-spacing:.04em;margin-bottom:6px}
.ret-body blockquote{margin:0 0 10px;padding:9px 12px;border-left:3px solid var(--blue);
  background:var(--sky);border-radius:0 8px 8px 0;font-size:13.5px;line-height:1.55;
  color:var(--ink);overflow-wrap:anywhere}
@media (max-width:700px){
  .retlist summary{grid-template-columns:1fr;gap:3px}
  .ret-what,.ret-who{white-space:normal}
}

/* --- back from the Principal ------------------------------------------- */
/* On an Incharge's queue card, work she approved once and the Principal sent
   back. Amber rather than the variance red: this is "read this before you
   decide", not "someone is behind". */
.preturn{margin:10px 0 0;padding:9px 12px;border-radius:8px;
  background:#FFF6E5;border:1px solid #F0D9A8;color:#5A4108;
  font-size:13px;line-height:1.5}
.preturn strong{color:#7A4F01}
.preturn q{font-style:italic}

/* --- kolam rule --------------------------------------------------------- */
/* A traditional border, drawn as a tileable SVG rather than embedded as an
   image: it repeats to any width, stays crisp at any zoom, and is one small
   file that ships with the app -- the LAN may have no internet.
   The red here is decorative and deliberately NOT the status red: nothing on
   this rule means "behind", and it never sits beside a chip that does. */
.kolamrule{height:48px;margin:6px 0 20px;
  background:url("../brand/kolam-border.a622f24ae845.svg") repeat-x center/auto 48px}
@media (max-width:700px){.kolamrule{height:32px;background-size:auto 32px;margin:4px 0 14px}}
/* Ink is expensive and a flourish is not evidence. */
@media print{.kolamrule{display:none}}

/* --- kolam corner ------------------------------------------------------- */
/* The same motif as the Incharge's rule, but as a corner ornament rather than
   a divider: the Principal's page is the densest in the product and a
   full-width band across it would be one more thing competing with the
   numbers.
   A pseudo-element, so it adds no node anyone can tab into and cannot push
   the layout around. Behind the content, non-interactive, and faint enough
   that it reads as paper rather than as data. */
/* Pending board: one card per person, their arrears on the header. */
.pendcard h2{font-size:15px;margin:0}
.pendcard h2 a{color:var(--deep);text-decoration:none;border-bottom:1px solid transparent}
.pendcard h2 a:hover{border-bottom-color:currentColor}
.pendcard+.pendcard{margin-top:14px}

/* ==========================================================================
   Aug 7 v2 -- executive refresh
   --------------------------------------------------------------------------
   A visual pass only: no panel moved, no link changed, no number recomputed.
   It is appended rather than edited in place so the whole thing is one block
   that can be lifted out to return to v1.

   The brief asked for React, Tailwind, shadcn and Framer Motion. This app is
   Django templates with HTMX and Alpine and no build step -- a fixed
   constraint, because the school LAN may have no internet and there is no
   pipeline on the deployment box. Everything below is the same design in the
   stack that exists: CSS custom properties for the tokens, CSS transitions
   and keyframes for the motion, inline SVG for the icons.
   ========================================================================== */

/* --- motion, declared once ------------------------------------------------
   Every animation here is decorative. Anyone who has asked their system not
   to animate gets the finished state immediately, not a faster version of the
   same movement. */
@media (prefers-reduced-motion:no-preference){
  .card,.panel,.qcard,.chart,.kpiband,.heatlevel{animation:riseIn .38s cubic-bezier(.16,1,.3,1) both}
  .zone>*+*{animation-delay:.05s}
  .rail>*+*{animation-delay:.08s}
}
@keyframes riseIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}

/* --- header ---------------------------------------------------------------
   Taller, and the underline on every link is gone: eleven underlines is
   eleven things shouting. The page you are on wears a pill instead, so the
   emphasis lands once. */
.topbar{padding:16px 28px}
.navlink{border-bottom:0;margin-right:2px;padding:8px 13px;border-radius:var(--r-pill);
  color:#C9DBFA;font-weight:500;line-height:1;
  transition:background-color .18s ease,color .18s ease}
.navlink:hover,.navlink:focus-visible{background:rgba(255,255,255,.10);color:#fff}
.navlink.active{background:rgba(255,255,255,.16);color:#fff;font-weight:600}
.who{color:#C9DBFA;margin-left:6px}
.linkbtn{border-radius:var(--r-pill);padding:7px 15px;font-weight:500;
  border-color:rgba(255,255,255,.28);transition:background-color .18s ease}
.linkbtn:hover{background:rgba(255,255,255,.12)}

/* --- surfaces -------------------------------------------------------------
   Softer corners, a shadow instead of a border. The border is kept at a hair
   of its old weight rather than removed: on a white-on-near-white page the
   card edge is the only thing separating two surfaces for a reader in bright
   light or forced colours. */
.card,.panel,.qcard,.chart{border-radius:var(--r-card);border-color:var(--rule);
  box-shadow:var(--sh-1);transition:box-shadow .22s ease,transform .22s ease}
.card,.panel{padding:var(--s3)}
.qcard,.chart{padding:20px}
.panel>header{margin-bottom:var(--s2)}
.qcard.act:hover,.plancard:hover{box-shadow:var(--sh-2)}

/* --- the banner -----------------------------------------------------------
   A gradient rather than a flat navy block, and more room around the words.
   The date panel keeps its weight: it is the one fixed point on three desks. */
.todayhero{border-radius:var(--r-card);border:0;box-shadow:var(--sh-2)}
.th-side{background:linear-gradient(155deg,#1B4FA8 0%,var(--deep) 55%,#0A2A6B 100%);
  padding:26px 28px}
.th-body{padding:24px 30px}
.th-greet strong{font-size:19px;letter-spacing:-.01em}
.th-date{font-size:29px;letter-spacing:-.02em}

/* --- the KPI band ---------------------------------------------------------
   Bigger figure, quieter label, and the dividing rules replaced by space.
   Vertical hairlines between five cells is four lines doing what a gap does. */
.kpiband{padding:var(--s3) 26px;border-radius:var(--r-card)}
.kpiband .cell{border-left:0;padding:2px var(--s2)}
.kpiband .cells{gap:6px}
.kpiband .lbl{font-size:11px;letter-spacing:.07em;font-weight:600;color:var(--soft)}
.kpiband b{font-size:34px;font-weight:600;letter-spacing:-.03em}
.kpiband .fig{font-size:50px;font-weight:700}
.kpiband .meter{margin-top:18px;height:10px}
.meter{border-radius:var(--r-pill)}
.meter>span{border-radius:var(--r-pill);transition:width .8s cubic-bezier(.16,1,.3,1)}

/* --- tables ---------------------------------------------------------------
   Rows separated by a soft rule rather than boxed in a grid, and the header
   loses its solid blue fill. The heatmap cells keep their bands: those carry
   meaning and are not decoration. */
.grid{border-radius:var(--r-inner);box-shadow:none;border:1px solid var(--rule)}
.grid thead th{background:#F4F7FC;color:var(--deep);border-bottom:1px solid var(--rule);
  font-weight:600;letter-spacing:.03em}
.grid td{border-bottom:1px solid #F1F5FA}
.grid tbody tr:last-child td{border-bottom:0}
.grid tbody tr{transition:background-color .15s ease}
.grid tbody tr:hover{background:#FAFCFF}

/* --- charts ---------------------------------------------------------------
   Rounded ends on every bar, and the fills grow on load rather than appearing
   at length. */
.lb-track,.spread .stack{border-radius:var(--r-pill)}
.spread .stack{height:24px}
.spread .seg:first-child{border-radius:var(--r-pill) 0 0 var(--r-pill)}
.spread .seg:last-child{border-radius:0 var(--r-pill) var(--r-pill) 0}
@media (prefers-reduced-motion:no-preference){
  .lb-fill,.spread .seg{animation:growW .7s cubic-bezier(.16,1,.3,1) both}
  .trendsvg .line{stroke-dasharray:600;animation:draw 1.1s cubic-bezier(.16,1,.3,1) both}
}
@keyframes growW{from{transform:scaleX(0);transform-origin:left}to{transform:none}}
@keyframes draw{from{stroke-dashoffset:600}to{stroke-dashoffset:0}}
.trendsvg .line{stroke-width:2.5}

/* --- headings -------------------------------------------------------------
   Larger, tighter, and the section headings stop shouting in capitals. Only
   the small labels keep them, which is what capitals are for. */
h1{font-size:26px;font-weight:700;letter-spacing:-.02em;margin-bottom:var(--s2)}
h1 small{font-weight:400;letter-spacing:normal}
.panel h2{font-size:16px;font-weight:600;letter-spacing:-.01em}
.hl-head h3,.chart h3{text-transform:none;letter-spacing:-.01em;font-size:13.5px;
  font-weight:600}
.heatlevel+.heatlevel{margin-top:var(--s3);padding-top:var(--s3)}
.charts{gap:var(--s2);margin-bottom:var(--s3)}
.cockpit{gap:var(--s2)}

/* ==========================================================================
   Aug 7 v3 -- executive layout
   --------------------------------------------------------------------------
   Sticky chrome, a wider canvas, KPI sparklines and progress bars, sticky
   table headers. Appended as its own block so it lifts out cleanly to return
   to v2 or v1.

   Tailwind and DaisyUI are not here on purpose. Both want a build step, and
   the CDN build is a remote script -- this deploys to a school LAN that may
   have no internet, and the brand tests forbid a fetched URL in this file.
   The tokens below do the same job in the stack that ships.
   ========================================================================== */

/* --- a wider canvas, per the 1600px brief -------------------------------- */
.wrap{max-width:1180px}
.wrap.wide{max-width:1600px}
@media (min-width:820px){.wrap{width:calc(100% - 72px)}}

/* --- the header stays with you ------------------------------------------
   A dashboard is a page people scroll and then act on; the actions live in
   the bar. Sticky, and it gains a shadow once the page moves under it so the
   edge is legible against a white card. */
.topbar{position:sticky;top:0;z-index:50;
  box-shadow:0 1px 0 rgba(16,59,140,.10),0 6px 24px rgba(16,59,140,.10)}

/* --- KPI: sparkline, bar, and the counter's landing state ---------------- */
.kpiband .cell{position:relative;min-width:0}
.kpiband .spark{width:100%;height:30px;margin-top:12px;display:block;overflow:visible}
.kpiband .spark polyline{fill:none;stroke:var(--blue);stroke-width:2;
  stroke-linejoin:round;stroke-linecap:round;opacity:.85}
.kbar{display:block;height:6px;border-radius:var(--r-pill);background:var(--rule);
  margin-top:10px;overflow:hidden}
.kbar>span{display:block;height:100%;border-radius:var(--r-pill);background:var(--blue);
  transition:width .8s cubic-bezier(.16,1,.3,1)}
/* Tabular figures, so a counter ticking 0->88 does not shuffle the label
   under it sideways on every frame. */
.kpiband b{font-variant-numeric:tabular-nums}

/* --- tables keep their heading in view ----------------------------------
   The heatmap and the drill-downs are taller than the viewport; a class
   column with no subject heading above it is a wall of percentages. */
.scroller{max-height:min(70vh,620px);overflow:auto}
.grid thead th{position:sticky;top:0;z-index:2}
.tablebox .grid thead th{z-index:3}

/* --- hero: the AI status line -------------------------------------------
   The quick-action row that used to sit here was four links to Approvals,
   Pending, Staffing and Appropriations -- all four already in the top nav.
   A second copy of the menu is not a shortcut, it is a second thing to keep
   in step. Only the status line remains, which is not navigation. */
.th-ai{display:inline-flex;align-items:center;gap:7px;margin-top:12px;
  font-size:12px;color:var(--soft)}
.th-ai .pip{width:7px;height:7px;border-radius:50%;background:#1F9D55;
  box-shadow:0 0 0 3px rgba(31,157,85,.16)}

/* --- focus, everywhere ---------------------------------------------------
   One visible ring on anything reachable by keyboard. The old rule only
   covered inputs, so tabbing through the nav was invisible. */
a:focus-visible,button:focus-visible,summary:focus-visible,
[tabindex]:focus-visible{outline:2px solid var(--blue);outline-offset:2px;
  border-radius:4px}
.topbar a:focus-visible,.topbar button:focus-visible{outline-color:#fff}

@media (max-width:700px){
  .wrap,.wrap.wide{width:100%}
  .topbar{position:static}          /* a sticky bar on a phone eats the page */
}

/* --- Incharge desk: the plan card's stats and actions ---------------------
   Two classes that had never been styled. `.plancard .cta.ghost` set colours
   for a *variant* of a base rule that was never written, so both links
   rendered as bare underlined text; and `.stats` is the Incharge desk's own
   class -- the teacher desk uses `.pc-stats`, which is styled -- so its value
   and label ran together as "10weeks 47periods".

   Written to match .btn rather than reinvent it: same radius, same weight,
   same blue, so the two desks offer the same affordance for the same act. */
.plancard .stats{display:flex;align-items:flex-end;gap:18px;flex-wrap:wrap;
  margin-top:10px}
.plancard .stats>span{display:flex;flex-direction:column;align-items:flex-start;gap:1px}
.plancard .stats b{font-size:16px;font-weight:700;color:var(--ink);line-height:1.1;
  font-variant-numeric:tabular-nums}
.plancard .stats .l{font-size:9.5px;text-transform:uppercase;letter-spacing:.05em;
  color:var(--soft);font-weight:600}
.plancard .stats .muted{font-size:11.5px;color:var(--soft);align-self:flex-end}

.plancard .cta{display:inline-flex;align-items:center;justify-content:center;
  background:var(--blue);color:#fff;border:1px solid transparent;border-radius:10px;
  padding:9px 16px;font-size:13px;font-weight:600;text-decoration:none;
  margin:12px 8px 0 0;
  transition:background-color .18s ease,box-shadow .18s ease,transform .18s ease}
.plancard .cta:hover{background:#1D4FD8;box-shadow:var(--sh-2);transform:translateY(-1px)}
.plancard .cta.ghost:hover{background:var(--sky);color:var(--deep)}

/* --- header layout, one row at every desktop width -----------------------
   The Incharge carries eight nav items where the Principal has six, and the
   pill padding made each one wider. `nav` was a plain block, so its children
   wrapped as inline content: Logout dropped onto a second line, the bar grew
   from 76px to 91px, and the brand -- which could not shrink -- was pushed
   off its own left edge.

   Three fixes, in the order they matter: the bar and the nav become real flex
   rows that do not wrap; the brand is allowed to shrink and ellipsis rather
   than overflow; and the links tighten as the window narrows so eight of them
   still fit before anything has to give. */
.topbar{flex-wrap:nowrap;gap:20px}
.brand{min-width:0;flex:0 1 auto;overflow:hidden}
.brand-name{white-space:nowrap}
.brand-inst{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
.topbar nav{display:flex;align-items:center;gap:2px;flex-wrap:nowrap;
  min-width:0;flex:0 1 auto}
.topbar nav form{display:inline-flex;flex:none;margin-left:4px}
.navlink{white-space:nowrap;margin-right:0}
.who{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;
  margin-left:10px}

/* Tighten as it narrows, so the row survives instead of wrapping. */
@media (max-width:1440px){
  .navlink{padding:8px 10px;font-size:12.5px}
  .topbar{gap:14px;padding:14px 20px}
}
@media (max-width:1200px){
  .navlink{padding:7px 8px;font-size:12px}
  /* The role is the first thing to go: the name still identifies who is
     signed in, and Logout sits beside it. */
  .who .role-sep,.who .role{display:none}
}
@media (max-width:1000px){
  /* Past here eight pills cannot fit honestly. The nav scrolls sideways
     rather than wrapping into a second bar or hiding links silently. */
  .topbar nav{overflow-x:auto;scrollbar-width:none}
  .topbar nav::-webkit-scrollbar{display:none}
  .who{display:none}
}

/* --- Incharge desk: the waiting-plan card as a row -----------------------
   It was a stack in a wide box: title, then figures, then two buttons, each
   on its own line down the left edge while two thirds of the card sat empty.
   Three cards filled a screen that could hold eight.

   Now one row -- who and what on the left, the figures in the middle, the two
   ways in on the right -- which is the same anatomy the teacher desk's card
   already uses, so the two desks read as one product. */
/* The identity column gets a floor. Without one the fixed stats and action
   columns took what they wanted and "VI-Lotus · Hindi Language" wrapped three
   lines deep in a card that was two thirds empty. */
.plancard .pl-row{display:grid;grid-template-columns:minmax(230px,1fr) auto auto;
  gap:24px;align-items:center}
.plancard .pl-id{display:flex;flex-direction:column;align-items:flex-start;
  gap:3px;min-width:0}
.plancard .pl-id .title{white-space:normal}
.plancard .pl-act{display:flex;align-items:center;gap:8px;justify-self:end}
/* The buttons already carry their own top margin from the stacked layout. */
.plancard .pl-act .cta{margin:0}
.plancard .stats{margin-top:0;flex-wrap:nowrap;white-space:nowrap}
.plancard .stats .muted{white-space:nowrap}

/* Narrow: back to a stack, but a deliberate one -- the actions go full width
   rather than floating at the end of a short line. */
@media (max-width:1080px){
  .plancard .pl-row{grid-template-columns:1fr;gap:12px}
  .plancard .pl-act{justify-self:stretch}
  .plancard .pl-act .cta{flex:1;justify-content:center}
  .plancard .stats{flex-wrap:wrap;white-space:normal}
}

/* --- table headings and section titles, tinted to the page ---------------
   The header had gone almost white in the v2 pass, which left the grid
   floating with nothing to anchor its top edge. Solid brand blue (where it
   started) is the other extreme: on a page of six stacked tables it reads as
   six navigation bars.

   A light wash of the brand hue instead -- the same blue the page is built
   from, at a fraction of its strength -- so the heading belongs to the table
   and the table belongs to the page. The level title takes the same wash with
   an accent edge, so title and header read as one object. */
.grid thead th{background:linear-gradient(180deg,#EEF4FE,#E6EEFB);
  color:var(--deep);border-bottom:1px solid #CFDFF8;
  box-shadow:inset 0 -1px 0 rgba(16,59,140,.06)}
.grid thead th:first-child{border-top-left-radius:var(--r-inner)}
.grid thead th:last-child{border-top-right-radius:var(--r-inner)}

.heatlevel .hl-head{background:linear-gradient(90deg,rgba(37,99,235,.09),rgba(37,99,235,0) 70%);
  border-left:3px solid var(--blue);border-radius:0 10px 10px 0;
  padding:9px 14px;margin-bottom:12px}
.heatlevel .hl-head h3{color:var(--deep)}

/* The row label column is the table's own spine: a whisper of the same wash
   so the eye can run down it without a rule drawn between every cell. */
.heat td.rowhead{background:#F8FAFE}
.heat tbody tr:hover td.rowhead{background:#EEF4FE}

/* --- table headings and titles: the brand blue, properly ------------------
   The wash was too quiet to read as a heading. This is the page's own navy,
   with white on it -- 10.4:1, so the column names are legible rather than
   merely visible -- and the level title takes a filled blue band beside it so
   the two read as one block of chrome above the data.

   The data cells stay white and keep their green/amber/red. Chrome may be as
   blue as it likes; it must never compete with the three colours on this
   table that actually mean something. */
.grid thead th{background:linear-gradient(180deg,#1B4FA8,var(--deep));
  color:#fff;border-bottom:0;
  box-shadow:inset 0 -2px 0 rgba(255,255,255,.12);
  font-weight:600;letter-spacing:.04em}
.grid thead th:first-child{border-top-left-radius:var(--r-inner)}
.grid thead th:last-child{border-top-right-radius:var(--r-inner)}

.heatlevel .hl-head{background:linear-gradient(90deg,#E8EFFC,rgba(232,239,252,.35) 78%,transparent);
  border-left:4px solid var(--blue);border-radius:0 12px 12px 0;
  padding:11px 16px;margin-bottom:12px}
.heatlevel .hl-head h3{color:var(--deep);font-size:14px}
.heatlevel .hl-note{color:var(--soft)}

/* --- "waiting for your approval" ----------------------------------------
   The same shape on both desks: a number, what it is made of, and the way in.
   Amber while there is work, calm when there is none -- the card should look
   different on a clear day, or it stops being read on a busy one. */
.qcard.waiting{border-color:#F0D9A8;background:#FFFBF3}
.qcard.waiting h3{color:#7A4F01}
.qcard.waiting h3 .n{color:#7A4F01;font-size:20px}
.qcard.waiting.clear{border-color:var(--rule);background:#fff}
.qcard.waiting.clear h3,.qcard.waiting.clear h3 .n{color:var(--deep)}
.wkinds{list-style:none;padding:0;margin:10px 0 12px;display:flex;
  flex-direction:column;gap:5px}
/* justify-content is load-bearing: `.qcard li` is a space-between flex row,
   so without this the count and its label fly to opposite edges of the card
   and read as two unrelated columns. Third rule in this stylesheet to be
   caught by that inheritance. */
.wkinds li{display:flex;align-items:baseline;justify-content:flex-start;gap:7px;
  font-size:12.5px;color:var(--ink);background:none;border:0;padding:0}
.wkinds b{font-size:15px;font-weight:700;color:#7A4F01;
  font-variant-numeric:tabular-nums;min-width:26px}

/* --- what kind of thing is waiting ---------------------------------------
   "Submitted" on a card says nothing a reader can act on: an appropriation,
   a lesson plan and a detailed schedule are different work, often for
   different people, and the list holds all three.

   The kind takes a hue of its own, and the card takes the same hue on its
   left edge -- so the list can be scanned by colour and read by word. The
   three hues are brand blues and a violet, deliberately clear of the
   green/amber/red that mean on-track, lagging and behind: a *kind* is not a
   *state*, and borrowing a status colour would say the appropriation is in
   trouble simply for being an appropriation. */
.kindtag{display:inline-flex;align-items:center;align-self:flex-start;
  padding:3px 9px;border-radius:var(--r-pill);font-size:10.5px;font-weight:700;
  letter-spacing:.05em;text-transform:uppercase;margin-bottom:2px}
.kindtag.ds{background:#E4EDFC;color:#103B8C}
.kindtag.lp{background:#E8E5FB;color:#4B3FA8}
.kindtag.appro{background:#DDF0F7;color:#12586E}
.plancard.kind-ds{border-left-color:#2563EB}
.plancard.kind-lp{border-left-color:#6D5AE0}
.plancard.kind-appro{border-left-color:#1B87A8}

/* --- one colour per kind of work, everywhere it appears -------------------
   Detailed schedule, lesson plan and appropriation each take a hue and keep
   it: on the Incharge's chips, on the cards in her list, on the section
   headings in the queue those chips link to, and on the Principal's
   breakdown. Press a blue chip, land on a blue heading.

   The three are a brand blue, a violet and a teal -- chosen to sit clear of
   the green/amber/red that mean on track, lagging and behind. A kind is not a
   state: an appropriation is not "in trouble" for being an appropriation, and
   a hue that says so would be lying in the one place a reader trusts colour.

   Defined once here as variables so the four places that use them cannot
   drift apart. */
:root{
  --kind-ds:#2563EB;    --kind-ds-bg:#E4EDFC;    --kind-ds-ink:#103B8C;
  --kind-lp:#6D5AE0;    --kind-lp-bg:#E8E5FB;    --kind-lp-ink:#4B3FA8;
  --kind-appro:#1B87A8; --kind-appro-bg:#DDF0F7; --kind-appro-ink:#12586E;
}

/* The chips on the desk: now links, so they say so on hover and focus. */
.pchip{text-decoration:none;transition:background-color .18s ease,box-shadow .18s ease,transform .18s ease}
a.pchip:hover{box-shadow:var(--sh-2);transform:translateY(-1px)}
.pchip.ds{background:var(--kind-ds-bg);color:var(--kind-ds-ink);
  border:1px solid rgba(37,99,235,.22)}
.pchip.lp{background:var(--kind-lp-bg);color:var(--kind-lp-ink);
  border:1px solid rgba(109,90,224,.24)}
.pchip.appro{background:var(--kind-appro-bg);color:var(--kind-appro-ink);
  border:1px solid rgba(27,135,168,.26)}

/* The queue's section headings, in the same three hues. */
.kindhead{display:flex;align-items:baseline;gap:9px;padding:8px 0 8px 13px;
  border-left:4px solid var(--rule);border-radius:0 8px 8px 0;
  scroll-margin-top:96px}          /* the sticky header must not cover it */
.kindhead.ds{border-left-color:var(--kind-ds);color:var(--kind-ds-ink);
  background:linear-gradient(90deg,var(--kind-ds-bg),transparent 62%)}
.kindhead.lp{border-left-color:var(--kind-lp);color:var(--kind-lp-ink);
  background:linear-gradient(90deg,var(--kind-lp-bg),transparent 62%)}
.kindhead.appro{border-left-color:var(--kind-appro);color:var(--kind-appro-ink);
  background:linear-gradient(90deg,var(--kind-appro-bg),transparent 62%)}

/* The badges and card edges on her list, from the same variables. */
.kindtag.ds{background:var(--kind-ds-bg);color:var(--kind-ds-ink)}
.kindtag.lp{background:var(--kind-lp-bg);color:var(--kind-lp-ink)}
.kindtag.appro{background:var(--kind-appro-bg);color:var(--kind-appro-ink)}
.plancard.kind-ds{border-left-color:var(--kind-ds)}
.plancard.kind-lp{border-left-color:var(--kind-lp)}
.plancard.kind-appro{border-left-color:var(--kind-appro)}

/* The Principal's breakdown was amber -- a status colour, saying "lagging"
   about a count that is simply a count. Each line now wears its own kind. */
.wkinds b{color:var(--ink)}
.wkinds li.ds b{color:var(--kind-ds-ink)}
.wkinds li.lp b{color:var(--kind-lp-ink)}
.wkinds li.appro b{color:var(--kind-appro-ink)}
.wkinds li i{width:9px;height:9px;border-radius:3px;flex:none}
.wkinds li.ds i{background:var(--kind-ds)}
.wkinds li.lp i{background:var(--kind-lp)}
.wkinds li.appro i{background:var(--kind-appro)}

/* --- the Incharge's rail, with no work column beside it -------------------
   The "Submitted — awaiting your approval" list used to fill the left of a
   two-column cockpit here. It listed the same items, in the same order, as
   the queue it linked to -- without the approve or return buttons that make
   the queue worth opening. The chips in "Waiting on your decision" already
   say what is waiting and land on the section holding it.

   With the work column gone, a two-column grid holding one column would leave
   the cards in a narrow strip against a wide empty page, so the rail lays out
   across the width instead. */
.railwide{margin-top:4px}
.railwide .rail{position:static;display:grid;
  grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
  gap:16px;align-items:start}
@media (max-width:700px){
  .railwide .rail{grid-template-columns:1fr}
}

/* --- "returned by the Principal" as a fold -------------------------------
   Sixteen returns made this card ten times the height of the two beside it.
   Folded, it is the size of its neighbours; the count in the summary is the
   alert, and she does not need sixteen comments to know sixteen came back.
   Open, the list scrolls inside a bounded box rather than growing the page. */
.qcard.preturns{padding:0;overflow:hidden}
.qcard.preturns>summary{display:flex;align-items:center;gap:9px;flex-wrap:wrap;
  padding:18px 20px;cursor:pointer;list-style:none;
  transition:background-color .18s ease}
.qcard.preturns>summary::-webkit-details-marker{display:none}
.qcard.preturns>summary::marker{content:""}
.qcard.preturns>summary:hover{background:#FFF6E5}
.qcard.preturns>summary .h{font-size:12.5px;font-weight:800;letter-spacing:.05em;
  text-transform:uppercase;color:#7A4F01}
.qcard.preturns>summary .hint{font-size:12px;color:var(--soft);flex:1 1 100%}
/* The chevron, matching the other folds in this stylesheet. */
.qcard.preturns>summary::after{content:"";margin-left:auto;width:8px;height:8px;
  border-right:2px solid #B0894A;border-bottom:2px solid #B0894A;
  transform:rotate(45deg) translate(-2px,-2px);transition:transform .18s ease}
.qcard.preturns[open]>summary::after{transform:rotate(225deg) translate(-2px,-2px)}
.qcard.preturns[open]>summary{border-bottom:1px solid #F0D9A8}
.qcard.preturns .pr-body{padding:14px 20px 18px}
.qcard.preturns .pr-body .none{margin:0}
@media print{.qcard.preturns .pr-body{display:block}}

/* --- the Incharge's rail: three cards, one shape -------------------------
   They held different amounts of text and so came out three different
   heights, which reads as three unrelated boxes rather than one row.

   A min-height rather than `align-items:stretch`: stretch would equalise to
   the tallest, so opening the returns fold to 524px would drag two mostly
   empty cards down with it. A floor makes them match while they are all
   short, and lets the one that grows grow alone. */
.railwide .rail{grid-template-columns:repeat(auto-fit,minmax(320px,1fr));
  align-items:start}
.railwide .rail>*{min-height:158px;display:flex;flex-direction:column}
/* The action sits on one baseline across the row, whatever the text above it
   runs to -- three buttons at three heights is the thing the eye catches. */
.railwide .rail>.qcard>.btn,
.railwide .rail>.qcard>a.btn{margin-top:auto;align-self:flex-start}
/* The folded panel has no body to push, so its summary takes the height. */
.railwide .qcard.preturns>summary{flex:1;align-content:center}
@media (max-width:700px){
  .railwide .rail>*{min-height:0}
}

/* --- tasks, in the two directions they run -------------------------------
   Work to do on the left, work to chase on the right. One list plus a
   "handed back" footnote hid everything a person had asked of somebody else,
   which is half of what a Level Incharge's day actually is. */
/* Two equal halves with a rule between them. `minmax(0,1fr)` rather than a
   bare `1fr`: a grid track's floor is its content, so one long task title was
   enough to make the left column wider than the right and the divider sit off
   centre. The gap is on the sections, not the grid, so the rule lands exactly
   halfway. */
.mytasks .taskcols{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:0;margin-top:6px}
.mytasks .taskcols>section{min-width:0;padding:0 26px}
.mytasks .taskcols>section:first-child{padding-left:0}
.mytasks .taskcols>section:last-child{padding-right:0;
  border-left:1px solid var(--rule)}
.mytasks h3{font-size:12px;font-weight:800;text-transform:uppercase;
  letter-spacing:.05em;color:var(--soft);display:flex;align-items:center;
  gap:8px;margin:0 0 10px}
.mytasks h3 .n{font-size:14px;font-weight:800;color:var(--deep);
  font-variant-numeric:tabular-nums}
/* The title is the thing being read; the meta line under it is context.
   It was the same weight and colour as everything else on the card. */
.mytasks .tasklist>li>a{color:var(--deep);font-weight:700;font-size:13.5px;
  text-decoration:none;line-height:1.35}
.mytasks .tasklist>li>a:hover,.mytasks .tasklist>li>a:focus-visible{
  color:var(--blue);text-decoration:underline}
/* A hairline between tasks, so a list of five reads as five rows. */
.mytasks .tasklist>li+li{border-top:1px solid #F1F5FA;padding-top:9px;margin-top:9px}
@media (max-width:900px){
  /* Stacked, the divider becomes a rule above the second section. */
  .mytasks .taskcols{grid-template-columns:1fr}
  .mytasks .taskcols>section{padding:0}
  .mytasks .taskcols>section:last-child{border-left:0;
    border-top:1px solid var(--rule);padding-top:18px;margin-top:18px}
}

/* --- the tasks card's own heading ----------------------------------------
   It was the only unmarked heading on the page: the heatmap levels, the queue
   sections and the returns panel all carry a tinted band, and this one sat as
   bare text above two columns. Same treatment as the others -- a wash of the
   brand hue with a solid accent edge -- so the page has one way of saying
   "this is a section", not two. */
.mytasks .toolbar{background:linear-gradient(90deg,#E8EFFC,rgba(232,239,252,.30) 72%,transparent);
  border-left:4px solid var(--blue);border-radius:0 12px 12px 0;
  padding:11px 16px;margin:-6px -6px 18px -6px;align-items:center}
.mytasks .toolbar h2{color:var(--deep);font-size:15px;font-weight:700;
  letter-spacing:-.01em;margin:0}

/* --- the tasks card inside the Principal's rail ---------------------------
   One column, because two in a 330px rail would be two narrow columns of
   nothing. The card chrome comes from the rail's own .qcard, so the inner
   card sheds its border, padding and heading band rather than drawing a
   second box inside the first. */
/* The OUTER .qcard keeps its chrome -- stripping both left the card with no
   white ground at all and the page pattern showing through the text. Only the
   inner .mytasks sheds its box, so there is one card, not two nested. */
.tasksrail{min-height:0}
.tasksrail .mytasks{border:0;box-shadow:none;background:none;padding:0;
  border-radius:0}
/* `.qcard h3` is space-between, so the count flew to the far edge of the rail
   away from the label it belongs to. Fourth rule caught by that inheritance. */
.tasksrail .mytasks h3{justify-content:flex-start}
/* The same band its neighbours in the rail wear. It was stripped when this
   card moved inside a .qcard, which left one heading in the column announcing
   itself differently from the three around it. */
.tasksrail .mytasks .toolbar{
  background:linear-gradient(90deg,#E8EFFC,rgba(232,239,252,.30) 72%,transparent);
  border-left:4px solid var(--blue);border-radius:0 10px 10px 0;
  padding:9px 13px;margin:-4px -4px 14px -4px}
.tasksrail .mytasks .toolbar h2{font-size:11.5px;font-weight:800;
  text-transform:uppercase;letter-spacing:.055em;color:var(--deep)}
/* One column: the only section is both :first-child and :last-child, so the
   divider rule still matched and drew a rule down the left of a card with
   nothing to its left. Needs to out-specify `.mytasks .taskcols>section:last-child`,
   not merely follow it. */
.mytasks .taskcols.one{grid-template-columns:1fr}
.mytasks .taskcols.one>section,
.mytasks .taskcols.one>section:last-child,
.mytasks .taskcols.one>section:first-child{padding:0;border-left:0;border-top:0}
.tasksrail .mytasks h3{margin-bottom:8px}
.alsomine{margin:12px 0 0;font-size:12px}
.alsomine a{color:var(--blue);text-decoration:none;font-weight:600}
.alsomine a:hover{text-decoration:underline}

/* --- the tasks card had no margin ----------------------------------------
   Measured against its neighbours: every other block on these dashboards sits
   16-20px from the next, and this one sat flush against both the rail above
   and the reference sections below. It was never noticed while the card was
   last on the page with nothing under it. */
.mytasks{margin:18px 0}

/* --- one heading treatment for every panel -------------------------------
   The heatmap levels, the queue sections and the tasks card carry a tinted
   band; the desk panels and rail cards were still bare uppercase text, so the
   page had two ways of saying "this is a section". This is the same band, on
   the rest of them.

   The count keeps its place at the far end of the band -- `.qcard h3` is a
   space-between row and that is right here: the label reads left, the number
   reads right, and the band ties them together. */
.dpanel>h3,.qcard>h3{
  background:linear-gradient(90deg,#E8EFFC,rgba(232,239,252,.30) 72%,transparent);
  border-left:4px solid var(--blue);border-radius:0 10px 10px 0;
  padding:9px 13px;margin:-4px -4px 14px -4px;
  font-size:11.5px;color:var(--deep);font-weight:800;letter-spacing:.055em}
.dpanel>h3 .n,.qcard>h3 .n{font-size:17px}

/* The amber cards keep their own hue: they are exceptions, and a blue band
   would file them with the routine ones. */
.qcard.waiting>h3{
  background:linear-gradient(90deg,#FDF0D9,rgba(253,240,217,.30) 72%,transparent);
  border-left-color:var(--blocked,#C79A4B);color:#7A4F01}
.qcard.waiting>h3 .n{color:#7A4F01;font-size:22px}

/* The folded returns panel is a <summary>, not an h3, so it takes the same
   band by hand. Its count is the alert, so it is the largest number on it. */
.qcard.preturns>summary{background:linear-gradient(90deg,#FDF0D9,rgba(253,240,217,.28) 74%,transparent);
  border-left:4px solid var(--blocked,#C79A4B)}
.qcard.preturns>summary .chip{font-size:15px;font-weight:800;padding:3px 12px}

/* The rail's task card sets its own quieter heading; keep it that way. */
.tasksrail .mytasks h3{background:none;border-left:0;padding:0;margin:0 0 8px;
  font-size:12px}

/* --- the returns panel's own heading and count ---------------------------
   Its band was the same pale amber as the card behind it, so it read as no
   band at all, and the count sat in a #FFF6E5 pill on a #FFFBF3 card -- 1.2:1,
   invisible. A solid pill instead: white on #7A4F01 is 7.1:1, and the number
   is the alert on this card, so it should be the loudest thing on it. */
.qcard.preturns>summary{background:linear-gradient(90deg,#F7E4BC,rgba(247,228,188,.35) 74%,transparent)}
.qcard.preturns>summary .chip{background:#7A4F01;color:#fff;border:0;
  font-size:15px;font-weight:800;padding:2px 12px;border-radius:var(--r-pill)}

/* The kind badge on the teacher's own plan card. `.pc-id` is a flex column,
   so the badge needs to stop stretching to the column's width. */
.pc-id .kindtag{align-self:flex-start;margin-bottom:3px}

/* --- "open Review first" --------------------------------------------------
   The approve and return buttons live behind the peek now. A card with the
   buttons simply gone reads as one with nothing to do, so the line that
   replaces them says where they went. */
.reviewfirst{margin:12px 0 0;font-size:12.5px;color:var(--soft)}
.reviewfirst b{color:var(--deep);font-weight:600}

/* The tick selections in the Incharge's lesson-plan peek. Small, wrapping
   pills: five groups can total a dozen options on one period, and a comma
   list of twelve reads as prose rather than as choices. */
.lppeek .tickpill{display:inline-block;background:var(--kind-lp-bg);
  color:var(--kind-lp-ink);border-radius:var(--r-pill);padding:2px 8px;
  font-size:11px;font-weight:600;margin:0 4px 4px 0;white-space:nowrap}
.lppeek td{vertical-align:top}

/* --- the Incharge's full LP-04 review -------------------------------------
   The same five-column tick layout the teacher filled in, so comparing the
   two costs no translation. `.ticks` and `.tickgroup` are the teacher
   editor's own classes, reused rather than reimplemented. */
.lpfull{display:flex;flex-direction:column;gap:14px;margin-top:12px}
.lpperiod-review{border:1px solid var(--rule);border-radius:var(--r-inner);
  padding:14px 16px;background:#FCFDFF}
.lpr-head{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;
  margin-bottom:6px}
.lpr-n{font-size:11px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;
  color:var(--kind-lp-ink);background:var(--kind-lp-bg);
  border-radius:var(--r-pill);padding:2px 9px}
.lpr-sub{font-weight:700;color:var(--deep);font-size:13.5px}
.lpr-date{margin-left:auto;font-size:11.5px;color:var(--soft)}
.lpr-out{margin:0 0 10px;font-size:12.5px;color:var(--ink);line-height:1.5}
.lpr-out .l{display:block;font-size:10.5px;text-transform:uppercase;
  letter-spacing:.05em;color:var(--soft);font-weight:600;margin-bottom:2px}
.licticks-foot{display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  margin-top:10px}
.licticks-foot .tmeta{font-size:11.5px;color:var(--soft)}

/* --- what the Incharge changed, shown to the Principal --------------------
   She may amend the delivery ticks while the plan is hers. He approves after
   her, so the amendment has to be visible at his gate: a signature covering a
   change nobody showed him is the failure this prevents. Amber, because it is
   something to read before deciding, not something wrong. */
.licamended{background:#FFFBF3;border:1px solid #F0D9A8;border-radius:var(--r-inner);
  padding:11px 14px;margin-top:12px;font-size:12.5px;color:#5A4108}
.licamended b{color:#7A4F01}
.licamended ul{list-style:none;padding:0;margin:6px 0 0;display:flex;
  flex-direction:column;gap:5px}
.licamended li{background:none;border:0;padding:0;display:block}
.licamended .tmeta{display:block;color:var(--soft);font-size:11px}

/* The Principal reads the ticks, he does not set them. Unticked options fade
   so the chosen ones carry the eye. */
.ticks.readonly .tick{cursor:default}
.ticks.readonly .tick.off{opacity:.45}
.ticks.readonly input{pointer-events:none}

/* One line on the plan card when the Incharge has amended a lesson plan under
   it. Same amber as the full notice on the chapter page, so the teacher meets
   one visual idea for "somebody changed your plan", not two. */
.plancard .amended{font-size:12.5px;color:#5A4108;background:#FFFBF3;
  border:1px solid #F0D9A8;border-radius:8px;padding:7px 11px;margin:8px 0 0;
  line-height:1.5}
.plancard .amended .tmeta{display:block;color:var(--soft);font-size:11px}

/* The Principal's breakdown lines are links now. They keep the kind's colour
   on the count and read as ordinary text until hovered -- a row of four
   underlined links in a card is louder than the number it is reporting. */
.wkinds li a{color:inherit;text-decoration:none;display:inline}
.wkinds li a:hover,.wkinds li a:focus-visible{text-decoration:underline}
.wkinds li.ds a b{color:var(--kind-ds-ink)}
.wkinds li.lp a b{color:var(--kind-lp-ink)}
.wkinds li.appro a b{color:var(--kind-appro-ink)}

/* --- collapsible queue sections -------------------------------------------
   Ninety-three items in three sections: once a Principal has cleared the
   appropriations she should be able to fold them away and see the schedules
   without scrolling past sixty-nine cards.

   Open by default. Collapsing is for skipping a section you have dealt with,
   and nothing should start hidden from somebody who has not asked -- least of
   all on the page that tells her what is waiting. */
.qsection{margin-bottom:8px}
.qsection>summary{display:flex;align-items:center;gap:12px;cursor:pointer;
  list-style:none;padding:0;margin-bottom:10px}
.qsection>summary::-webkit-details-marker{display:none}
.qsection>summary::marker{content:""}
.qsection>summary .kindhead{flex:1 1 auto;margin:0}
.qsec-n{font-size:15px;font-weight:800;color:var(--deep);
  font-variant-numeric:tabular-nums}
/* A bare chevron at the end of a wide heading is easy to miss, and the whole
   row is the control. So: the row looks pressable, and the control says what
   it will do in a word rather than in a symbol. */
.qsection>summary{border:1px solid transparent;border-radius:10px;
  transition:background-color .18s ease,border-color .18s ease}
.qsection>summary:hover{background:#F3F7FE;border-color:var(--rule)}
.qsection>summary:hover .qsec-act{background:#1D4FD8;box-shadow:var(--sh-2);
  transform:translateY(-1px)}
/* Folded is the state worth noticing -- there is content out of sight. The
   control goes amber so a closed section cannot be mistaken for an empty one. */
.qsection:not([open])>summary .qsec-act{background:#8C6009}
.qsection:not([open])>summary:hover .qsec-act{background:#7A4F01}
.qsection:not([open])>summary{background:#FFFBF3;border-color:#F0D9A8}
/* A filled control, not an outline. At outline weight it still read as
   decoration on a wide heading; solid, it reads as the button it is. */
.qsec-act{flex:none;display:inline-flex;align-items:center;gap:7px;
  padding:7px 15px;border:0;border-radius:var(--r-pill);
  background:var(--blue);color:#fff;font-size:12px;font-weight:700;
  letter-spacing:.03em;box-shadow:var(--sh-1);
  transition:background-color .18s ease,transform .18s ease,box-shadow .18s ease}
.qsec-act::before{content:"Hide"}
.qsection:not([open])>summary .qsec-act::before{content:"Show"}
.qsec-act::after{content:"";width:7px;height:7px;
  border-right:2px solid currentColor;border-bottom:2px solid currentColor;
  transform:rotate(45deg) translateY(-2px);transition:transform .18s ease}
.qsection[open]>summary .qsec-act::after{transform:rotate(225deg) translateY(-1px)}
/* The anchor lives on the heading inside the summary, so a deep link from the
   dashboard still lands on a visible target even when the section is folded. */
.qsection>summary .kindhead{scroll-margin-top:96px}

/* --- queue section headings: final alignment ------------------------------
   Four rows, four different shapes: two carried a coloured band and pushed
   their control to the end, two were bare headings that let the control sit
   wherever the text stopped. The eye reads a column of buttons; it cannot
   read a column that is not one. */
.qsection>summary>h2{flex:1 1 auto;margin:0;min-width:0}
.kindhead small{font-weight:400;font-size:11.5px;letter-spacing:normal}
/* Not a kind of work but a stage of it, so the same band in the page's own
   navy rather than one of the three kind hues. */
.kindhead.stage{border-left-color:var(--deep);color:var(--deep);
  background:linear-gradient(90deg,#EAEFF8,rgba(234,239,248,.30) 70%,transparent)}
/* The counts line up as a column, whatever the label above them runs to. */
.qsec-n{min-width:34px;text-align:right}
.qsection>summary{padding:4px 10px 4px 0}
.qsection+.qsection{margin-top:10px}

/* --- queue sections: the fold state stops shouting -------------------------
   The folded state washed the whole row cream, which put an amber field over
   the very band that says which of the three kinds of work the row holds --
   Basic Appropriation's teal read as a rumour under it. The fold is a state of
   the *control*, not of the work, so the control carries it: a filled button
   and a warm rule under the row. The kind colour stays the kind colour. */
.qsection:not([open])>summary{background:var(--card,#fff);border-color:var(--rule);
  box-shadow:inset 0 -2px 0 rgba(176,121,12,.34)}
.qsection:not([open])>summary:hover{background:#FCFAF5;border-color:#E8D3A6}
.qsection[open]>summary{background:transparent;box-shadow:none}
/* Open reads as the quieter state, so the eye lands on what is still folded. */
.qsection[open]>summary .qsec-act{background:transparent;color:var(--blue);
  box-shadow:inset 0 0 0 1.5px var(--blue)}
.qsection[open]>summary:hover .qsec-act{background:var(--blue);color:#fff;
  box-shadow:var(--sh-2)}
.qsec-n{min-width:38px;padding:3px 0;font-variant-numeric:tabular-nums}
.qsection>summary{padding:5px 12px 5px 0;transition:background .14s ease,
  border-color .14s ease,box-shadow .14s ease}
@media (prefers-reduced-motion:reduce){.qsection>summary{transition:none}}
/* "Show" and "Hide" are not the same width, so the button moved 4px every time
   it was pressed -- a row of controls that will not hold still. */
.qsec-act{min-width:84px;justify-content:center}

/* An empty state must not wear the colour of the problem it does not have.
   "Nothing has been returned to you" is good news, and a pink card saying it
   teaches the teacher to read pink as decoration -- so when something IS
   returned, the colour has already stopped meaning anything. Empty = a plain
   card; the tint is earned only by an actual return. */
.qcard.returned.empty{background:#fff;border-color:var(--rule)}
.qcard.returned.empty li{background:#fff;border-color:var(--rule)}
