/* ============================================================
   Unison for Schools
   Tokens inherit the band app's palette; the semantic layer
   (wait / late / done) is deliberately separate from the brand.
   ============================================================ */
:root {
  --ground:#080B18; --surface:#101526; --raised:#19203A;
  --border:#242C48; --border-soft:#1B2138;
  --brand:#4ECDC6; --sky:#5EA9F0; --violet:#8B6BE8;
  --wait:#FFB74D; --late:#F0616B; --done:#63D19A;
  --on-brand:#04201E; --on-wait:#17120A; --on-late:#1A0A0C;
  --text:#EDF0F7; --text-2:#8D96B4; --text-3:#5A6280;
  --r:14px; --r-sm:9px;
  --ui:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;
  --mono:'SF Mono','Fira Code',ui-monospace,Menlo,monospace;
  --rail-w:236px;
  --shadow:0 24px 60px rgba(0,0,0,.55);
}
:root[data-theme="light"]{
  --ground:#F4F6FA; --surface:#EBEEF4; --raised:#FFFFFF;
  --border:#D3DAE7; --border-soft:#E2E7F0;
  --brand:#159C93; --sky:#2F7FD0; --violet:#6A4BCC;
  --wait:#B87415; --late:#CE3B47; --done:#2E9A66;
  --on-brand:#FFF; --on-wait:#FFF; --on-late:#FFF;
  --text:#101422; --text-2:#4C5468; --text-3:#78819A;
  --shadow:0 20px 50px rgba(23,32,58,.14);
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0; background:var(--ground); color:var(--text);
  font-family:var(--ui); font-size:15px; line-height:1.5;
  -webkit-font-smoothing:antialiased;
}
.hidden{display:none !important}
.num{font-family:var(--mono);font-variant-numeric:tabular-nums}
:where(button,a,input,textarea,select,[tabindex]):focus-visible{
  outline:2px solid var(--brand); outline-offset:2px; border-radius:5px;
}
@media (prefers-reduced-motion:reduce){*{transition:none !important;animation:none !important}}

/* ── boot ── */
.boot{position:fixed;inset:0;display:grid;place-items:center;background:var(--ground);z-index:90}
.boot-pulse{
  width:34px;height:34px;border-radius:50%;
  background:linear-gradient(to bottom,var(--violet),var(--sky),var(--brand));
  animation:bp 1.1s ease-in-out infinite;
}
@keyframes bp{0%,100%{transform:scale(.78);opacity:.5}50%{transform:scale(1);opacity:1}}

/* ── gate ── */
.gate{position:fixed;inset:0;display:grid;place-items:center;padding:24px;background:var(--ground);z-index:80;overflow:auto}
.gate-card{
  width:100%;max-width:392px;background:var(--surface);
  border:1px solid var(--border);border-radius:18px;padding:30px 28px;
  box-shadow:var(--shadow);display:flex;flex-direction:column;gap:14px;
}
/* The bare mark on a near-black card read as a floating smudge — its strokes
   sit only a step off the surface. The badge gives it an edge to sit against
   and a box to be aligned by, so the lockup balances against the wordmark
   instead of against the SVG's empty margins. */
.gate-mark{display:flex;align-items:center;gap:12px;margin-bottom:2px}
.gate-badge{
  flex:0 0 auto;display:grid;place-items:center;width:42px;height:42px;border-radius:13px;
  background:
    linear-gradient(150deg,color-mix(in srgb,var(--violet) 22%,transparent),
                           color-mix(in srgb,var(--brand) 16%,transparent)),
    var(--raised);
  border:1px solid color-mix(in srgb,var(--sky) 26%,var(--border));
  box-shadow:inset 0 1px 0 color-mix(in srgb,#fff 7%,transparent);
}
.gate-mark > div > b{display:block;font-size:17px;letter-spacing:-.012em;line-height:1.15;font-weight:640}
.gate-mark > div > span{display:block;font-size:10px;text-transform:uppercase;letter-spacing:.1em;
  color:var(--text-3);font-weight:600;margin-top:1px}
.gate-card h1{margin:0;font-size:25px;line-height:1.2;letter-spacing:-.024em;font-weight:640;text-wrap:balance}
.gate-card > p{margin:0;color:var(--text-2);font-size:14px}
.gate-foot{font-size:12.5px;color:var(--text-3) !important}
.gate-err{margin:0;font-size:13px;color:var(--late);background:color-mix(in srgb,var(--late) 12%,transparent);
  border:1px solid color-mix(in srgb,var(--late) 36%,transparent);border-radius:var(--r-sm);padding:9px 11px}
.gate-ok{margin:0;font-size:13px;color:var(--done);background:color-mix(in srgb,var(--done) 12%,transparent);
  border:1px solid color-mix(in srgb,var(--done) 36%,transparent);border-radius:var(--r-sm);padding:9px 11px}

/* The gate stacks with a 14px gap; the auth block wants to read as one unit,
   so its own spacing is tightened rather than fighting the parent. */
.gate-card .field{gap:5px}
.or{display:flex;align-items:center;gap:11px;color:var(--text-3);font-size:11px;
  text-transform:uppercase;letter-spacing:.09em;font-weight:600;margin:-2px 0}
.or::before,.or::after{content:"";height:1px;flex:1;background:var(--border)}
.gate-alt{margin:-4px 0 0;font-size:12.5px;color:var(--text-3);text-align:center}
.gate-card a{color:var(--brand);text-decoration:none;font-weight:560}
.gate-card a:hover{text-decoration:underline}
.lrow{display:flex;align-items:baseline;justify-content:space-between;gap:10px}
.lrow a{font-size:11px;text-transform:uppercase;letter-spacing:.07em;font-weight:600;
  display:inline-block;padding:10px 4px;margin:-10px -4px}
/* Links inside the app chrome are brand-coloured, not browser blue. */
.sheet a,.main a{color:var(--brand);text-decoration:none}
.sheet a:hover,.main a:hover{text-decoration:underline}

/* ── shell ── */
.app{display:grid;grid-template-columns:var(--rail-w) minmax(0,1fr);min-height:100vh}
.rail{
  background:var(--surface);border-right:1px solid var(--border);
  padding:16px 12px;display:flex;flex-direction:column;gap:17px;
  position:sticky;top:0;height:100vh;
}
.brandmark{display:flex;align-items:center;gap:9px;padding:2px 6px 0}
.brandmark b{display:block;font-size:15px;letter-spacing:-.01em;line-height:1.15;font-weight:640}
.brandmark span{font-size:10px;color:var(--text-3);text-transform:uppercase;letter-spacing:.08em;font-weight:600;
  display:block;max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

.ens-switch{
  background:var(--raised);border:1px solid var(--border);border-radius:var(--r-sm);
  padding:9px 11px;cursor:pointer;text-align:left;color:inherit;font:inherit;width:100%;
  display:flex;align-items:center;justify-content:space-between;gap:8px;
}
.ens-switch:hover{border-color:var(--text-3)}
.ens-switch .lbl{display:block;font-size:10px;text-transform:uppercase;letter-spacing:.07em;color:var(--text-3);font-weight:600}
.ens-switch .val{display:block;font-size:13.5px;font-weight:580;margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:150px}
.ens-switch .chev{color:var(--text-3);font-size:11px}

.navgroup{display:flex;flex-direction:column;gap:1px}
.navgroup .cap{font-size:10px;text-transform:uppercase;letter-spacing:.08em;color:var(--text-3);padding:0 10px 6px;font-weight:600}
.nav{
  display:flex;align-items:center;gap:9px;padding:7px 10px;border-radius:var(--r-sm);
  color:var(--text-2);font:inherit;font-size:13.5px;border:1px solid transparent;
  cursor:pointer;background:none;width:100%;text-align:left;
}
.nav:hover{background:var(--raised);color:var(--text)}
.nav.on{background:var(--raised);color:var(--text);border-color:var(--border);font-weight:570}
.nav .ico{width:15px;text-align:center;opacity:.75;flex:0 0 auto}
.nav .badge{margin-left:auto;font-family:var(--mono);font-size:10.5px;padding:1px 6px;border-radius:999px;
  background:var(--wait);color:var(--on-wait);font-weight:700}

.rail-foot{margin-top:auto;display:flex;align-items:center;gap:9px;padding:10px 8px;border-top:1px solid var(--border);
  background:none;border-left:0;border-right:0;border-bottom:0;color:inherit;font:inherit;
  width:100%;text-align:left;cursor:pointer;border-radius:0}
.rail-foot:hover{background:var(--raised)}
.rail-foot .chev{color:var(--text-3);font-size:11px;flex:0 0 auto}
.who-wrap{min-width:0;flex:1}
.avatar{width:28px;height:28px;border-radius:50%;background:var(--raised);border:1px solid var(--border);
  display:grid;place-items:center;font-size:11px;font-weight:640;color:var(--text-2);flex:0 0 auto;overflow:hidden}
.avatar img{width:100%;height:100%;object-fit:cover}
.avatar.lg{width:44px;height:44px;font-size:15px}
.rail-foot .who{font-size:12.5px;font-weight:560;line-height:1.25;display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rail-foot .role{font-size:10.5px;color:var(--text-3);text-transform:capitalize}
/* ── hit targets ──
   The rules below grow the area a thumb has to find without changing how big
   anything looks: min-height and padding, never a larger glyph, with negative
   margins where a row would otherwise grow around the new box. 44px is the
   platform guideline; these sit at 36–40, which is where a thumb stops missing
   and the dense director rows still hold their shape. */
.iconbtn{
  background:none;border:0;color:var(--text-3);cursor:pointer;font-size:14px;
  border-radius:6px;
  display:inline-flex;align-items:center;justify-content:center;
  min-width:36px;min-height:36px;padding:0 6px;
}
.iconbtn:hover{color:var(--text);background:var(--raised)}
/* The close button is 36px of target against a 25px heading, so the head
   would grow by a third. Pull the box back to the height it always had. */
.sheet-head .iconbtn{margin:-6px -7px -6px 0}

/* ── chevrons ──
   The ‹ and › glyphs sit low in their em box in the system UI stack, so
   centring the box still leaves the mark about 1.4px under the label beside
   it. These are vectors centred on their own viewBox, which puts the mark
   where the eye expects it whatever the surrounding font does. */
.chev{
  display:inline-block;vertical-align:middle;flex:none;
  width:.33em;height:.58em;
  fill:none;stroke:currentColor;stroke-width:2.2;
  stroke-linecap:round;stroke-linejoin:round;
}

/* ── main ── */
.main{padding:22px 26px 44px;display:flex;flex-direction:column;gap:18px;min-width:0}
.head{display:flex;align-items:flex-end;justify-content:space-between;gap:16px;flex-wrap:wrap;min-width:0}
.head h2{margin:0;font-size:22px;letter-spacing:-.022em;font-weight:640}
.head .sub{margin:3px 0 0;color:var(--text-2);font-size:13px}
.head .acts{display:flex;gap:8px;flex-wrap:wrap;min-width:0;max-width:100%}
/* An action's label is the one thing in this row that must never wrap: a
   director-named term long enough to squeeze it turned "New assignment" into
   two lines and doubled the height of the header. The filter beside it is the
   part that can afford to lose characters, so shrinking is pushed onto it. */
.head .acts>.btn{flex:0 0 auto;white-space:nowrap}

/* ── buttons ── */
.btn{
  font:inherit;font-size:13px;font-weight:570;padding:8px 14px;border-radius:var(--r-sm);
  border:1px solid var(--border);background:var(--raised);color:var(--text);cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;gap:7px;transition:filter .14s,opacity .14s;
  text-decoration:none;                 /* .btn is used on <a> in the landing page too */
}
.btn:hover:not(:disabled){filter:brightness(1.15)}
.btn:disabled{opacity:.5;cursor:not-allowed}
.btn.primary{background:var(--brand);border-color:var(--brand);color:var(--on-brand)}
.btn.danger{background:var(--late);border-color:var(--late);color:#fff}
.btn.danger-quiet{background:transparent;color:var(--late);
  border-color:color-mix(in srgb,var(--late) 40%,transparent)}
.btn.danger-quiet:hover:not(:disabled){background:color-mix(in srgb,var(--late) 12%,transparent);filter:none}
.btn.ghost{background:transparent}
.btn.sm{padding:5px 10px;font-size:12px;min-height:32px}
.btn.lg{padding:11px 16px;font-size:14.5px;width:100%}
.btn.full{width:100%}

/* ── panels / cards ── */
.panel{background:var(--surface);border:1px solid var(--border);border-radius:var(--r);overflow:hidden}
.panel-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 15px;border-bottom:1px solid var(--border)}
.panel-head h3{margin:0;font-size:13.5px;font-weight:610;letter-spacing:-.008em}
/* Secondary explanatory copy. Scoped variants below win where they apply;
   this is the bare form, for a hint that sits directly in a sheet. */
.hint{font-size:11.5px;color:var(--text-3);line-height:1.5}
.panel-head .hint{font-size:11.5px;color:var(--text-3)}
.panel-body{padding:14px 15px}

.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(176px,1fr));gap:12px}
.card{
  background:var(--surface);border:1px solid var(--border);border-radius:var(--r);
  padding:14px 15px;display:flex;flex-direction:column;gap:3px;position:relative;overflow:hidden;
  text-align:left;font:inherit;color:inherit;cursor:pointer;
}
.card::before{content:"";position:absolute;inset:0 auto 0 0;width:3px;background:var(--border)}
.card.wait::before{background:var(--wait)}
.card.late::before{background:var(--late)}
.card.done::before{background:var(--done)}
.card.info::before{background:var(--sky)}
.card .k{font-size:10.5px;text-transform:uppercase;letter-spacing:.075em;color:var(--text-3);font-weight:600}
.card .v{font-family:var(--mono);font-size:27px;font-weight:600;letter-spacing:-.02em;line-height:1.15}
.card .m{font-size:12px;color:var(--text-2)}
/* A stat that leads somewhere should look like it does — but quietly, so the
   dashboard still reads as numbers first. */
.card.act{transition:border-color .12s ease,background .12s ease}
.card.act:hover{background:var(--raised);border-color:color-mix(in srgb,var(--brand) 45%,var(--border))}
.card.act .cgo{color:var(--brand);font-weight:600;margin-left:5px;white-space:nowrap}
.card.act:hover .cgo{text-decoration:underline}

/* ── rows ── */
.rows{display:flex;flex-direction:column}
.row{
  display:flex;align-items:center;gap:12px;padding:10px 15px;
  border:0;border-bottom:1px solid var(--border-soft);
  background:none;color:inherit;font:inherit;width:100%;text-align:left;cursor:pointer;
}
.row:last-child{border-bottom:0}
.row:hover{background:var(--raised)}
.row.sel{background:var(--raised);box-shadow:inset 3px 0 0 var(--brand)}
.row .nm{font-size:13.5px;font-weight:560;line-height:1.3;display:block}
.row .meta{font-size:11.5px;color:var(--text-3);display:block}
.row .grow{flex:1;min-width:0}
.row .rt{margin-left:auto;display:flex;align-items:center;gap:9px;flex:0 0 auto;color:var(--text-3)}
.row.static{cursor:default}
.row.static:hover{background:none}
a.row{text-decoration:none;color:inherit}

.sechead{display:flex;align-items:center;gap:9px;padding:8px 15px;background:var(--raised);
  border-top:1px solid var(--border);border-bottom:1px solid var(--border)}
.sechead .nmm{font-size:12.5px;font-weight:620}

/* ── pills ── */
.pill{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:600;
  padding:2px 8px;border-radius:999px;border:1px solid;white-space:nowrap}
.pill.wait{color:var(--wait);border-color:color-mix(in srgb,var(--wait) 42%,transparent);background:color-mix(in srgb,var(--wait) 12%,transparent)}
.pill.late{color:var(--late);border-color:color-mix(in srgb,var(--late) 42%,transparent);background:color-mix(in srgb,var(--late) 12%,transparent)}
.pill.done{color:var(--done);border-color:color-mix(in srgb,var(--done) 42%,transparent);background:color-mix(in srgb,var(--done) 12%,transparent)}
.pill.sect{color:var(--sky);border-color:color-mix(in srgb,var(--sky) 40%,transparent);background:color-mix(in srgb,var(--sky) 12%,transparent)}
.pill.mut{color:var(--text-3);border-color:var(--border)}
/* the due-date glyph — ○ ◔ ◑ ◕ ● fill up as the deadline closes */
.pill .dic{font-size:9.5px;line-height:1;opacity:.95}

/* ── due date banner ──────────────────────────────────────────
   The recorder opens on this: the deadline is the first thing on
   the sheet, before the rubric and before the record button. */
.duebar{display:flex;align-items:center;gap:9px;padding:10px 12px;margin-bottom:10px;
  border:1px solid var(--border);border-radius:var(--r-sm);background:var(--raised);
  font-size:13.5px;font-weight:640;color:var(--text)}
.duebar .ic{font-size:13px;line-height:1;color:var(--text-3)}
.duebar .sub{margin-left:auto;font-size:11.5px;font-weight:500;color:var(--text-3);
  text-transform:lowercase}
.duebar.wait{color:var(--wait);border-color:color-mix(in srgb,var(--wait) 42%,transparent);
  background:color-mix(in srgb,var(--wait) 11%,transparent)}
.duebar.wait .ic,.duebar.wait .sub{color:inherit;opacity:.85}
.duebar.late{color:var(--late);border-color:color-mix(in srgb,var(--late) 42%,transparent);
  background:color-mix(in srgb,var(--late) 11%,transparent)}
.duebar.late .ic{color:inherit;font-weight:800}
.duebar.late .sub{color:inherit;opacity:.85}

/* inline-flex, not inline: these sit in a .chips row next to the taller due
   pill, and as plain flex items they stretch to its height with the text left
   sitting at the top of the box. */
.req{display:inline-flex;align-items:center;
  font-family:var(--mono);font-size:10.5px;padding:2px 7px;border-radius:5px;
  background:color-mix(in srgb,var(--sky) 13%,transparent);color:var(--sky);white-space:nowrap}
/* The total an assignment is worth. It is not typed anywhere — the rubric
   decides it — so it is stated once, at the end of the criteria that add up
   to it, weighted enough to read as the answer rather than one more chip. */
.req.tot{margin-left:auto;font-weight:600;letter-spacing:.01em;
  background:color-mix(in srgb,var(--brand) 15%,transparent);color:var(--brand)}

/* ── forms ── */
.field{display:flex;flex-direction:column;gap:5px}
.field label{font-size:11px;text-transform:uppercase;letter-spacing:.07em;color:var(--text-3);font-weight:600}
.input{
  background:var(--raised);border:1px solid var(--border);border-radius:var(--r-sm);
  padding:9px 11px;color:var(--text);font:inherit;font-size:14px;width:100%;
}
.input::placeholder{color:var(--text-3)}
.input:focus{border-color:var(--brand);outline:none}
/* A field that cannot be edited has to look like one, or the only way to find
   out is to click into it and wonder why nothing types. */
.input:disabled{color:var(--text-3);background:color-mix(in srgb,var(--raised) 55%,transparent);
  border-style:dashed;cursor:not-allowed}
textarea.input{resize:vertical;min-height:74px;line-height:1.5}
.grid2{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:13px}
/* Three short fields that belong on one line. min-width:0 because a date input
   carries an intrinsic width big enough to push the row wider than the sheet. */
.grid3{display:grid;grid-template-columns:repeat(auto-fit,minmax(142px,1fr));gap:13px}
.grid3>*{min-width:0}
.check{display:flex;gap:10px;align-items:center;font-size:13.5px;cursor:pointer}
/* ── checkbox ──
   The native control paints an opaque white box that belongs to no palette
   here, and accent-color only recolours the tick, not the unchecked well. So
   the box is drawn from tokens: a hollow well on the raised surface that
   fills with brand on check. The tick is a rotated L rather than a glyph —
   a font-rendered checkmark sits off-centre at this size on every platform. */
.check input[type=checkbox]{
  appearance:none;-webkit-appearance:none;margin:0;flex:none;
  width:17px;height:17px;border-radius:5px;position:relative;
  border:1.5px solid var(--border);background:var(--raised);cursor:pointer;
  transition:background .13s ease,border-color .13s ease,box-shadow .13s ease;
}
.check:hover input[type=checkbox]:not(:disabled){border-color:color-mix(in srgb,var(--brand) 55%,var(--border))}
.check input[type=checkbox]:checked{background:var(--brand);border-color:var(--brand)}
.check input[type=checkbox]:checked::after{
  content:"";position:absolute;left:5px;top:1.5px;width:4.5px;height:9px;
  border:solid var(--on-brand);border-width:0 2px 2px 0;transform:rotate(43deg);
}
.check input[type=checkbox]:active:not(:disabled){transform:scale(.93)}
.check input[type=checkbox]:disabled{opacity:.4;cursor:not-allowed}
.check:has(input:disabled){cursor:not-allowed;color:var(--text-3)}

.field .hint{font-size:11.5px;color:var(--text-3)}
.hint.warn{color:var(--wait)}

/* The native date picker ships a black glyph that is invisible on a dark
   field; there is no colour property for it, only a filter. */
input[type=date]::-webkit-calendar-picker-indicator{
  filter:invert(1) brightness(1.7) opacity(.65); cursor:pointer;
}
input[type=date]::-webkit-calendar-picker-indicator:hover{filter:invert(1) brightness(2) opacity(1)}
:root[data-theme="light"] input[type=date]::-webkit-calendar-picker-indicator{filter:opacity(.6)}

/* ── file drop zone ──
   The native file input renders an OS button that ignores every token in this
   sheet, so the real input is hidden inside a label that is styled instead. */
.drop{display:flex;flex-direction:column;align-items:center;gap:3px;cursor:pointer;
  padding:20px 16px;border:1.5px dashed var(--border);border-radius:var(--r-sm);
  background:color-mix(in srgb,var(--raised) 60%,transparent);text-align:center;transition:border-color .14s,background .14s}
.drop:hover,.drop.over{border-color:var(--brand);background:color-mix(in srgb,var(--brand) 8%,transparent)}
.drop input[type=file]{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
.drop .drop-ic{font-size:19px;color:var(--brand);line-height:1}
.drop .drop-t{font-size:13.5px;font-weight:580}
.drop .drop-s{font-size:11.5px;color:var(--text-3)}

/* ── assignment targeting ── */
.seg{display:flex;gap:3px;padding:3px;background:var(--raised);border:1px solid var(--border);border-radius:var(--r-sm)}
.segb{flex:1;font:inherit;font-size:12.5px;font-weight:560;padding:6px 8px;border:0;border-radius:6px;
  background:none;color:var(--text-2);cursor:pointer;white-space:nowrap;min-height:34px}
.segb:hover{color:var(--text)}
.segb[aria-pressed="true"]{background:var(--brand);color:var(--on-brand);font-weight:640}
.chips.pick{margin-top:8px}

/* ── terms ──
   A grading period is a filter, not a destination, so its control sits in the
   page header beside the primary action and is drawn a step quieter than a
   button. The dot carries the state — brand for the period you are actually
   in, hollow for one you have gone looking at, muted for one that is closed —
   because a teacher scanning the header should learn "this is now" without
   reading a word. */
.tdot{
  width:7px;height:7px;border-radius:50%;flex:0 0 auto;
  border:1.5px solid var(--text-3);background:transparent;
}
.tdot.now{background:var(--brand);border-color:var(--brand);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--brand) 18%,transparent)}
.tdot.closed{background:var(--text-3);border-color:var(--text-3);opacity:.6}
.tdot.all{border-color:var(--sky);background:color-mix(in srgb,var(--sky) 45%,transparent)}

.termchip{
  display:inline-flex;align-items:center;gap:8px;min-width:0;flex:0 1 auto;max-width:100%;
  font:inherit;font-size:12.5px;font-weight:560;color:var(--text);
  padding:8px 10px;min-height:36px;border-radius:var(--r-sm);
  background:var(--raised);border:1px solid var(--border);cursor:pointer;
}
.termchip:hover{border-color:var(--text-3)}
.termchip .tc-dot{
  width:7px;height:7px;border-radius:50%;flex:0 0 auto;
  border:1.5px solid var(--text-3);background:transparent;
}
.termchip.now .tc-dot{background:var(--brand);border-color:var(--brand);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--brand) 18%,transparent)}
.termchip.closed .tc-dot{background:var(--text-3);border-color:var(--text-3);opacity:.6}
.termchip.all .tc-dot{border-color:var(--sky);background:color-mix(in srgb,var(--sky) 45%,transparent)}
/* The name is the one part allowed to lose characters: a director who names a
   period "Second Quarter — Concert Cycle" must not push the button off. */
.termchip .tc-name{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:15ch;min-width:0}
/* A phone header holds the filter and one action and nothing else fits, so the
   name gives up more of itself there. Losing the tail costs little: the
   subtitle directly above spells the term out in full. */
@media (max-width:560px){ .termchip .tc-name{max-width:10ch} }
.termchip .tc-tag{
  font-size:9.5px;text-transform:uppercase;letter-spacing:.06em;font-weight:700;
  color:var(--text-3);border:1px solid var(--border);border-radius:4px;padding:1px 4px;
}
.termchip .tc-chev{color:var(--text-3);font-size:10px;margin-left:1px}

/* The switcher's rows put the dot outside the text column, so the names stay
   on one left edge whatever their state. */
.row .tdot{margin-right:2px}

/* ── the shape of a year ──
   Four cards, each showing its own answer as a bar cut into that many pieces.
   The picture is the point: "trimesters" is a word a teacher has to translate,
   and three segments is not. */
/* Two columns, always. There are four shapes and there always will be, and an
   auto-fit grid puts three on a wide sheet with the fourth orphaned below. */
.shapes{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin-top:2px}
.shapecard{
  display:flex;flex-direction:column;gap:7px;text-align:left;
  font:inherit;padding:11px 12px;cursor:pointer;
  background:var(--raised);border:1px solid var(--border);border-radius:var(--r-sm);
  color:var(--text-2);
}
/* The hints run to one or two lines depending on the reader's font size, and
   a row of cards that disagree about their height reads as a mistake. */
.sc-hint{min-height:2.7em}
.shapecard:hover{border-color:var(--text-3)}
.shapecard[aria-pressed="true"]{
  border-color:var(--brand);color:var(--text);
  background:color-mix(in srgb,var(--brand) 10%,var(--raised));
}
.sc-bar{display:flex;gap:2px;height:6px}
.sc-bar i{flex:1;border-radius:2px;background:var(--text-3);opacity:.45}
.shapecard[aria-pressed="true"] .sc-bar i{background:var(--brand);opacity:1}
.sc-name{font-size:13px;font-weight:600;color:var(--text);line-height:1.2}
.sc-hint{font-size:11px;color:var(--text-3);line-height:1.35}

/* ── term dates ──
   Name, start, end. The name column takes what is left so a long term name
   never squeezes a date box below the width its picker needs. */
.termdates{display:flex;flex-direction:column;gap:7px;margin-top:2px}
.td-row{display:grid;grid-template-columns:minmax(0,1.35fr) minmax(0,1fr) auto minmax(0,1fr);
  gap:6px;align-items:center}
.td-row .input{padding:8px 9px;font-size:12.5px;min-width:0;min-height:36px}
.td-row .td-date{font-family:var(--mono);font-size:11.5px}
.td-to{color:var(--text-3);font-size:12px;text-align:center}
@media (max-width:560px){
  /* Below this the four columns start clipping the native date picker, so the
     name takes its own line and the two dates keep full width underneath.
     Stacking costs the grouping the single row gave for free — a name and its
     own dates end up as far apart as two different terms — so the gap between
     terms opens up to say which dates belong to which. */
  .termdates{gap:15px}
  .td-row{grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);gap:5px}
  .td-row .td-name{grid-column:1 / -1}
}

/* ── "filed under" ──
   One line, stated rather than asked. It is a receipt for a decision the app
   already made correctly, which is why it reads as prose and not as a field. */
#a-term:empty{display:none}
.termline{
  display:flex;align-items:center;gap:8px;
  padding:9px 11px;border-radius:var(--r-sm);
  background:color-mix(in srgb,var(--raised) 60%,transparent);
  border:1px solid var(--border-soft);
}
.tl-txt{flex:1;min-width:0;font-size:12.5px;color:var(--text-2)}
.tl-txt b{color:var(--text);font-weight:600}
.tl-why{color:var(--text-3);font-size:11.5px}
.tl-act{
  font:inherit;font-size:11px;text-transform:uppercase;letter-spacing:.07em;font-weight:600;
  background:none;border:0;padding:0;cursor:pointer;color:var(--brand);flex:0 0 auto;
}
.tl-act:hover{text-decoration:underline}
.tl-act{padding:10px 4px;margin:-10px -4px}

/* A checkbox whose label carries a second explanatory line — used wherever a
   confirm sheet offers to do one extra thing. Top-aligned, because a two-line
   label against a centred box leaves the tick floating. */
.check .grow{flex:1;min-width:0}
.check .grow .meta{display:block;font-size:11.5px;color:var(--text-3);line-height:1.45;margin-top:2px}
.check:has(.grow .meta){align-items:flex-start}
.check:has(.grow .meta) input[type=checkbox]{margin-top:2px}
.picklist{margin-top:8px;max-height:210px;overflow-y:auto;border:1px solid var(--border);
  border-radius:var(--r-sm);background:color-mix(in srgb,var(--raised) 55%,transparent)}
/* align-items:center, not baseline: the row's text is two sizes (name and
   part), so a baseline box lands high against the taller line. */
.pickrow{padding:8px 11px;border-bottom:1px solid var(--border-soft);font-size:13px;
  align-items:center;transition:background .12s ease}
.pickrow:last-child{border-bottom:0}
.pickrow:hover:not(:has(input:disabled)){background:color-mix(in srgb,var(--brand) 6%,transparent)}
.pickrow:has(input:checked){background:color-mix(in srgb,var(--brand) 10%,transparent)}
.pickrow .grow{flex:1;min-width:0}
.pickrow .meta{color:var(--text-3);font-size:11.5px}

/* ── rubric ── */
.rubpeek{display:flex;flex-wrap:wrap;gap:5px;margin-top:7px}
.rubedit{display:flex;flex-direction:column;gap:8px}
.rbrow{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.35fr) 66px auto;gap:7px;align-items:center}
.rbrow .input{padding:7px 9px;font-size:13px}
.tbl td.na{background:color-mix(in srgb,var(--border) 30%,transparent)}
@media (max-width:560px){
  .rbrow{grid-template-columns:minmax(0,1fr) 62px auto}
  .rbrow .input[data-d]{grid-column:1 / -1;order:3}
}

/* ── rubric library (accordion) ──
   A school ends up with a lot of these, so the list collapses to one line per
   rubric: the collapsed line still carries the criteria names, which is what
   people actually scan for when they are looking for the right rubric. */
.rlib{display:flex;flex-direction:column;gap:9px}
.rlib-tools{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-bottom:-3px}
.rlib-tools .input{flex:0 1 260px;min-width:150px;font-size:13px;padding:7px 11px}
.rlib-tools .cnt{font-size:11.5px;color:var(--text-3);margin-left:auto}

.rcard{background:var(--surface);border:1px solid var(--border);border-radius:var(--r);
  overflow:hidden;transition:border-color .16s}
.rcard.open{border-color:color-mix(in srgb,var(--brand) 40%,var(--border))}
.rhead{display:flex;align-items:center;gap:11px;width:100%;padding:12px 15px;
  background:none;border:0;color:inherit;font:inherit;text-align:left;cursor:pointer}
.rhead:hover{background:var(--raised)}
.rchev{flex:0 0 auto;width:11px;font-size:13px;line-height:1;color:var(--text-3);
  transition:transform .2s ease,color .16s}
.rcard.open .rchev{transform:rotate(90deg);color:var(--brand)}
.rhead .grow{flex:1;min-width:0}
.rhead .nm{display:block;font-size:14px;font-weight:600;letter-spacing:-.008em;line-height:1.3}
.rhead .meta{display:block;margin-top:2px;font-size:11.5px;color:var(--text-3);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rhead .rt{margin-left:auto;display:flex;align-items:center;gap:8px;flex:0 0 auto}
.runused{font-size:11px;color:var(--text-3);white-space:nowrap}
.rpts{font-family:var(--mono);font-size:11.5px;color:var(--text-2);white-space:nowrap;
  border:1px solid var(--border);border-radius:999px;padding:2px 9px}
.rcard.open .rpts{color:var(--brand);border-color:color-mix(in srgb,var(--brand) 42%,transparent)}

/* 0fr → 1fr animates a height nobody has to measure, so a rubric with two
   criteria and one with twelve both open at the same speed. */
.rbody{display:grid;grid-template-rows:0fr;transition:grid-template-rows .22s ease}
.rcard.open .rbody{grid-template-rows:1fr}
.rbin{overflow:hidden;min-height:0}
.rbody .rows{border-top:1px solid var(--border)}
.rfoot{display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:10px 15px;border-top:1px solid var(--border-soft)}
.rfoot .meta{font-size:11.5px;color:var(--text-3)}
.rlib-none{padding:26px 18px;text-align:center;font-size:13px;color:var(--text-3);
  border:1px dashed var(--border);border-radius:var(--r)}

@media (max-width:560px){
  .rhead{padding:11px 13px;gap:9px}
  .rhead .nm{font-size:13.5px}
  .rlib-tools .cnt{display:none}
  .runused{display:none}          /* the name needs the width more than this does */
  .rfoot{flex-direction:column;align-items:stretch;gap:8px}
  .rfoot .btn{width:100%}
}
.inline{display:flex;gap:8px;align-items:center}
.inline .input{flex:1;min-width:0}

/* ── roster filters ── */
.rfilter{display:flex;flex-direction:column;gap:9px}
.rfilter .input{max-width:340px}
.rfilter #r-chips{display:flex;flex-direction:column;gap:7px}
.rfilter .chip .num{margin-left:2px;opacity:.7;font-size:11px}

/* ── import ── */
.steps{display:flex;flex-direction:column;gap:15px}
.step{display:flex;gap:11px;align-items:flex-start}
.step .sn{flex:0 0 auto;width:22px;height:22px;border-radius:50%;display:grid;place-items:center;
  background:var(--raised);border:1px solid var(--border);font-family:var(--mono);font-size:11px;color:var(--text-2)}
.step b{font-size:13.5px;font-weight:600;display:block}
.step p{margin:3px 0 8px;font-size:12.5px;line-height:1.5;color:var(--text-2)}
/* the block rule above is for the step title only — bold inside body copy
   must stay inline or every emphasis breaks the paragraph */
.step p b{display:inline;font-size:inherit;font-weight:650;color:var(--text)}
.step input[type=file]{font-size:12.5px;padding:8px 10px}
.improws{max-height:290px;overflow-y:auto}
/* one unrecognised spelling from the file, and the instrument it becomes */
.maprow{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.25fr);gap:9px;align-items:center}
.maprow .input{padding:7px 9px;font-size:13px}
.mapraw{font-size:13px;font-weight:560;min-width:0;overflow-wrap:anywhere}
.mapraw small{display:block;font-weight:400;font-size:11.5px;color:var(--text-3);margin-top:1px}
@media (max-width:560px){
  .maprow{grid-template-columns:minmax(0,1fr)}
}

/* ── profile ── */
.me-card{display:flex;align-items:center;gap:12px;padding:13px 14px;background:var(--raised);
  border:1px solid var(--border);border-radius:var(--r)}
.me-card .grow{flex:1;min-width:0}
.me-card .nm{font-size:14.5px;font-weight:600;display:block}
.me-card .meta{font-size:12px;color:var(--text-3);display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

.chips{display:flex;flex-wrap:wrap;gap:6px}
.chip{font:inherit;font-size:12px;padding:5px 11px;border-radius:999px;border:1px solid var(--border);
  background:var(--raised);color:var(--text-2);cursor:pointer}
.chip:hover{color:var(--text);border-color:var(--text-3)}
.chip[aria-pressed="true"]{background:color-mix(in srgb,var(--sky) 16%,transparent);border-color:var(--sky);color:var(--sky);font-weight:600}

/* ── rubric ── */
.rubric{display:flex;flex-direction:column;gap:11px}
.rub-row{display:grid;grid-template-columns:1fr auto;gap:10px;align-items:center}
.rub-row .rl{font-size:13px}
.rub-row .rl small{display:block;color:var(--text-3);font-size:10.5px;margin-top:1px}
.scale{display:flex;gap:4px}
.scale button{width:29px;height:29px;border-radius:7px;border:1px solid var(--border);
  background:var(--raised);color:var(--text-2);font-family:var(--mono);font-size:12.5px;cursor:pointer;padding:0}
.scale button:hover{border-color:var(--brand);color:var(--text)}
.scale button[aria-pressed="true"]{background:var(--brand);border-color:var(--brand);color:var(--on-brand);font-weight:700}
/* Actions sit at the end of the row the way a form's actions normally do.
   A full-width Save is a runway on a 1200px panel; on a phone it's the right
   target, so the width comes back below 560px. */
.gsave{display:flex;gap:9px;margin-top:4px;justify-content:flex-end}
.gsave .btn{padding-top:10px;padding-bottom:10px}
.gsave #g-save{flex:0 0 auto;min-width:172px}

/* ── assignment lifecycle ──
   The lock bar is a separate line from the due bar on purpose: a due date says
   when work was expected, closing is the only thing that stops it arriving,
   and a director who cannot tell those apart ends up back in a spreadsheet. */
.lockbar{display:flex;align-items:center;gap:9px;padding:9px 12px;
  border:1px solid var(--border);border-radius:var(--r-sm);background:var(--raised);
  font-size:12.5px;font-weight:560;color:var(--text-2)}
.lockbar .ic{font-size:12px;line-height:1}
.lockbar .btn{margin-left:auto}
.lockbar .exc{font-weight:500;color:var(--text-3)}
.lockbar.on{color:var(--done);border-color:color-mix(in srgb,var(--done) 38%,transparent);
  background:color-mix(in srgb,var(--done) 10%,transparent)}

/* A roster row that carries a secondary action: the row itself is still one
   tap to the student, and the extra button sits outside that tap target so
   "allow late" can never be hit by someone aiming for the name. */
.row.rowline{padding:0}
.row.rowline:hover{background:var(--raised)}
.rowopen{flex:1;display:flex;align-items:center;gap:12px;min-width:0;
  padding:10px 15px;background:none;border:0;color:inherit;font:inherit;
  text-align:left;cursor:pointer}
.rowopen .grow{flex:1;min-width:0}
.rowopen .rt{margin-left:auto;display:flex;align-items:center;gap:9px;flex:none;color:var(--text-3)}
.rowopen .qgo{color:var(--text-3);font-size:15px;line-height:1}
.row.rowline:hover .qgo{color:var(--brand)}
.lateact{font-size:11.5px;padding:3px 9px;flex:none;margin-right:15px}

/* the trail on a grade that has been changed before */
.gradeline{display:flex;align-items:center;gap:8px;font-size:11.5px;color:var(--text-3);
  padding:8px 11px;border:1px dashed var(--border);border-radius:var(--r-sm);
  background:color-mix(in srgb,var(--sky) 6%,transparent)}
.gradeline b{color:var(--text-2);font-weight:600}

/* nothing turned in — the empty state that still has three things to do */
.nosub{display:flex;flex-direction:column;align-items:center;gap:8px;text-align:center;
  padding:26px 16px;border:1px dashed var(--border);border-radius:var(--r-sm);color:var(--text-3)}
.nosub .eic{font-size:24px;opacity:.6}
.nosub .et{font-size:14px;font-weight:600;color:var(--text-2)}
.nosub .ed{font-size:12.5px;max-width:44ch;line-height:1.55}

/* the destructive-ish action in a solo action row reads as a link, not a
   button: it is the rare path and it should not compete with the primary */
.gsave.solo .lnk{background:none;border:0;color:var(--text-3);font:inherit;font-size:12.5px;
  cursor:pointer;padding:6px 10px;margin-right:auto}
.gsave.solo .lnk:hover{color:var(--late)}
@media (max-width:560px){
  .gsave.solo{flex-wrap:wrap}
  .gsave.solo .lnk{order:3;margin:0 auto}
  .lateact{margin-right:12px}
}

/* ── grading queue ──
   One card per assignment, collapsed to a single scannable line: title, how
   far through it you are, and how many are still waiting. A director opening
   this on Monday morning should be able to read the whole term's backlog
   without scrolling into a single student. */
.qgroup{background:var(--surface);border:1px solid var(--border);border-radius:var(--r);overflow:hidden}
.qgroup.open{border-color:color-mix(in srgb,var(--brand) 32%,var(--border))}
/* The head is a row, not a button: the caret/title area toggles the card and
   the Grade button sits beside the pill, so starting a queue is one click from
   a collapsed card and a button never nests inside a button. */
.qhead{display:flex;align-items:center;gap:12px;width:100%;padding:13px 15px;
  transition:background .13s ease}
.qhead:has(.qtog:hover){background:var(--raised)}
.qtog{display:flex;align-items:center;gap:12px;flex:1;min-width:0;padding:0;
  background:none;border:0;color:inherit;font:inherit;text-align:left;cursor:pointer}
/* the bar and the pill ride inside the toggle so the whole row stays clickable */
.qtog .qtitle{flex:1;min-width:0}
.qhead .qact{flex:none}
.qcar{flex:none;color:var(--text-3);font-size:16px;line-height:1;
  transition:transform .16s ease,color .16s ease}
.qgroup.open .qcar{transform:rotate(90deg);color:var(--brand)}
.qtitle{flex:1;min-width:0}
.qt{display:block;font-size:14px;font-weight:600;letter-spacing:-.008em;line-height:1.3;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.qsub{display:block;font-size:11.5px;color:var(--text-3);margin-top:2px}
/* the bar is the only thing on this line that answers "how much is left" at a
   glance, so it holds its width instead of collapsing on narrow cards */
.qbar{flex:0 0 84px;height:4px;border-radius:2px;background:var(--border);overflow:hidden}
.qbar i{display:block;height:100%;background:var(--brand);border-radius:2px;transition:width .3s ease}
.qbody{display:none;border-top:1px solid var(--border)}
.qgroup.open .qbody{display:block}
.qrow{padding-left:15px}
.qrow .qgo{color:var(--text-3);font-size:15px;line-height:1}
.qrow:hover .qgo{color:var(--brand)}

/* ── focused grader ── */
.gbar{display:flex;align-items:center;gap:12px;margin-bottom:2px}
.gback{padding-left:9px;padding-right:11px;color:var(--text-2);flex:none}
.gback:hover{color:var(--text)}
.gwhere{flex:1;min-width:0;text-align:center;font-size:13px;font-weight:600;color:var(--text-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.gnav{display:flex;align-items:center;gap:4px;flex:none}
.gnav .btn{padding:4px 9px;font-size:14px;line-height:1;color:var(--text-2)}
.gpos{font-size:12px;color:var(--text-3);min-width:44px;text-align:center}
@media (max-width:560px){
  /* "0 of 1 graded" already answers how far along you are, so the bar is the
     first thing to go when the Grade button needs the room */
  .qbar{display:none}
  .gwhere{display:none}
  .gsave{justify-content:stretch}
  .gsave #g-save{flex:1;min-width:0}
}

/* ── waveform ──
   Drawn on a canvas from the envelope the server builds for each take, the
   same rendering the band player uses: an RMS body for the shape with the peak
   outline behind it, played bars in brand, the rest stepped back. The bar
   count comes from the element's width, so the height below is the only thing
   CSS decides — the canvas is sized to its host in script. */
.wave-wrap{position:relative;height:76px;cursor:pointer}
.wavec{display:block;width:100%}
.ruler{display:flex;justify-content:space-between;margin-top:6px;font-family:var(--mono);font-size:10.5px;color:var(--text-3)}

.meter{display:flex;gap:2px;height:7px}
.meter i{flex:1;border-radius:2px;background:var(--border);display:block;transition:background .07s}
.meter i.on{background:var(--done)}
.meter i.hot{background:var(--wait)}
.meter i.clip{background:var(--late)}

.reclamp{display:flex;align-items:center;justify-content:center;gap:8px;
  font-family:var(--mono);font-size:15px;color:var(--late);font-weight:600}
.blip{width:10px;height:10px;border-radius:50%;background:var(--late);display:block;animation:bl 1s ease-in-out infinite}
@keyframes bl{0%,100%{opacity:1}50%{opacity:.25}}

/* ── record transport ──
   Start/Stop used to be `.btn primary` — the same teal slab as "Submit
   selected take", which put a device control and the one irreversible action
   on equal footing, and hung a ● text glyph on it that no font centres
   optically at button size. They get their own shell: an elevated bar that
   carries a transport lamp instead of competing with the submit CTA. Red is
   already this app's recording colour (.blip, .reclamp above), so the lamp
   here is the same light that starts pulsing once the take is rolling. */
.btn.rec{
  width:100%;gap:11px;padding:13px 18px;font-size:14.5px;font-weight:600;
  color:var(--text);border-radius:11px;
  background:linear-gradient(180deg,color-mix(in srgb,var(--raised) 94%,#fff),var(--raised));
  border:1px solid color-mix(in srgb,var(--brand) 30%,var(--border));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.05),0 8px 22px rgba(0,0,0,.34);
  transition:border-color .16s ease,box-shadow .16s ease,transform .12s ease;
}
/* the base .btn hover is a brightness filter, which would wash the lamp's
   glow out along with the bar */
.btn.rec:hover:not(:disabled){
  filter:none;
  border-color:color-mix(in srgb,var(--brand) 58%,var(--border));
  box-shadow:inset 0 1px 0 rgba(255,255,255,.07),0 10px 26px rgba(0,0,0,.4);
}
.btn.rec:active:not(:disabled){transform:translateY(1px)}
.btn.rec .lamp{
  width:11px;height:11px;border-radius:50%;background:var(--late);flex:none;
  box-shadow:0 0 0 4px color-mix(in srgb,var(--late) 15%,transparent),
             0 0 12px color-mix(in srgb,var(--late) 55%,transparent);
}
/* Stop drops the brand tint: by the time it shows, the sheet is already red
   (timer, blip), and a teal-edged button in the middle of that read as
   "confirm" rather than "halt". */
.btn.rec.stopping{border-color:color-mix(in srgb,var(--late) 42%,var(--border))}
.btn.rec.stopping:hover:not(:disabled){border-color:color-mix(in srgb,var(--late) 68%,var(--border))}
.btn.rec .lamp.sq{width:10px;height:10px;border-radius:2.5px;
  box-shadow:0 0 0 4px color-mix(in srgb,var(--late) 15%,transparent)}
:root[data-theme="light"] .btn.rec{
  background:var(--raised);
  box-shadow:0 2px 8px rgba(23,32,58,.09);
}
:root[data-theme="light"] .btn.rec:hover:not(:disabled){box-shadow:0 4px 14px rgba(23,32,58,.13)}

/* ── take picker ──
   One row per take, and every row carries the same controls so the right-hand
   column stays straight. Which take gets sent is a single choice, so it reads
   as a radio: the row itself is the control, and play/delete sit outside it as
   quiet icons rather than three equally-loud outlined buttons. */
.takes{display:flex;flex-direction:column}
.take{border-bottom:1px solid var(--border-soft);transition:background .13s ease}
.take:last-child{border-bottom:0}
.tk-row{display:flex;align-items:center;gap:4px;padding-right:9px}
/* Three rows of "Take 2 · 0:41" is not a basis for choosing which performance
   to send. The strip is what makes the rows tell them apart. */
.tk-wave{position:relative;height:38px;margin:0 12px 11px;cursor:pointer;
  border-radius:6px;transition:background .13s ease}
.tk-wave:hover{background:color-mix(in srgb,var(--text-2) 7%,transparent)}
.take.on{background:color-mix(in srgb,var(--brand) 9%,transparent)}
.tk-main{flex:1;min-width:0;display:flex;align-items:center;gap:11px;
  padding:10px 6px 10px 12px;background:none;border:0;color:inherit;font:inherit;
  text-align:left;cursor:pointer}
.tk-main:hover .tk-radio{border-color:color-mix(in srgb,var(--brand) 55%,var(--border))}
.tk-radio{width:17px;height:17px;flex:none;border-radius:50%;position:relative;
  border:1.5px solid var(--border);background:var(--raised);
  transition:background .14s ease,border-color .14s ease}
.take.on .tk-radio{background:var(--brand);border-color:var(--brand)}
/* a rotated L, not a glyph — no font centres a checkmark at 17px */
.take.on .tk-radio::after{content:"";position:absolute;left:5px;top:2px;width:4px;height:8.5px;
  border:solid var(--on-brand);border-width:0 2px 2px 0;transform:rotate(43deg)}
.tk-txt{min-width:0}
.tk-name{display:block;font-size:13.5px;font-weight:560;line-height:1.3;color:var(--text)}
.tk-dur{display:block;font-size:11.5px;color:var(--text-3)}
.take.on .tk-name{font-weight:620}
.tk-ic{width:30px;height:30px;flex:none;border-radius:8px;cursor:pointer;
  border:1px solid transparent;background:none;color:var(--text-2);font-size:12px;
  display:grid;place-items:center;transition:color .13s ease,background .13s ease,border-color .13s ease}
.tk-ic:hover{color:var(--text);background:var(--raised);border-color:var(--border)}
.tk-ic.del:hover{color:var(--late);background:color-mix(in srgb,var(--late) 12%,transparent);
  border-color:color-mix(in srgb,var(--late) 38%,transparent)}
.tk-tag{margin-left:8px;font-size:9.5px;font-weight:700;text-transform:uppercase;
  letter-spacing:.07em;padding:2px 6px;border-radius:5px;vertical-align:1px;white-space:nowrap}
.tk-tag.sent{color:var(--brand);background:color-mix(in srgb,var(--brand) 15%,transparent)}
.tk-tag.saving{color:var(--text-3);background:color-mix(in srgb,var(--text-3) 15%,transparent)}
.tk-tag.failed{color:var(--late);background:color-mix(in srgb,var(--late) 15%,transparent)}
.take.busy{opacity:.6}
.take.busy .tk-main,.take.busy .tk-ic{cursor:default}

/* ── empty / note ── */
.empty{display:flex;flex-direction:column;align-items:center;gap:10px;text-align:center;
  padding:46px 22px;color:var(--text-2)}
.empty .eic{font-size:26px;opacity:.65}
.empty .et{font-size:15px;font-weight:600;color:var(--text)}
.empty .ed{font-size:13px;max-width:38ch}

.note{border-left:2px solid var(--violet);background:color-mix(in srgb,var(--violet) 9%,transparent);
  padding:10px 13px;border-radius:0 var(--r-sm) var(--r-sm) 0;font-size:12.5px;color:var(--text-2)}
.note b{color:var(--text);font-weight:600}
.note a{color:var(--brand);text-decoration:underline;text-underline-offset:2px}

/* ── modal sheet ── */
.scrim{position:fixed;inset:0;background:rgba(4,6,14,.66);backdrop-filter:blur(3px);z-index:100}
.sheet{
  position:fixed;z-index:101;top:50%;left:50%;transform:translate(-50%,-50%);
  width:min(560px,calc(100vw - 32px));max-height:calc(100vh - 64px);overflow:auto;
  background:var(--surface);border:1px solid var(--border);border-radius:18px;box-shadow:var(--shadow);
}
.sheet-head{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:16px 20px;border-bottom:1px solid var(--border);position:sticky;top:0;background:var(--surface);z-index:2}
.sheet-head h3{margin:0;font-size:16.5px;font-weight:620;letter-spacing:-.015em}
.sheet-body{padding:18px 20px;display:flex;flex-direction:column;gap:14px}
.sheet-foot{padding:14px 20px;border-top:1px solid var(--border);display:flex;gap:9px;justify-content:flex-end;
  position:sticky;bottom:0;background:var(--surface)}

/* ── toasts ── */
.toast-wrap{position:fixed;left:50%;bottom:24px;transform:translateX(-50%);z-index:200;
  display:flex;flex-direction:column;gap:8px;align-items:center;pointer-events:none}
.toast{background:var(--raised);border:1px solid var(--border);border-radius:999px;
  padding:9px 17px;font-size:13px;box-shadow:var(--shadow);animation:tin .2s ease both}
.toast.err{border-color:color-mix(in srgb,var(--late) 50%,transparent);color:var(--late)}
.toast.ok{border-color:color-mix(in srgb,var(--done) 50%,transparent)}
@keyframes tin{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}

/* ── gradebook table ── */
.tblwrap{overflow-x:auto;border:1px solid var(--border);border-radius:var(--r);background:var(--surface)}
.tbl{border-collapse:separate;border-spacing:0;font-size:12.5px;width:100%;min-width:640px}
.tbl th,.tbl td{padding:8px 11px;border-bottom:1px solid var(--border-soft);white-space:nowrap}
.tbl thead th{position:sticky;top:0;background:var(--raised);font-size:10px;text-transform:uppercase;
  letter-spacing:.06em;color:var(--text-3);font-weight:640;text-align:center;border-bottom:1px solid var(--border)}
.tbl tbody th{position:sticky;left:0;background:var(--surface);text-align:left;font-weight:550;
  font-size:12.5px;color:var(--text);border-right:1px solid var(--border);z-index:1}
.tbl thead th:first-child{left:0;z-index:3;text-align:left;background:var(--raised);border-right:1px solid var(--border)}
.tbl td{text-align:center;font-family:var(--mono);font-variant-numeric:tabular-nums;color:var(--text-2)}
.tbl td.hi{color:var(--done);font-weight:700}
.tbl td.mid{color:var(--wait)}
.tbl td.lo{color:var(--late);font-weight:700}
.tbl td.miss{color:var(--late);font-size:11px}
.tbl td.none{color:var(--text-3)}

/* ── student phone-first ── */
.stack{display:flex;flex-direction:column;gap:12px}
.wcard{background:var(--surface);border:1px solid var(--border);border-radius:var(--r);
  padding:14px 15px;display:flex;flex-direction:column;gap:8px}
.wcard.due{border-color:color-mix(in srgb,var(--wait) 48%,transparent)}
.wcard.late{border-color:color-mix(in srgb,var(--late) 52%,transparent)}
.wcard .t{font-size:14.5px;font-weight:600;line-height:1.3}
.wcard .d{font-size:12px;color:var(--text-2)}
.wcard .wnote{font-size:12px;color:var(--text-3)}
/* Recorded and not turned in. Amber rather than grey because it is the one
   note on this card that is asking the student to go and do something. */
.wcard .wnote.warn{color:var(--wait);font-weight:600}
/* Sits under the turn-in button and carries the only warning a student gets
   before a one-way door, so it is centred on the button rather than ranged
   left with the body copy. */
.turnin-note{margin:8px 2px 0;text-align:center;font-size:12px;
  color:var(--text-3);line-height:1.5}

/* Work cards that carry an action (student "Your work"). The details take the
   left, the button sits beside them: a Record button stretched the full width
   of a desktop card reads as a banner, not something you press. Stacks and
   goes full-width on a phone, where a wide tap target is the point. */
.wcard.wrow{flex-direction:row;flex-wrap:wrap;align-items:center;gap:10px 18px}
.wcard.wrow .winfo{flex:1 1 240px;min-width:0;display:flex;flex-direction:column;gap:8px}
.wcard.wrow .wact{flex:0 0 auto;max-width:100%;display:flex;flex-direction:column;
  align-items:flex-end;gap:7px}
.wcard.wrow .wact .btn{min-width:150px}

/* ── phone tab bar ──────────────────────────────────────────
   Eight nav items do not fit across a phone, and a wrapped strip
   ate a third of the screen before any content. Four thumb-reach
   tabs plus More, fixed to the bottom, so navigation costs no
   vertical space at all. Hidden entirely above 860px. */
.tabbar{display:none}
.tabbar .nav{
  flex:1;min-width:0;flex-direction:column;align-items:center;justify-content:center;
  gap:3px;padding:6px 2px 4px;border:0;border-radius:10px;position:relative;
  color:var(--text-3);text-align:center;
}
.tabbar .nav svg.ico{
  width:21px;height:21px;flex:0 0 auto;opacity:1;
  fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;
}
.tabbar .nav .lb{font-size:9.5px;font-weight:600;letter-spacing:.004em;line-height:1.2;
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tabbar .nav:hover{background:none;color:var(--text-2)}
.tabbar .nav.on{background:none;color:var(--brand);border-color:transparent;font-weight:600}
.tabbar .nav .badge{position:absolute;top:2px;left:calc(50% + 5px);margin:0;
  font-size:9.5px;line-height:1.45;padding:0 5px;min-width:16px}

/* ── mobile-only / desktop-only label swaps ── */
.m-only{display:none}
/* .m-hide: shown on desktop, dropped on a phone where it is redundant or
   reachable in one tap from the tab bar. */

/* ── responsive ── */
@media (max-width:860px){
  /* auto + 1fr, or the grid hands the top bar half the leftover height */
  .app{grid-template-columns:1fr;grid-template-rows:auto 1fr;min-height:100dvh}

  /* the rail becomes a slim, sticky top bar: identity + which ensemble */
  .rail{
    position:sticky;top:0;z-index:40;height:auto;
    flex-direction:row;flex-wrap:nowrap;align-items:center;gap:9px;
    border-right:0;border-bottom:1px solid var(--border);
    padding:9px 12px;padding-top:calc(9px + env(safe-area-inset-top));
    background:color-mix(in srgb,var(--surface) 94%,transparent);
    backdrop-filter:saturate(1.4) blur(10px);
  }
  .rail .brandmark{padding:0;flex:0 0 auto}
  .rail .brandmark > div{display:none}        /* the school name moves into the switcher */
  .rail .navgroup{display:none}               /* → .tabbar */
  /* In top-bar mode this button is nothing but the 28px avatar, which is the
     whole tap target for the account menu. Pad it out to 40 and pull the
     padding back out of the bar's height so the bar does not grow. */
  .rail-foot{margin:-6px 0;padding:6px;border-top:0;width:auto;flex:0 0 auto;gap:4px;
    min-width:40px;min-height:40px;justify-content:center;border-radius:50%}
  .rail-foot:hover{background:none}
  .rail-foot .who-wrap,.rail-foot .chev{display:none}
  .rfilter .input{max-width:none}
  /* Three wrapped rows of chips push the roster itself off a phone screen;
     each filter row scrolls sideways instead. */
  .rfilter .chips{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;padding-bottom:1px}
  .rfilter .chips::-webkit-scrollbar{display:none}
  .rfilter .chip{flex:0 0 auto}
  .ens-switch{flex:1 1 auto;min-width:0;padding:6px 10px}
  .ens-switch .val,.ens-switch .lbl{max-width:none}
  .ens-switch .lbl{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

  .tabbar{
    display:flex;gap:2px;position:fixed;left:0;right:0;bottom:0;z-index:60;
    background:color-mix(in srgb,var(--surface) 94%,transparent);
    backdrop-filter:saturate(1.4) blur(12px);
    border-top:1px solid var(--border);
    padding:5px 4px;padding-bottom:calc(5px + env(safe-area-inset-bottom));
  }
  .tabbar.hidden{display:none !important}

  .main{padding:15px 13px calc(80px + env(safe-area-inset-bottom))}
  .toast-wrap{bottom:calc(84px + env(safe-area-inset-bottom))}

  .m-only{display:inline}
  .d-only{display:none}
}

@media (max-width:640px){
  .m-hide{display:none !important}
  /* Nothing here is a primary action, so these all sit a few pixels under the
     threshold on a desktop where a cursor is exact. On a phone they are the
     Edit, the Duplicate, the Everyone/Sections switch — all things people miss. */
  .btn.sm{min-height:36px;min-width:36px}
  .segb{min-height:36px}

  /* headings and their actions stack; buttons split the width evenly */
  .head{flex-direction:column;align-items:stretch;gap:11px}
  .head h2{font-size:20px}
  .head .acts{width:100%}
  .head .acts .btn{flex:1 1 0;min-width:0}

  /* four tall stat cards were 800px of scrolling before the first list */
  .cards{grid-template-columns:1fr 1fr;gap:9px}
  .card{padding:11px 12px;gap:2px}
  .card .k{font-size:9.5px;letter-spacing:.06em}
  .card .v{font-size:23px}
  .card .m{font-size:11px;line-height:1.35}
  /* an odd card would otherwise sit half-width beside a hole */
  .cards > .card:last-child:nth-child(odd){grid-column:1 / -1}

  /* one line per field, so a name never collides with its buttons */
  .row{padding:10px 13px;gap:9px}
  .row .nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .row .meta{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .row .rt{gap:6px}

  .panel-head{padding:11px 13px}
  .panel-body{padding:13px}
  .empty{padding:34px 18px}
  .sechead{padding:7px 13px}

  /* 16px stops iOS zooming the page when a field takes focus */
  .input{font-size:16px;padding:10px 12px}
  .btn{padding:9px 14px}
  .btn.sm{padding:6px 10px}
  /* short paired fields (tempo/measures) stay paired — stacking them turned a
     one-screen form into a scroll. The date is the one that cannot shrink:
     at 16px it needs the full width, so it takes the row and the pair sits
     under it. */
  .grid2{grid-template-columns:1fr 1fr;gap:10px}
  .grid3{grid-template-columns:1fr 1fr;gap:10px}
  .grid3>:first-child{grid-column:1/-1}
  .field label{font-size:10.5px}

  /* modals become bottom sheets — reachable, and taller on a short screen */
  .sheet{
    top:auto;bottom:0;left:0;transform:none;
    width:100%;max-width:none;max-height:88dvh;
    border-radius:18px 18px 0 0;border-bottom:0;
  }
  .sheet-head{padding:14px 16px}
  .sheet-body{padding:15px 16px}
  .sheet-foot{padding:12px 16px;padding-bottom:calc(12px + env(safe-area-inset-bottom));gap:8px}
  .sheet-foot .btn{flex:1 1 0;min-width:0}

  /* the gradebook is the one screen that has to scroll sideways — say so */
  .tbl{min-width:520px;font-size:12px}
  .tbl th,.tbl td{padding:7px 9px}
  .tbl tbody th{max-width:116px;overflow:hidden;text-overflow:ellipsis}

  .wcard .pill,.wcard .btn.sm{align-self:flex-start}
  .rub-row{gap:8px}
  .scale button{width:34px;height:34px;font-size:13px}
  .wave-wrap{height:64px}
}

/* phone: the action goes back under the details and spans the card */
@media (max-width:720px){
  .wcard.wrow{flex-direction:column;align-items:stretch;gap:8px}
  /* the row basis is a width; in a column it would become a 240px-tall block */
  .wcard.wrow .winfo{flex:0 0 auto}
  .wcard.wrow .wact{width:100%;align-items:stretch}
  .wcard.wrow .wact .pill,.wcard.wrow .wact .btn.sm{align-self:flex-start;min-width:0}
  .wcard.wrow .wact .btn.primary{width:100%}
}

.scrollhint{display:none;margin:-4px 0 0;font-size:11.5px;color:var(--text-3)}
@media (max-width:640px){ .scrollhint{display:block} }

/* ============================================================
   Plan & billing
   ============================================================ */
.bill-hero{display:flex;align-items:center;gap:16px;flex-wrap:wrap;padding:16px 15px;
  border-bottom:1px solid var(--border)}
.bill-hero > div{flex:1;min-width:200px;display:flex;flex-direction:column;gap:3px}
.bill-plan{font-size:20px;font-weight:640;letter-spacing:-.02em}
.bill-sub{font-size:13px;color:var(--text-2)}

.usage-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:16px 22px;padding:16px 15px}
.usage{display:flex;flex-direction:column;gap:7px}
.usage-top{display:flex;align-items:baseline;justify-content:space-between;gap:10px}
.usage-l{font-size:12px;color:var(--text-2);font-weight:560}
.usage-v{font-size:12px;color:var(--text-3);font-family:var(--mono);font-variant-numeric:tabular-nums}
.usage-bar{height:5px;border-radius:3px;background:var(--raised);overflow:hidden}
.usage-bar > i{display:block;height:100%;border-radius:3px;background:var(--brand);
  transition:width .3s ease}
.usage-bar > i.warn{background:var(--wait)}
.usage-bar > i.full{background:var(--late)}

/* A count with no ceiling, drawn where a meter would otherwise sit. Same
   height as one, so a grid of both stays on its baselines. */
.usage-free{font-size:11.5px;color:var(--text-3);line-height:5px;padding:0 0 1px}

.bill-foot{padding:0 15px 15px;font-size:12.5px;color:var(--text-3);line-height:1.55}

/* How seats work, as three steps rather than a paragraph nobody finishes. */
.steps{margin:0;padding:14px 15px 4px;list-style:none;counter-reset:step;
  display:flex;flex-direction:column;gap:12px}
.steps li{position:relative;counter-increment:step;padding-left:32px;
  font-size:13px;line-height:1.6;color:var(--text-2)}
.steps li b{color:var(--text);font-weight:600}
.steps li::before{content:counter(step);position:absolute;left:0;top:1px;
  width:21px;height:21px;border-radius:50%;background:var(--raised);
  border:1px solid var(--border);color:var(--text-2);
  display:grid;place-items:center;font-size:11px;font-weight:700}

.bill-seats{padding:12px 15px 15px;display:flex;flex-direction:column;gap:10px;align-items:flex-start}
.bill-seats p{margin:0;font-size:13.5px;color:var(--text-2);line-height:1.6}
.bill-seats .hint{line-height:1.55}

/* Annual / monthly. A segmented control, not two buttons: the point is that
   one of the two is always the one you are looking at. */
.plans-box{display:flex;flex-direction:column;align-items:stretch}
.int-toggle{display:inline-flex;align-self:flex-start;padding:3px;margin:0 0 14px;gap:3px;
  background:var(--raised);border:1px solid var(--border);border-radius:999px}
.int-toggle .int{appearance:none;background:none;border:0;cursor:pointer;
  display:inline-flex;align-items:center;gap:6px;padding:7px 14px;border-radius:999px;
  font:inherit;font-size:12.5px;font-weight:600;color:var(--text-2);
  letter-spacing:.01em;white-space:nowrap;transition:background .15s,color .15s}
.int-toggle .int:hover{color:var(--text)}
.int-toggle .int.on{background:var(--brand);color:var(--on-brand)}
.int-save{font-size:10.5px;font-weight:700;letter-spacing:.02em;padding:2px 6px;
  border-radius:999px;background:color-mix(in srgb,var(--brand) 16%,transparent);
  color:var(--brand)}
.int-toggle .int.on .int-save{background:color-mix(in srgb,#000 16%,transparent);color:inherit}

.plans-wrap{padding:15px}
.plans{display:grid;grid-template-columns:repeat(auto-fit,minmax(215px,1fr));gap:13px;
  align-items:start}
.plan{background:var(--raised);border:1px solid var(--border);border-radius:var(--r-sm);
  padding:15px 16px;display:flex;flex-direction:column;gap:9px}
.plan.hi{border-color:var(--brand);box-shadow:0 0 0 1px var(--brand) inset}
.plan.on{border-color:var(--done)}
.plan-top{display:flex;align-items:center;justify-content:space-between;gap:8px;min-height:22px}
.plan-nm{font-size:13px;font-weight:640;letter-spacing:.01em}
.plan-price{font-size:25px;font-weight:660;letter-spacing:-.025em;line-height:1.1}
.plan-price .per{display:block;font-size:11.5px;font-weight:500;color:var(--text-3);
  letter-spacing:0;margin-top:3px}
.plan-was{font-size:16px;font-weight:500;color:var(--text-3);margin-right:7px;
  text-decoration-thickness:1.5px}
.plan-note{margin:0;font-size:12.5px;color:var(--text-2);line-height:1.5}
.plan-tag{margin:0;font-size:12.5px;color:var(--text-3)}
.plan-list{margin:2px 0 0;padding:0;list-style:none;display:flex;flex-direction:column;gap:6px}
.plan-list li{position:relative;padding-left:17px;font-size:12.5px;color:var(--text-2);line-height:1.45}
.plan-list li::before{content:"✓";position:absolute;left:0;top:0;color:var(--brand);font-weight:700;font-size:11px}
.plans-cta{padding:0 15px 15px}
.plans-cta .btn{width:100%;justify-content:center}
@media (min-width:560px){ .plans-cta .btn{width:auto} }

.pill.found{color:var(--violet);border-color:color-mix(in srgb,var(--violet) 42%,transparent);
  background:color-mix(in srgb,var(--violet) 13%,transparent)}
.bill-plan .pill{margin-left:8px;vertical-align:3px}

/* limit reached / coming soon */
.lim{display:flex;gap:13px;align-items:flex-start;padding:14px 15px;margin-bottom:15px;
  background:var(--raised);border:1px solid var(--border);border-radius:var(--r-sm)}
.lim-ic{flex:0 0 auto;width:26px;height:26px;border-radius:50%;background:var(--brand);
  color:var(--on-brand);display:grid;place-items:center;font-weight:800;font-size:13px}
.lim-msg{margin:0;font-size:13.5px;line-height:1.6;color:var(--text-2)}
.lim-msg b{color:var(--text);font-weight:600}

/* ============================================================
   Rehearsals
   ============================================================ */

/* #reh-browse holds one <section> per group (a month, a piece). As a plain
   block its sections sat flush, so every group heading crowded the last row of
   the group above it — the heading read as a caption on the wrong card. */
#reh-body,#reh-detail,#reh-browse{display:flex;flex-direction:column;gap:22px}

.sheet-lead{margin:0 0 4px;font-size:13.5px;line-height:1.55;color:var(--text-2)}

.rsec{display:flex;flex-direction:column;gap:9px}
.rsec-h{
  margin:0;display:flex;align-items:center;gap:8px;
  font-size:11.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--text-3);
}
.rsec-n{
  padding:1px 7px;border-radius:999px;background:var(--raised);
  color:var(--text-2);font-size:11px;letter-spacing:0;font-weight:600;
}
.rsec.attn .rsec-h{color:var(--wait)}

/* ── rehearsal cards ── */
.rgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(232px,1fr));gap:11px}
.rcard{
  display:flex;flex-direction:column;gap:5px;align-items:flex-start;text-align:left;
  padding:14px 15px;border-radius:var(--r);cursor:pointer;
  background:var(--surface);border:1px solid var(--border);
  color:var(--text);font:inherit;position:relative;overflow:hidden;
  transition:border-color .14s ease,background .14s ease,transform .14s ease;
}
.rcard::before{content:"";position:absolute;inset:0 auto 0 0;width:3px;background:var(--border)}
.rcard.wait::before{background:var(--wait)}
.rcard.late::before{background:var(--late)}
.rcard.done::before{background:var(--done)}
.rcard:hover{background:var(--raised);border-color:color-mix(in srgb,var(--brand) 45%,var(--border))}
.rc-day{font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--text-3)}
.rc-title{font-size:15px;font-weight:640;letter-spacing:-.012em;line-height:1.25}
.rc-meta{display:flex;flex-wrap:wrap;gap:6px;margin-top:1px}
.rc-go{margin-top:5px;font-size:12px;font-weight:600;color:var(--brand)}
.rcard:hover .rc-go{text-decoration:underline}

/* ── browse toolbar ── */
.rbar{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.rseg{flex:0 0 auto}
.rbar-right{display:flex;gap:8px;margin-left:auto;flex-wrap:wrap}
.rsel{width:auto;min-width:150px;font-size:13px;padding:7px 10px}
.rsearch{width:auto;min-width:190px;font-size:13px;padding:7px 11px}

.rehrow{gap:11px}
.rehdot{flex:none;width:8px;height:8px;border-radius:50%;background:var(--text-3)}
.rehdot.wait{background:var(--wait)}
.rehdot.late{background:var(--late)}
.rehdot.done{background:var(--done)}

/* ── track and source rows ── */
.trackrow,.srcrow{gap:11px;cursor:pointer;align-items:center}
.srcrow{cursor:default}
.trackrow.bad .nm,.srcrow.bad .nm{color:var(--late)}
.trackrow.nowplaying{background:color-mix(in srgb,var(--brand) 10%,transparent)}
.trackrow.nowplaying .nm{color:var(--brand)}

.tplay{
  flex:none;width:34px;height:34px;display:grid;place-items:center;
  border:1px solid var(--border);background:var(--raised);border-radius:50%;
  color:var(--text-2);cursor:pointer;transition:all .13s ease;
}
.tplay.sm{width:27px;height:27px}
.tplay:hover:not(:disabled){background:var(--brand);border-color:var(--brand);color:var(--on-brand)}
.tplay:disabled{opacity:.4;cursor:default}
.tp-ic{
  display:block;width:0;height:0;margin-left:2px;
  border-style:solid;border-width:5.5px 0 5.5px 9px;
  border-color:transparent transparent transparent currentColor;
}
.tplay.sm .tp-ic{border-width:4.5px 0 4.5px 7.5px}
.trackrow.nowplaying .tplay{background:var(--brand);border-color:var(--brand);color:var(--on-brand)}

.srcacts{display:flex;align-items:center;gap:8px;flex-wrap:wrap;justify-content:flex-end}
.iconbtn.del{color:var(--text-3);font-size:13px}
.iconbtn.del:hover{color:var(--late);background:color-mix(in srgb,var(--late) 14%,transparent)}

/* ── upload queue ── */
.uploads{display:flex;flex-direction:column;gap:7px;margin-top:10px}
.uprow{
  display:grid;grid-template-columns:1fr auto;gap:3px 12px;align-items:center;
  padding:9px 12px;border:1px solid var(--border);border-radius:var(--r-sm);
  background:var(--surface);
}
.uprow.bad{border-color:color-mix(in srgb,var(--late) 45%,var(--border))}
.upn{font-size:13px;font-weight:560;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ups{font-size:11.5px;color:var(--text-3);font-variant-numeric:tabular-nums}
.ups.bad{color:var(--late)}
.upbar{grid-column:1/-1;height:3px;border-radius:2px;background:var(--raised);overflow:hidden}
.upbar i{display:block;height:100%;background:var(--brand);transition:width .2s ease}
.uprow.bad .upbar i{background:var(--late)}

/* ── detail header ── */
.dhead{display:flex;align-items:flex-start;gap:14px;flex-wrap:wrap;margin-bottom:18px}
.dtitle{flex:1;min-width:180px}
.dtitle h2{margin:0;font-size:22px;font-weight:640;letter-spacing:-.022em;outline:none}
.dtitle h2[contenteditable]{
  border-bottom:1px dashed transparent;padding-bottom:1px;
}
.dtitle h2[contenteditable]:hover{border-bottom-color:var(--border)}
.dtitle h2[contenteditable]:focus{border-bottom-color:var(--brand)}
.dsub{margin:3px 0 0;font-size:12.5px;color:var(--text-2)}
.dacts{display:flex;gap:8px;flex-wrap:wrap}
/* Its own row above the title: sitting inline next to a 22px heading it read
   as part of the title rather than as the way out of the screen. */
.dback{flex:0 0 100%;margin-bottom:12px}
.backbtn{
  display:inline-flex;align-items:center;gap:6px;
  background:var(--surface);border:1px solid var(--border-soft);
  color:var(--text-2);font:inherit;font-size:12.5px;font-weight:560;
  cursor:pointer;padding:5px 12px 5px 9px;border-radius:999px;
  transition:color .13s ease,background .13s ease,border-color .13s ease;
}
.backbtn:hover{color:var(--text);background:var(--raised);border-color:var(--border)}
.backbtn:focus-visible{outline:2px solid var(--brand);outline-offset:2px}
.backbtn span{font-size:15px;line-height:1;transition:transform .13s ease}
.backbtn:hover span{transform:translateX(-2px)}

/* ── banners ── */
.banner{
  display:flex;gap:10px;align-items:flex-start;
  padding:12px 14px;border-radius:var(--r-sm);font-size:13px;line-height:1.5;
  border:1px solid var(--border);background:var(--surface);
}
.banner.wait{border-color:color-mix(in srgb,var(--wait) 40%,transparent);
  background:color-mix(in srgb,var(--wait) 9%,transparent)}
.banner.info{border-color:color-mix(in srgb,var(--sky) 38%,transparent);
  background:color-mix(in srgb,var(--sky) 8%,transparent)}
.warnbox{
  padding:12px 14px;border-radius:var(--r-sm);font-size:13px;line-height:1.55;
  border-left:3px solid var(--late);background:color-mix(in srgb,var(--late) 10%,transparent);
}
.sheet-note{margin:12px 0 0;font-size:12.5px;color:var(--text-3)}
.keepbox{align-items:flex-start;padding:12px;border:1px solid var(--border);
  border-radius:var(--r-sm);background:var(--raised);line-height:1.5}
.keepbox .hint{font-size:12px;color:var(--text-3)}
.rowform{display:flex;gap:8px}
.rowform .input{flex:1}

.spin{
  display:inline-block;width:11px;height:11px;flex:none;
  border:1.7px solid color-mix(in srgb,currentColor 30%,transparent);
  border-top-color:currentColor;border-radius:50%;
  animation:spin .7s linear infinite;
}
@keyframes spin{to{transform:rotate(360deg)}}

/* ── publish bar ── */
.pubbar{
  position:sticky;bottom:0;z-index:6;
  display:flex;align-items:center;gap:14px;flex-wrap:wrap;
  padding:14px 16px;border-radius:var(--r);
  border:1px solid color-mix(in srgb,var(--brand) 40%,var(--border));
  background:color-mix(in srgb,var(--surface) 96%,transparent);
  backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);
}
.pubinfo{display:flex;flex-direction:column;gap:1px;flex:1;min-width:150px}
.pubinfo b{font-size:15px;font-weight:640}
.pubinfo span{font-size:12px;color:var(--text-2)}

/* ── piece picker ── */
.piecebtn{
  display:inline-flex;align-items:center;gap:6px;max-width:200px;
  padding:5px 10px;border-radius:999px;cursor:pointer;
  border:1px dashed var(--border);background:transparent;color:var(--text-3);
  font:inherit;font-size:12px;font-weight:560;
}
.piecebtn:hover{border-color:var(--brand);color:var(--brand)}
.piecebtn.set{
  border-style:solid;border-color:color-mix(in srgb,var(--violet) 45%,transparent);
  background:color-mix(in srgb,var(--violet) 13%,transparent);color:var(--violet);
}
.piecebtn .pl{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.piecebtn .pic{font-size:11px}
.piecebtn .pc{font-size:9px;opacity:.7}

.pkrow{
  display:flex;flex-direction:column;gap:1px;width:100%;text-align:left;
  padding:9px 12px;border:0;border-bottom:1px solid var(--border-soft);
  background:none;color:var(--text);font:inherit;cursor:pointer;
}
.pkrow:last-child{border-bottom:0}
.pkrow:hover{background:color-mix(in srgb,var(--brand) 8%,transparent)}
.pkrow.on{background:color-mix(in srgb,var(--brand) 13%,transparent)}
.pkrow.on .pkt{color:var(--brand)}
.pkrow.add .pkt{color:var(--brand)}
.pkrow.clear .pkt{color:var(--text-3)}
.pkrow.static{cursor:default;flex-direction:row;align-items:center;gap:10px}
.pkrow.static:hover{background:none}
.pkrow.static .pkt{flex:1}
.pkt{font-size:13.5px;font-weight:560}
.pkc{font-size:11.5px;color:var(--text-3)}
.pkempty{margin:0;padding:16px 12px;font-size:13px;color:var(--text-3);text-align:center}

/* ── calendar ── */
/* A month is a scanning surface, not a planner: square cells on a desktop-wide
   panel push the last week below the fold for no gain. */
.calwrap{padding:16px;max-width:520px}
.cal-head{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px}
.cal-m{font-size:14.5px;font-weight:640;letter-spacing:-.01em}
.cal-dow{
  display:grid;grid-template-columns:repeat(7,1fr);gap:4px;margin-bottom:5px;
  font-size:10.5px;font-weight:700;color:var(--text-3);text-align:center;letter-spacing:.05em;
}
.cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:4px}
.cal-cell{
  position:relative;height:44px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
  border:1px solid transparent;border-radius:var(--r-sm);
  background:none;color:var(--text-3);font:inherit;font-size:12px;cursor:default;
}
.cal-cell.out{visibility:hidden}
.cal-cell.today{border-color:var(--border);color:var(--text)}
.cal-cell.has{
  cursor:pointer;color:var(--text);background:var(--raised);
  border-color:var(--border);font-weight:600;
}
.cal-cell.has:hover{border-color:var(--brand);background:color-mix(in srgb,var(--brand) 12%,transparent)}
.cal-cell .cd{line-height:1}
.cdot{display:inline-block;width:5px;height:5px;border-radius:50%;background:var(--done)}
.cdot.draft{background:var(--wait)}
.cal-cell .cn{
  position:absolute;top:3px;right:4px;font-size:9.5px;color:var(--text-3);font-weight:600;
}
.cal-legend{
  margin:12px 0 0;display:flex;align-items:center;gap:6px;
  font-size:11.5px;color:var(--text-3);
}
.cal-legend .cdot{margin-left:6px}
.cal-legend .cdot:first-child{margin-left:0}

/* ============================================================
   The splitter
   ============================================================ */
.splitwrap{display:flex;flex-direction:column;gap:14px}

.sp-wave{
  position:relative;height:190px;border-radius:var(--r);
  background:var(--ground);border:1px solid var(--border);
  touch-action:none;cursor:crosshair;overflow:hidden;
  -webkit-user-select:none;user-select:none;
}
.sp-wave:focus-visible{border-color:var(--brand)}
.sp-wave.over-mark{cursor:ew-resize}
.sp-wave.dragging{cursor:grabbing}
.sp-wave canvas{display:block;width:100%;height:100%;position:absolute;inset:0}

.sp-marks{position:absolute;inset:0;pointer-events:none}
/* A gap the server found. Faint on purpose: it is a suggestion until the
   director turns it into a split. */
.sp-hint{
  position:absolute;top:0;bottom:26px;width:1px;
  background:repeating-linear-gradient(var(--text-3) 0 3px,transparent 3px 7px);
  opacity:.42;
}
.sp-mark{
  position:absolute;top:0;bottom:26px;width:2px;background:var(--brand);
  pointer-events:auto;cursor:ew-resize;
}
.sp-mark i{
  position:absolute;top:-1px;left:50%;transform:translateX(-50%);
  width:15px;height:15px;border-radius:0 0 8px 8px;background:var(--brand);
}
.sp-mark.sel{background:var(--text);box-shadow:0 0 0 1px color-mix(in srgb,var(--text) 30%,transparent)}
.sp-mark.sel i{background:var(--text)}

.sp-labels{position:absolute;left:0;right:0;bottom:0;height:26px;pointer-events:none}
.sp-label{
  position:absolute;bottom:0;height:26px;display:flex;align-items:center;
  padding:0 7px;pointer-events:auto;cursor:pointer;overflow:hidden;
  border-top:1px solid var(--border-soft);
  background:color-mix(in srgb,var(--surface) 88%,transparent);
}
.sp-label span{
  font-size:11px;font-weight:600;color:var(--text-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.sp-label.sel{background:color-mix(in srgb,var(--brand) 22%,transparent)}
.sp-label.sel span{color:var(--brand)}
.sp-label:hover span{color:var(--text)}

.sp-clock{display:flex;justify-content:space-between;font-size:12px;color:var(--text-2);margin-top:-8px}

.sp-bar{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
.sp-play{
  flex:none;width:44px;height:44px;display:grid;place-items:center;
  border:0;border-radius:50%;cursor:pointer;
  background:var(--brand);color:var(--on-brand);
}
.sp-play::before{
  content:"";width:0;height:0;margin-left:3px;
  border-style:solid;border-width:8px 0 8px 13px;
  border-color:transparent transparent transparent currentColor;
}
.sp-play.on::before{
  margin:0;width:12px;height:14px;border:0;
  background:linear-gradient(to right,currentColor 0 4px,transparent 4px 8px,currentColor 8px 12px);
}
.sp-play:hover{filter:brightness(1.1)}

/* ── segment cards ── */
.segstrip{display:flex;flex-direction:column;gap:8px}
.segcard{
  padding:11px 13px;border-radius:var(--r-sm);cursor:pointer;
  background:var(--surface);border:1px solid var(--border);
  display:flex;flex-direction:column;gap:8px;
  transition:border-color .13s ease,background .13s ease;
}
.segcard:hover{background:var(--raised)}
.segcard.sel{border-color:var(--brand);background:color-mix(in srgb,var(--brand) 8%,transparent)}
.sc-head{display:flex;align-items:center;gap:9px}
.sc-n{
  flex:none;width:20px;height:20px;display:grid;place-items:center;border-radius:5px;
  background:var(--raised);color:var(--text-3);font-size:11px;font-weight:700;
}
.segcard.sel .sc-n{background:var(--brand);color:var(--on-brand)}
.sc-title{
  flex:1;min-width:0;background:transparent;border:0;border-bottom:1px solid transparent;
  color:var(--text);font:inherit;font-size:14px;font-weight:600;padding:3px 0;
}
.sc-title::placeholder{color:var(--text-3);font-weight:500}
.sc-title:hover{border-bottom-color:var(--border)}
.sc-title:focus{outline:none;border-bottom-color:var(--brand)}
.sc-len{flex:none;font-size:12px;color:var(--text-2)}
.sc-foot{display:flex;align-items:center;gap:9px;flex-wrap:wrap;padding-left:29px}
.sc-range{font-size:11px;color:var(--text-3)}
.sc-edge{display:flex;align-items:center;gap:4px;margin-left:auto;flex-wrap:wrap}
.nudge{
  padding:3px 8px;border-radius:6px;cursor:pointer;
  border:1px solid var(--border);background:transparent;color:var(--text-3);
  font:inherit;font-size:11px;font-weight:600;
}
.nudge:hover{color:var(--text);border-color:var(--text-3)}
.nudge.del:hover{color:var(--late);border-color:color-mix(in srgb,var(--late) 50%,transparent)}

/* ── phone ── */
@media (max-width:720px){
  .rgrid{grid-template-columns:1fr}
  .rbar{gap:8px}
  .rbar-right{margin-left:0;width:100%}
  .rsel,.rsearch{flex:1;min-width:0}
  .dhead{gap:10px}
  .dacts{width:100%}
  .dacts .btn{flex:1}
  .sp-wave{height:150px}
  .sc-foot{padding-left:0}
  .sc-edge{margin-left:0;width:100%}
  .nudge{flex:1;text-align:center}
  .pubbar{position:sticky;bottom:calc(66px + env(safe-area-inset-bottom))}
  .pubbar .btn{width:100%}
  .calwrap{max-width:none}
  .cal-cell{height:40px}
}

/* ── the player dock against the phone tab bar ──
   The dock is fixed to the bottom of the viewport and the tab bar lives there
   too, so on a phone the collapsed dock rides above it. Expanded, the sheet
   owns the screen and covers it. */
@media (max-width:820px){
  .up{bottom:calc(56px + env(safe-area-inset-bottom))}
  .up.expanded{bottom:0}
  .up-mini{padding-bottom:9px}
  body.up-docked .main{padding-bottom:calc(150px + env(safe-area-inset-bottom))}
}
@media (min-width:821px){
  body.up-docked .main{padding-bottom:96px}
}

/* ============================================================
   Student cockpit (Home)
   ------------------------------------------------------------
   Two columns on a desktop: what a student owes on the left,
   what they can look at on the right. One column on a phone,
   in that same order, so the first thing under the numbers is
   still the button that records the next thing due.
   ============================================================ */
.dash{display:grid;grid-template-columns:minmax(0,1.32fr) minmax(0,1fr);gap:14px;align-items:start;margin-top:14px}
.dash-col{display:flex;flex-direction:column;gap:14px;min-width:0}
@media (max-width:1040px){ .dash{grid-template-columns:minmax(0,1fr)} }

/* Four tiles, and four is the point — auto-fit lands 3 + 1 at the widths
   between the two-column dash and the phone, which reads as a mistake. */
.cards.tiles4{grid-template-columns:repeat(4,minmax(0,1fr))}
@media (max-width:1040px){ .cards.tiles4{grid-template-columns:1fr 1fr} }

.card.violet::before{background:var(--violet)}
/* the greeting line: one urgent true thing, in the colour it deserves */
.hl-late{color:var(--late);font-weight:640}
.hl-ok{color:var(--done);font-weight:640}

/* ── Up next ─────────────────────────────────────────────────
   The work cards live inside the panel now, so they drop their own
   box and become rows: one border round the group instead of one per
   card, and a left bar that keeps each card's urgency without painting
   the whole panel amber. */
.upnext .uplist > .wcard{
  background:transparent;border:0;border-radius:0;
  border-top:1px solid var(--border);
}
.upnext .uplist > .wcard:first-child{border-top:0}
.upnext .uplist > .wcard.due{box-shadow:inset 3px 0 0 var(--wait)}
.upnext .uplist > .wcard.late{box-shadow:inset 3px 0 0 var(--late)}
.upnext .rows{border-top:1px solid var(--border)}
.upnext .row.golink.mut .nm{color:var(--text-3);font-weight:560}
.upnext .row.golink.mut .rt{color:var(--text-3)}
@media (max-width:640px){ .upnext .uplist > .wcard{padding:13px} }

/* a panel with nothing in it yet — a sentence, not a 200px empty state */
.mini{padding:14px 15px;font-size:12.5px;line-height:1.55;color:var(--text-3)}
.row.golink .nm{color:var(--brand);font-weight:620}
.row.golink .rt{color:var(--brand)}
.wcard.done-card{border-color:color-mix(in srgb,var(--done) 34%,transparent)}
.wcard.done-card .t{color:var(--done)}

/* ── the grade chart ─────────────────────────────────────────
   One column per graded assignment, oldest at the left. The
   shape over a term is the point — the individual numbers are
   one tap away in Feedback — so only the newest column carries
   its value and the rest are read against the average line. */
/* The plot fills its panel rather than sizing itself off the number of
   bars: a four-bar chart hugging the left edge of a wide panel read as
   a thumbnail someone forgot to finish. Bars stay slim (.gfill caps at
   24px), so filling the width spreads them instead of fattening them. */
.gwrap{padding-top:20px;width:100%}
.gplot{
  position:relative;display:flex;align-items:flex-end;gap:6px;
  height:118px;border-bottom:1px solid var(--border);
}
.gcol{flex:1 1 0;min-width:0;height:100%;display:flex;align-items:flex-end;justify-content:center}
.gfill{
  position:relative;display:block;width:100%;max-width:24px;
  border-radius:4px 4px 0 0;                     /* rounded at the data end, square on the baseline */
  background:color-mix(in srgb,var(--brand) 40%,transparent);
}
.gcol.now .gfill{background:var(--brand)}
/* the same 40% wash is a mark on white in the light theme — barely a mark at all */
:root[data-theme="light"] .gfill{background:color-mix(in srgb,var(--brand) 58%,transparent)}
:root[data-theme="light"] .gcol.now .gfill{background:var(--brand)}
.gval{
  position:absolute;left:50%;transform:translateX(-50%);bottom:calc(100% + 5px);
  font-size:11px;font-weight:640;color:var(--text-2);white-space:nowrap;
}
.gavg{
  position:absolute;left:0;right:0;height:0;pointer-events:none;
  border-top:1px solid color-mix(in srgb,var(--text-3) 65%,transparent);
}
.gavg span{
  position:absolute;left:0;bottom:2px;padding:0 4px;border-radius:4px;
  background:var(--surface);font-size:9.5px;font-weight:700;letter-spacing:.03em;color:var(--text-3);
}
.gaxis{display:flex;gap:6px;margin-top:7px}
.gaxis span{
  flex:1 1 0;min-width:0;text-align:center;font-size:9.5px;color:var(--text-3);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.gnote{margin:12px 0 0;font-size:12px;line-height:1.55;color:var(--text-2)}
.gnote b{color:var(--text);font-weight:640}

@media (max-width:640px){
  .gplot{height:96px}
  .gnote{font-size:11.5px}
}

/* ── setup guide ──
   Three steps between a new workspace and a grade in a student's hand. It
   sits above the numbers on Today because on the day it matters there are no
   numbers yet, and it leaves for good — for this director, on every device —
   the moment they either finish it or close it.

   The brandmark is three nested arcs in violet, sky and teal. The steps take
   those colours in that order and the hairline across the top fills through
   the same gradient, so progress is legible from across a rehearsal room
   before a word of it is read. */
#guide-slot:empty{display:none}

.guide{
  position:relative;overflow:hidden;
  background:var(--surface);border:1px solid var(--border);border-radius:var(--r);
}
/* The rail rides the card's own top edge rather than sitting inside it: a
   progress bar with padding around it reads as a control, and this is a
   report. clip-path rather than width, so the gradient's three colours stay
   where they are as it fills instead of sliding along under the mask. */
.gbar{position:absolute;top:0;left:0;right:0;height:2px;background:var(--border-soft)}
.gbar::after{
  content:"";position:absolute;inset:0;
  background:linear-gradient(90deg,var(--violet),var(--sky),var(--brand));
  clip-path:inset(0 calc(100% - var(--p,0%)) 0 0);
  transition:clip-path .55s cubic-bezier(.4,0,.2,1);
}

.ghead{display:flex;align-items:flex-start;gap:12px;padding:15px 15px 10px}
.gtitle{flex:1;min-width:0}
.gtitle h3{margin:0;font-size:15px;font-weight:620;letter-spacing:-.014em}
.gtitle p{margin:3px 0 0;font-size:12.5px;color:var(--text-2);line-height:1.45;text-wrap:pretty}
.gcount{white-space:nowrap;color:var(--text-3)}
/* Same trick as the sheet's close button: 36px of target, no extra height. */
.ghead .iconbtn{margin:-6px -6px 0 0;flex:0 0 auto}

.gsteps{list-style:none;margin:0;padding:0 15px 7px}
.gstep{
  position:relative;display:grid;gap:2px 12px;
  grid-template-columns:22px minmax(0,1fr) auto;
  grid-template-areas:"dot txt act";
  align-items:start;padding:9px 0;
}
.gact{grid-area:act;align-self:center;display:flex;gap:7px;flex-wrap:wrap;justify-content:flex-end}

/* The thread is what makes three rows read as one sequence rather than three
   unrelated suggestions. Each row draws its own full-height segment in the
   dot column and the dots, being opaque and a layer up, punch through it —
   which is the only version of this that survives rows of different heights.
   The first and last are trimmed back to their own dot's centre: 9px of row
   padding plus half of a 22px dot. */
.gstep::before{content:"";position:absolute;left:10.5px;top:0;bottom:0;width:1px;background:var(--border)}
.gstep:first-child::before{top:20px}
.gstep:last-child::before{bottom:calc(100% - 20px)}
.gsteps > .gstep:only-child::before{display:none}

.gdot{
  grid-area:dot;position:relative;z-index:1;
  width:22px;height:22px;border-radius:50%;display:grid;place-items:center;
  border:1.5px solid var(--border);background:var(--surface);
  transition:background .22s ease,border-color .22s ease,box-shadow .22s ease;
}
.gn{font-size:10.5px;font-weight:640;color:var(--text-3);line-height:1}
.gstep.now .gdot{
  border-color:color-mix(in srgb,var(--brand) 60%,var(--border));
  box-shadow:0 0 0 3px color-mix(in srgb,var(--brand) 13%,transparent);
}
.gstep.now .gn{color:var(--brand)}
.gstep.ok .gdot{border-color:transparent}
.gstep[data-tone="v"].ok .gdot{background:var(--violet)}
.gstep[data-tone="s"].ok .gdot{background:var(--sky)}
.gstep[data-tone="b"].ok .gdot{background:var(--brand)}
/* The three fills are light in the dark theme and dark in the light one, so
   the one ink that contrasts with all six is the page behind them. */
.gtick{width:12px;height:12px;fill:none;stroke:var(--ground);stroke-width:2.7;
  stroke-linecap:round;stroke-linejoin:round}

/* The text block is shared with the student's welcome card, whose rows are a
   plain two-column grid with no named areas — so the placement stays with the
   step that needs it rather than riding along on the typography. */
.gtxt{display:flex;flex-direction:column;gap:1px;min-width:0}
.gstep .gtxt{grid-area:txt}
.gnm{font-size:13.5px;font-weight:570;letter-spacing:-.006em}
/* Capped on the same principle as an empty state's copy: the step column is
   as wide as the window on a desktop, and a hundred-character line is read
   twice or not at all. */
.gmeta{font-size:12px;color:var(--text-2);line-height:1.45;text-wrap:pretty;max-width:68ch}
.gstep.later .gnm{color:var(--text-2)}
.gstep.later .gmeta{color:var(--text-3)}
/* A finished step keeps its place — seeing it is the reward for doing it —
   but it stops competing with the one still open. */
.gstep.ok .gnm{color:var(--text-2);font-weight:540}
.gstep.ok .gmeta{color:var(--text-3)}
/* Only the step you are on gets a filled button. The ones after it stay
   reachable, because a director who wants to jump ahead should not be
   stopped, but nothing on the card competes with the single next thing. */
.gstep.later .btn.primary{background:transparent;border-color:var(--border);color:var(--text-2)}
.gstep.later .btn.primary:hover:not(:disabled){filter:none;background:var(--raised)}

.guide.gone{
  height:0 !important;opacity:0;border-color:transparent;
  transition:height .24s cubic-bezier(.4,0,1,1),opacity .16s ease,border-color .24s ease;
}

@media (max-width:640px){
  .ghead{padding:14px 13px 8px;gap:9px}
  .gtitle h3{font-size:14.5px}
  .gsteps{padding:0 13px 9px}
  /* A 36px button and a two-line explanation cannot share 320px, so the
     action drops beneath the text — indented to the text's own column, which
     keeps it inside the step it belongs to instead of floating under all
     three. */
  .gstep{grid-template-columns:22px minmax(0,1fr);grid-template-areas:"dot txt" ". act";row-gap:9px}
  .gact{align-self:auto;justify-content:stretch;padding-bottom:1px}
  .gact .btn{flex:1 1 0;min-width:0}
  /* On a phone only the step you are on explains itself. Three explanations
     and three sets of buttons is half a screen spent on work that cannot be
     started yet; the steps ahead keep their number and their name, which is
     all that is needed to see what is coming. */
  .gstep.later .gmeta,.gstep.later .gact{display:none}
  /* Trailing the count after a sentence that wraps left it orphaned mid-line
     against the fold. Its own line is both shorter to read and easier to find. */
  .gcount{display:block;margin-top:2px}
}

/* ── student welcome ──
   The director's card counts down to a finish line; this one has nothing to
   count. A student has no workspace to build, so there are no numbers, no
   progress and nothing to tick — three facts, each carrying one of the
   brandmark's arc colours so the two cards read as the same family without
   this one pretending to be a checklist. */
.welc{
  position:relative;overflow:hidden;
  background:var(--surface);border:1px solid var(--border);border-radius:var(--r);
}
/* Full width and static: the director's rail fills because there is progress
   to report, and a bar at a permanent 100% here would promise one. */
.welc-bar{
  position:absolute;top:0;left:0;right:0;height:2px;
  background:linear-gradient(90deg,var(--violet),var(--sky),var(--brand));
}
.welc-lines{list-style:none;margin:0;padding:0 15px 13px;display:flex;flex-direction:column;gap:11px}
.wline{display:grid;grid-template-columns:26px minmax(0,1fr);gap:2px 12px;align-items:start}
/* A tinted disc, not a ring with a number in it — nothing here is step one. */
.wico{
  width:26px;height:26px;border-radius:8px;display:grid;place-items:center;margin-top:1px;
}
.wico svg{width:15px;height:15px;fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.wline[data-tone="v"] .wico{background:color-mix(in srgb,var(--violet) 15%,transparent)}
.wline[data-tone="v"] .wico svg{stroke:var(--violet)}
.wline[data-tone="s"] .wico{background:color-mix(in srgb,var(--sky) 15%,transparent)}
.wline[data-tone="s"] .wico svg{stroke:var(--sky)}
.wline[data-tone="b"] .wico{background:color-mix(in srgb,var(--brand) 15%,transparent)}
.wline[data-tone="b"] .wico svg{stroke:var(--brand)}
/* The lead-in carries the whole line for anyone skimming, so it gets the
   weight the director's step titles get. */
.wline .gnm{color:var(--text)}

.welc.gone{
  height:0 !important;opacity:0;border-color:transparent;
  transition:height .24s cubic-bezier(.4,0,1,1),opacity .16s ease,border-color .24s ease;
}

@media (max-width:640px){
  .welc .ghead{padding:14px 13px 8px;gap:9px}
  .welc-lines{padding:0 13px 12px;gap:10px}
  .wline{grid-template-columns:24px minmax(0,1fr);gap:2px 10px}
  .wico{width:24px;height:24px;border-radius:7px}
}

/* ── recorder, before the first take ──
   Sits above the button rather than under it: it is the context for the
   decision to press, and the two lines already below the button are about
   what happens once you have. */
.rec-first{
  margin:0 auto 12px;max-width:34ch;text-align:center;
  font-size:12.5px;line-height:1.5;color:var(--text-2);
}
