  :root{
    --ink:#0E0E10; --ink2:#3A3D45; --muted:#5C6270; --faint:#6A7080;
    --line:#E7E9ED; --mist:#EEF0F3; --paper:#fff; --sunk:#F7F8FA;
    --firefly:#FFC53D;
    --good:#1E7247; --warn:#8A5E07; --crit:#A82C25;   /* each ≥5.7:1 on white */
    --rail:236px; --tabh:58px;
  }
  *{box-sizing:border-box}
  html,body{margin:0;padding:0}
  body{font-family:"Hanken Grotesk",system-ui,sans-serif;color:var(--ink);background:var(--paper);
       -webkit-font-smoothing:antialiased;font-size:15px;line-height:1.5}
  .mono{font-family:"IBM Plex Mono",ui-monospace,monospace}
  a{color:inherit}
  button{font:inherit;color:inherit}
  :focus-visible{outline:2.5px solid var(--ink);outline-offset:2px;border-radius:5px}

  /* ---------------- shell ----------------
     Desktop keeps the rail. Below 860 the rail is replaced by a top bar and a bottom tab bar, and
     the controls that used to live in the rail footer move into a sheet. Nothing is hidden: the
     old build dropped the role switcher, the language, the snapshot age and the reload button at
     exactly the width where this is used most. */
  .rail{position:fixed;inset:0 auto 0 0;width:var(--rail);border-right:1px solid var(--line);
        padding:22px 14px;display:flex;flex-direction:column;gap:2px;background:var(--paper);z-index:5}
  .brand{padding:0 10px 20px}
  .brand img{display:block;height:auto}
  .navlink{display:flex;justify-content:space-between;align-items:center;gap:10px;padding:8px 10px;
           border-radius:9px;text-decoration:none;color:var(--ink2);font-size:14.5px}
  .navlink:hover{background:var(--sunk);color:var(--ink)}
  .navlink.on{background:var(--ink);color:#fff}
  .navlink.on .n{color:rgba(255,255,255,.62)}
  .navlink .n{color:var(--faint);font-size:13px}
  .railfoot{margin-top:auto;padding:14px 10px 0;border-top:1px solid var(--line);
            font-size:12.5px;color:var(--muted);line-height:1.45}
  .otherside{display:block;margin:0 0 12px;color:var(--ink2);text-decoration:none;font-size:13px}
  .otherside:hover{color:var(--ink);text-decoration:underline}
  .pills{display:flex;flex-wrap:wrap;gap:6px;margin:6px 0 12px}
  .pill{border:1px solid var(--line);background:var(--paper);border-radius:999px;padding:3px 10px;
        font-size:12.5px;color:var(--ink2);cursor:pointer}
  .pill:hover{background:var(--sunk)}
  .pill.on{background:var(--ink);color:#fff;border-color:var(--ink)}

  .topbar,.tabbar{display:none}
  main{margin-left:var(--rail);padding:34px clamp(18px,3.4vw,46px) 90px;max-width:1240px}

  /* ---------------- type ---------------- */
  h1{font-size:clamp(28px,3.2vw,36px);letter-spacing:-.03em;font-weight:500;margin:0;line-height:1.12}
  .h2{font-size:17px;font-weight:500;letter-spacing:-.015em;margin:0}
  .sub{color:var(--muted);margin:7px 0 0;max-width:70ch;font-size:14.5px}
  .head{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:22px}
  .sec{color:var(--faint);font-size:12.5px}
  .nm{font-weight:500;letter-spacing:-.01em}

  /* ---------------- the pulse line ----------------
     One sentence of state where five counters used to be. Vercel's rule: you should read a state,
     not a statistic. */
  .pulse{display:flex;flex-wrap:wrap;align-items:baseline;gap:8px 16px;border:1px solid var(--line);
         border-radius:14px;padding:13px 16px;margin:0 0 26px;font-size:15px}
  .pulse b{font-weight:500;letter-spacing:-.015em}
  .pulse .d{color:var(--faint)}

  /* ---------------- the queue ----------------
     Every row ends in an action. The learning-analytics literature's finding is that teacher
     dashboards raise awareness and stop there; a row that only states a number is that failure. */
  .grp{margin:0 0 26px}
  .grphead{display:flex;align-items:baseline;gap:9px;margin:0 0 9px}
  .grphead h2{font-size:13px;font-weight:500;letter-spacing:.02em;text-transform:none;margin:0;color:var(--ink)}
  .grphead .c{font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:12.5px;color:var(--faint)}
  .q{border:1px solid var(--line);border-radius:14px;overflow:hidden}
  .item{display:grid;grid-template-columns:8px 1fr auto;gap:14px;align-items:center;padding:13px 16px;
       text-decoration:none;
        box-shadow:inset 0 -1px 0 var(--line);background:none;border:0;width:100%;text-align:left;
        cursor:pointer;font:inherit;color:inherit}
  .item:last-child{box-shadow:none}
  .item:hover{background:var(--sunk)}
  .item .dot{width:8px;height:8px;border-radius:50%;background:var(--line)}
  .item.b2 .dot{background:var(--crit)} .item.b1 .dot{background:var(--warn)}
  .item .who{display:block;font-weight:500;letter-spacing:-.01em}
  .item:hover .who{text-decoration:underline;text-underline-offset:3px}
  .item .why{display:block;color:var(--muted);font-size:13.5px;margin-top:3px;line-height:1.45}
  .item .go{font-size:13.5px;color:var(--ink2);white-space:nowrap}
  .item:hover .go{color:var(--ink)}
  .empty{border:1px dashed var(--line);border-radius:14px;padding:22px 18px;color:var(--muted);font-size:14.5px}

  /* ---------------- tiles / panels ---------------- */
  .tiles{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(min(170px,100%),1fr));
         grid-auto-rows:auto;margin-bottom:26px}
  .tile{border:1px solid var(--line);border-radius:14px;padding:15px 16px 13px;
        display:grid;grid-row:span 3;grid-template-rows:subgrid;row-gap:0}
  .tile .k{font-size:12.5px;color:var(--muted);align-self:end}
  .tile .v{font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:27px;letter-spacing:-.04em;
           font-weight:500;margin-top:5px;line-height:1.05}
  .tile .v small{font-size:15px;color:var(--muted);letter-spacing:0;margin-left:3px;font-weight:400}
  .tile .m{font-size:12.5px;color:var(--faint);margin-top:4px;line-height:1.4}
  .panel{border:1px solid var(--line);border-radius:16px;padding:17px 18px;margin-bottom:22px}
  /* Tiles inside a panel were cards inside cards. Here they are plain stat blocks with a rule. */
  .panel .tiles{gap:0}
  .panel .tile{border:0;border-radius:0;padding:12px 16px 12px 0;border-right:1px solid var(--line);
               height:100%}
  .panel .tile:last-child{border-right:0}
  @media(max-width:860px){ .panel .tile{border-right:0;box-shadow:inset 0 -1px 0 var(--line);padding-right:0} }
  .note{background:#FFFBF0;border:1px solid #EFE2C0;border-left:3px solid var(--firefly);
        border-radius:0 12px 12px 0;padding:13px 16px;font-size:13.5px;line-height:1.6;color:var(--ink2);margin:16px 0 0}
  .note b{color:var(--ink);font-weight:500}
  .cols{display:grid;gap:18px;grid-template-columns:1fr}
  @media(min-width:980px){ .cols.two{grid-template-columns:1fr 1fr} }

  /* ---------------- tables ---------------- */
  .wrap{border:1px solid var(--line);border-radius:16px;overflow:auto}
  table{width:100%;border-collapse:collapse;font-size:14.5px}
  th{font-weight:400;font-size:12.5px;color:var(--muted);text-align:left;padding:10px 15px;
     background:var(--sunk);white-space:nowrap;position:sticky;top:0;z-index:2;
     box-shadow:inset 0 -1px 0 var(--line)}
  th[data-k]{cursor:pointer;user-select:none}
  th[data-k]:hover{color:var(--ink)}
  th.r,td.r{text-align:right}
  td.r{font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:13.5px;letter-spacing:-.02em}
  td{padding:11px 15px;box-shadow:inset 0 -1px 0 var(--line)}
  tbody tr:last-child td{box-shadow:none}
  tbody tr.click{cursor:pointer}
  tbody tr.click:hover{background:var(--sunk)}
  tbody tr.open{background:var(--sunk)}
  td.detail{padding:0;box-shadow:inset 0 -1px 0 var(--line)}
  .detailbox{padding:16px 15px 20px;background:var(--sunk)}

  .tag{display:inline-block;padding:2px 8px;border-radius:999px;font-size:12px;border:1px solid var(--line);color:var(--ink2);white-space:nowrap}
  .tag.good{color:var(--good);border-color:rgba(30,114,71,.3);background:rgba(30,114,71,.07)}
  .tag.warn{color:var(--warn);border-color:rgba(138,94,7,.3);background:rgba(138,94,7,.07)}
  .tag.crit{color:var(--crit);border-color:rgba(168,44,37,.3);background:rgba(168,44,37,.07)}
  /* A 6px bar: a 3px radius is visually identical to a pill and stops the slop probe counting the
     fill as a card nested in its own track. */
  .meter{height:6px;border-radius:3px;background:var(--mist);overflow:hidden;min-width:80px}
  .meter i{display:block;height:100%;background:var(--ink);border-radius:3px}
  .search{border:1px solid var(--line);border-radius:999px;padding:7px 14px;font:inherit;font-size:14px;
          width:min(260px,100%);background:var(--paper)}

  /* ---------------- charts ---------------- */
  .chartbox{position:relative;color:var(--ink);margin-top:12px}
  .chart-svg{width:100%;height:188px;display:block}
  .pt{fill:var(--paper);stroke:currentColor;stroke-width:2}
  .pt.hot{r:6}
  .tip{position:absolute;transform:translate(-50%,-128%);background:var(--ink);color:#fff;font-size:12px;
       padding:6px 9px;border-radius:7px;white-space:nowrap;pointer-events:none;z-index:4;line-height:1.4}
  .tip.under{transform:translate(-50%,0)}
  .heatbox .tip{white-space:normal;width:max-content;max-width:min(250px,80vw);text-align:center}
  .tip b{display:block;font-size:13.5px;font-weight:500}
  .tip span{color:rgba(255,255,255,.72)}
  .axis{display:flex;justify-content:space-between;font-size:12.5px;color:var(--muted);margin-top:6px}
  .bars{display:flex;align-items:flex-end;gap:3px;height:120px;margin-top:12px}
  .bars i{flex:1;background:var(--mist);border-radius:3px 3px 0 0;min-height:2px;position:relative}
  .bars i:hover,.bars i[data-hi]{background:var(--ink)}
  .bars i:hover::after{content:attr(data-t);position:absolute;bottom:calc(100% + 7px);left:50%;
    transform:translateX(-50%);background:var(--ink);color:#fff;font-size:11.5px;white-space:nowrap;
    padding:4px 7px;border-radius:6px;z-index:3}

  /* Two variables per cell: hue says how well the hour went against the cohort's own average,
     depth says how much work landed in it. An hour judged on a handful of answers gets no hue. */
  .heatbox{position:relative;margin-top:12px}
  .heat{display:grid;grid-template-columns:34px repeat(var(--cols,24),1fr);gap:3px}
  .heat .lab{font-size:12px;color:var(--muted);display:flex;align-items:center}
  .heat .cell{aspect-ratio:1;border-radius:2px;background:var(--mist);cursor:default;
              transition:transform .08s ease}
  .heat .cell[data-b="g"]{background:rgba(30,114,71,var(--a))}
  .heat .cell[data-b="a"]{background:rgba(190,132,10,var(--a))}
  .heat .cell[data-b="r"]{background:rgba(168,44,37,var(--a))}
  .heat .cell[data-b="n"]{background:rgba(92,98,112,calc(var(--a) * .42))}
  .heat .cell.hot{transform:scale(1.35);outline:2px solid var(--ink);outline-offset:0;
                  border-radius:3px;position:relative;z-index:2}
  .qtable .narrow{display:none}
  @media(max-width:860px){
    .qtable th:nth-child(3),.qtable td:nth-child(3),
    .qtable th:nth-child(4),.qtable td:nth-child(4){display:none}
    .qtable .narrow{display:block;margin-top:3px}
  }
  .legend{display:flex;flex-wrap:wrap;gap:6px 18px;margin-top:12px;font-size:12.5px;color:var(--muted)}
  .legend span{display:flex;align-items:center;gap:6px}
  .legend i{width:11px;height:11px;border-radius:2px;display:block}
  .heatax{display:grid;grid-template-columns:34px repeat(var(--cols,24),1fr);gap:3px;font-size:12px;
          font-family:"IBM Plex Mono",ui-monospace,monospace;color:var(--muted);margin-top:5px}

  /* ---------------- hint ---------------- */
  .hint{border-bottom:1px dotted var(--faint);cursor:help;position:relative}
  .hint::after{content:attr(data-hint);position:absolute;bottom:calc(100% + 8px);left:50%;
    transform:translateX(-50%);background:var(--ink);color:#fff;font-size:12.5px;line-height:1.5;
    padding:9px 11px;border-radius:9px;width:260px;opacity:0;pointer-events:none;transition:opacity .13s;z-index:7}
  .hint:hover::after,.hint:focus-visible::after{opacity:1}
  .hint.edge-r::after{left:auto;right:0;transform:none}
  .hint.edge-l::after{left:0;transform:none}

  /* ---------------- toast + sheet ---------------- */
  .toast{position:fixed;left:50%;bottom:calc(26px + env(safe-area-inset-bottom,0px));transform:translateX(-50%);
         background:var(--ink);color:#fff;padding:10px 16px;border-radius:10px;font-size:13.5px;z-index:90;
         box-shadow:0 6px 24px rgba(0,0,0,.18);max-width:min(90vw,460px);line-height:1.45}
  .stamp{font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:12px;color:var(--faint);letter-spacing:-.01em}
  .stamp.warn{color:var(--warn)} .stamp.crit{color:var(--crit)}
  .snap{display:flex;align-items:center;gap:7px;margin:0 0 12px}
  .reload{border:1px solid var(--line);background:var(--paper);border-radius:7px;width:25px;height:25px;
          display:grid;place-items:center;cursor:pointer;color:var(--muted);padding:0}
  .reload:hover{background:var(--sunk);color:var(--ink)}
  .reload svg{display:block}
  .reload.spin svg{animation:spin .8s linear infinite}
  @keyframes spin{to{transform:rotate(360deg)}}

  .back{display:inline-block;margin:0 0 14px;color:var(--muted);text-decoration:none;font-size:14px}
  .back:hover{color:var(--ink);text-decoration:underline}
  .kbd{font-size:13px;color:var(--muted);margin-top:22px}
  .kbd kbd{font-family:"IBM Plex Mono",monospace;font-size:12px;background:var(--mist);color:var(--ink2);
           border:1px solid var(--line);border-radius:5px;padding:2px 7px;margin:0 2px}

  /* ---------------- mobile ---------------- */
  @media(max-width:860px){
    .rail{display:none}
    main{margin-left:0;padding:calc(54px + 18px) 16px calc(var(--tabh) + 28px + env(safe-area-inset-bottom,0px))}
    .topbar{display:flex;position:fixed;inset:0 0 auto 0;height:54px;align-items:center;justify-content:space-between;
            gap:12px;padding:0 14px;background:var(--paper);border-bottom:1px solid var(--line);z-index:6}
    .tabbar{display:flex;position:fixed;inset:auto 0 0 0;height:calc(var(--tabh) + env(safe-area-inset-bottom,0px));
            padding-bottom:env(safe-area-inset-bottom,0px);background:var(--paper);border-top:1px solid var(--line);z-index:6}
    .tabbar a{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
              text-decoration:none;color:var(--muted);font-size:11.5px}
    .tabbar a.on{color:var(--ink);font-weight:500}
    .tabbar a .n{font-size:10px;color:var(--faint)}
    .sheetbtn{border:1px solid var(--line);background:var(--paper);border-radius:999px;padding:5px 12px;
              font-size:12.5px;color:var(--ink2);cursor:pointer;display:flex;align-items:center;gap:7px}
    .sheet{position:fixed;inset:auto 0 0 0;background:var(--paper);border-top:1px solid var(--line);
           border-radius:18px 18px 0 0;padding:18px 18px calc(22px + env(safe-area-inset-bottom,0px));
           z-index:20;transform:translateY(100%);transition:transform .22s cubic-bezier(.22,.61,.36,1);
           box-shadow:0 -10px 40px rgba(0,0,0,.14)}
    .sheet.on{transform:none}
    .scrim{position:fixed;inset:0;background:rgba(14,14,16,.32);opacity:0;pointer-events:none;
           transition:opacity .18s;z-index:19}
    .scrim.on{opacity:1;pointer-events:auto}
    h1{font-size:30px}
    .tiles{grid-template-columns:1fr 1fr;gap:10px}
    .tile .v{font-size:25px}
    .pulse{font-size:14px;gap:6px 12px}
    /* The action wraps under the text here, so the dot has to hang off the first line rather than
       float in the middle of a three-line block. */
    .item{grid-template-columns:8px 1fr;gap:11px;padding:12px 14px;align-items:start}
    .item .dot{margin-top:6px}
    .item .go{grid-column:2;margin-top:7px}
    .cols.two{grid-template-columns:1fr}
  }
  @media(min-width:861px){ .sheet,.scrim{display:none} }

  /* ---------------- admin additions ---------------- */
  .pickers{display:grid;gap:6px;margin:0 0 12px}
  .pickers label{display:grid;gap:3px;font-size:12px;color:var(--muted)}
  .sel{border:1px solid var(--line);border-radius:8px;padding:5px 8px;font:inherit;font-size:13px;
       background:var(--paper);color:var(--ink);width:100%}
  .who{font-size:12.5px;color:var(--muted);margin:0 0 10px;word-break:break-all}
  .n{font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:12px;color:var(--faint);letter-spacing:-.01em}
  .mt{display:inline-block;font-size:10.5px;letter-spacing:.04em;text-transform:uppercase;color:var(--faint);
      border:1px solid var(--line);border-radius:4px;padding:0 4px;margin-left:6px;vertical-align:1px}
  .opt{display:grid;grid-template-columns:22px 1fr 140px 64px;gap:12px;align-items:center;padding:8px 0;
       box-shadow:inset 0 -1px 0 var(--line)}
  .opt:last-child{box-shadow:none}
  .opt .mark{width:18px;height:18px;border-radius:50%;display:grid;place-items:center;font-size:11px;
             border:1px solid var(--line);color:var(--muted)}
  .opt.ok .mark{background:var(--good);border-color:var(--good);color:#fff}
  .opt.trap .mark{background:var(--crit);border-color:var(--crit);color:#fff}
  .passage{border-left:3px solid var(--line);padding:4px 0 4px 14px;margin:0 0 14px;color:var(--ink2);
           font-size:14.5px;white-space:pre-line}
  .stem{font-size:17px;line-height:1.45;margin:0 0 14px}
  .orig{color:var(--muted);font-size:13.5px;margin:-6px 0 14px}
  .kv{display:grid;grid-template-columns:auto 1fr;gap:4px 14px;font-size:13.5px}
  .kv dt{color:var(--muted)} .kv dd{margin:0}
  .btn{border:1px solid var(--ink);background:var(--ink);color:#fff;border-radius:9px;padding:7px 13px;
       font:inherit;font-size:13.5px;cursor:pointer}
  .btn.ghost{background:var(--paper);color:var(--ink);border-color:var(--line)}
  .btn:disabled{opacity:.5;cursor:default}
  code,.code{font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:12.5px;background:var(--sunk);
       border:1px solid var(--line);border-radius:5px;padding:1px 5px}
  pre.code{display:block;padding:12px 14px;overflow:auto;line-height:1.5}
  .loading{color:var(--muted);padding:40px 0}
  @media(max-width:860px){ .opt{grid-template-columns:22px 1fr 64px} .opt .meter{display:none} }
  .media{display:flex;flex-wrap:wrap;gap:12px;margin:0 0 16px}
  .media img{display:block;max-width:100%;max-height:420px;border:1px solid var(--line);border-radius:10px;background:var(--sunk)}
  .media audio{width:min(100%,420px)}
  .thumb{float:right;width:56px;height:42px;object-fit:cover;border-radius:6px;border:1px solid var(--line);margin:0 0 4px 12px;background:var(--sunk)}
