/* ============================================================
   INTELLICAMP — feature page graphics
   Loaded after service.css on the Features pages.

   Two components:
     .bp     the marks blueprint — what you set
     .paper  the generated paper — what you get

   They sit side by side so the page shows input and output in
   one view. The paper sheet keeps its own light palette in every
   theme, because paper is paper.
   ============================================================ */

.io {
  display: grid;
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1fr);
  gap: clamp(1.4rem, 3.5vw, 3rem);
  align-items: start;
}
@media (max-width: 960px) { .io { grid-template-columns: 1fr; } }

/* ---------- 1. The blueprint ---------- */

.bp {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--ink-2);
  padding: clamp(1.1rem, 2.5vw, 1.6rem);
  display: flex;
  flex-direction: column;
  gap: 1.35rem;
  position: sticky;
  top: calc(var(--header-h) + 1.5rem);
}
@media (max-width: 960px) { .bp { position: static; } }

.bp__head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.bp__head b {
  font-family: var(--font-display); font-size: 0.94rem; font-weight: 700;
  color: var(--paper); letter-spacing: -0.01em;
}
.bp__head span { font-size: 0.79rem; color: var(--muted-2); letter-spacing: 0.06em; text-transform: uppercase; }

.bp__group { display: flex; flex-direction: column; gap: 0.55rem; }
.bp__label {
  font-size: 0.76rem; font-weight: 700; letter-spacing: 0.11em;
  text-transform: uppercase; color: var(--muted);
}

/* chapter chips — the scope you pick */
.bp__chips { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.bp__chip {
  font-size: 0.72rem; font-weight: 600;
  padding: 0.24rem 0.6rem; border-radius: 999px;
  border: 1px solid var(--line); color: var(--muted-2);
  transition: all 0.4s var(--ease);
}
.bp__chip.is-on {
  border-color: var(--accent-ink); color: var(--accent-ink);
  background: rgba(var(--accent-rgb), 0.09);
}

/* stacked marks bar */
.bp__bar {
  display: flex; height: 26px; border-radius: 7px; overflow: hidden;
  border: 1px solid var(--line-soft); background: var(--ink-3);
}
.bp__seg {
  width: 0; background: var(--sc);
  transition: width 1.1s var(--ease) var(--d, 0s);
}
.is-on .bp__seg { width: var(--w); }

/* legend rows: type · count · marks */
.bp__legend { display: flex; flex-direction: column; gap: 0.3rem; }
.bp__row {
  display: grid; grid-template-columns: 10px 1fr auto auto; gap: 0.55rem;
  align-items: center; font-size: 0.75rem;
  opacity: 0; transform: translateX(-6px);
  transition: opacity 0.4s ease var(--d), transform 0.4s var(--ease) var(--d);
}
.is-on .bp__row { opacity: 1; transform: none; }
.bp__row i { width: 10px; height: 10px; border-radius: 3px; background: var(--sc); }
.bp__row span { color: var(--text-2); }
.bp__row em { font-style: normal; color: var(--muted-2); font-variant-numeric: tabular-nums; }
.bp__row b { color: var(--paper); font-variant-numeric: tabular-nums; font-weight: 600; }

.bp__total {
  display: flex; align-items: baseline; justify-content: space-between;
  padding-top: 0.9rem; border-top: 1px solid var(--line-soft);
}
.bp__total span { font-size: 0.72rem; color: var(--muted); }
.bp__total b {
  font-family: var(--font-display); font-size: 1.5rem; font-weight: 700;
  color: var(--accent-ink); font-variant-numeric: tabular-nums; letter-spacing: -0.02em;
}

/* ---------- 2. The generated paper ---------- */

.paper {
  position: relative;
  border-radius: var(--radius);
  /* its own palette — a sheet of paper doesn't follow the site theme */
  background: #f7f6f1;
  color: #1a1c20;
  box-shadow: 0 34px 70px -34px rgba(0, 0, 0, 0.75);
  padding: clamp(1.1rem, 2.6vw, 1.9rem);
  font-size: 0.78rem;
  line-height: 1.5;
  overflow: hidden;
}

/* faint ruled margin, like a real answer sheet */
.paper::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 3px; background: linear-gradient(180deg, var(--accent), var(--p5));
}

.paper__head { text-align: center; border-bottom: 1.5px solid #1a1c20; padding-bottom: 0.7rem; }
.paper__school {
  font-family: var(--font-display); font-size: 0.98rem; font-weight: 700;
  letter-spacing: 0.02em; margin: 0;
}
.paper__exam { font-size: 0.75rem; color: #55595f; margin: 0.15rem 0 0.5rem; }
.paper__meta {
  display: flex; justify-content: space-between; gap: 0.6rem;
  font-size: 0.7rem; font-weight: 600; flex-wrap: wrap;
}
.paper__meta span { color: #33363b; }

.paper__note {
  font-size: 0.7rem; color: #6a6e75; font-style: italic;
  border-bottom: 1px dashed #c9c6bb; padding: 0.55rem 0; margin-bottom: 0.4rem;
}

.paper__sec {
  display: flex; align-items: baseline; justify-content: space-between; gap: 0.6rem;
  margin: 0.85rem 0 0.45rem;
  font-family: var(--font-display); font-size: 0.75rem; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase;
}
.paper__sec span { color: #1a1c20; }
.paper__sec em {
  font-style: normal; font-size: 0.7rem; font-weight: 600;
  color: #6a6e75; letter-spacing: 0.02em; text-transform: none; white-space: nowrap;
}

/* each question fades up as the paper "writes itself" */
.paper__q {
  display: grid; grid-template-columns: 1.15rem 1fr auto; gap: 0.4rem;
  padding: 0.32rem 0;
  opacity: 0; transform: translateY(6px);
  transition: opacity 0.45s ease var(--d), transform 0.45s var(--ease) var(--d);
}
.is-on .paper__q { opacity: 1; transform: none; }
.paper__n { font-weight: 700; color: #55595f; font-variant-numeric: tabular-nums; }
.paper__mk {
  font-size: 0.7rem; font-weight: 700; color: #6a6e75;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.paper__opts {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.1rem 0.7rem;
  margin-top: 0.22rem; font-size: 0.75rem; color: #44484e;
}
.paper__opts b { font-weight: 600; color: #1a1c20; }

/* the chapter/difficulty tag the generator attaches to every question */
.paper__tag {
  display: inline-block; margin-top: 0.25rem;
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.04em;
  padding: 0.05rem 0.35rem; border-radius: 4px;
  background: rgba(var(--accent-rgb), 0.30); color: #33440a;
}

.paper__more {
  text-align: center; font-size: 0.72rem; color: #8a8e95;
  border-top: 1px dashed #c9c6bb; margin-top: 0.7rem; padding-top: 0.6rem;
}

/* floating chip: the generation time */
.paper__stamp {
  position: absolute; top: -12px; right: -8px;
  display: flex; align-items: center; gap: 0.3rem;
  font-size: 0.72rem; font-weight: 700;
  padding: 0.3rem 0.55rem; border-radius: 8px;
  background: var(--accent); color: var(--on-accent);
  box-shadow: 0 12px 26px -12px rgba(0, 0, 0, 0.8);
}
.paper__stamp i {
  width: 5px; height: 5px; border-radius: 50%; background: currentColor;
  animation: paper-pulse 1.8s ease-in-out infinite;
}
@keyframes paper-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }

/* ---------- 3. Chapter coverage strip ---------- */

.cov { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 0.3rem; }
.cov__row { display: grid; grid-template-columns: 4.6rem 1fr 2.2rem; gap: 0.6rem; align-items: center; }
.cov__name { font-size: 0.72rem; color: var(--muted); white-space: nowrap; }
.cov__track { height: 7px; border-radius: 4px; background: var(--ink-3); overflow: hidden; }
.cov__fill {
  display: block; height: 100%; width: 0; border-radius: 4px;
  background: linear-gradient(90deg, var(--accent-ink), var(--accent));
  transition: width 1.2s var(--ease) var(--d, 0s);
}
.is-on .cov__fill { width: var(--w); }
.cov__val {
  font-size: 0.7rem; color: var(--text-2); text-align: right;
  font-variant-numeric: tabular-nums; font-weight: 600;
}

/* ---------- 4. Reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  .bp__seg { width: var(--w); transition: none; }
  .bp__row { opacity: 1; transform: none; transition: none; }
  .paper__q { opacity: 1; transform: none; transition: none; }
  .cov__fill { width: var(--w); transition: none; }
  .paper__stamp i { animation: none; }
}

/* ============================================================
   5. Auto-scrolling track
   Turns a grid of 4–6 items into a snapping carousel that
   advances on its own. It only becomes a carousel when the
   items genuinely overflow — feature.js checks and adds
   .is-track — so a wide desktop keeps the static grid.
   ============================================================ */

.is-track {
  display: flex !important;
  flex-wrap: nowrap;
  gap: 1rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  padding-bottom: 0.9rem;
  /* fade the cut edges so the row reads as continuing */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 3%, #000 97%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 3%, #000 97%, transparent);
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}
.is-track::-webkit-scrollbar { height: 4px; }
.is-track::-webkit-scrollbar-track { background: transparent; }
.is-track::-webkit-scrollbar-thumb { background: var(--line); border-radius: 4px; }
.is-track:hover::-webkit-scrollbar-thumb { background: var(--accent-ink); }

.is-track > * {
  flex: 0 0 clamp(240px, 27vw, 320px);
  scroll-snap-align: start;
  min-width: 0;
}

/* the item currently under the playhead lifts and brightens */
.is-track > .is-active { transform: translateY(-5px); }
.is-track > .is-active .grd__dot {
  background: var(--pc, var(--accent));
  color: var(--ink);
  box-shadow: 0 0 0 6px rgba(var(--ink-3-rgb), 0.6), 0 10px 26px -8px var(--pc, var(--accent));
}

/* progress pips under an auto-scrolling track */
.track-dots { display: flex; justify-content: center; gap: 7px; margin-top: 1.1rem; }
.track-dots button {
  width: 7px; height: 7px; padding: 0; border: 0; border-radius: 50%;
  background: var(--line); cursor: pointer;
  transition: background 0.35s ease, width 0.35s var(--ease);
}
.track-dots button[aria-selected="true"] {
  background: var(--accent-ink); width: 20px; border-radius: 999px;
}
.track-dots button:focus-visible { outline: 2px solid var(--accent-ink); outline-offset: 3px; }

/* a thin bar showing how long until the track advances */
.track-timer {
  height: 2px; border-radius: 2px; background: var(--line-soft);
  margin-top: 0.9rem; overflow: hidden; position: relative;
}
.track-timer i {
  position: absolute; inset: 0; transform-origin: 0 50%; transform: scaleX(0);
  background: linear-gradient(90deg, var(--accent-ink), var(--accent));
}
.track-timer.is-running i { animation: track-tick var(--dur, 4s) linear infinite; }
@keyframes track-tick { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* ============================================================
   6. Ambient section backgrounds
   Slow-drifting colour fields behind a section. Purely
   decorative, sits under the content, never intercepts a
   pointer. Hue comes from --fx so each section differs.
   ============================================================ */

.fx { position: relative; isolation: isolate; overflow: hidden; }

.fx::before,
.fx::after {
  content: "";
  position: absolute;
  z-index: -1;
  border-radius: 50%;
  pointer-events: none;
  filter: blur(70px);
  opacity: 0.5;
}

.fx::before {
  width: 46vw; height: 46vw; max-width: 620px; max-height: 620px;
  top: -18%; left: -12%;
  background: radial-gradient(circle, rgba(var(--fx, var(--accent-rgb)), 0.30), transparent 68%);
  animation: fx-drift-a 26s ease-in-out infinite alternate;
}

.fx::after {
  width: 38vw; height: 38vw; max-width: 520px; max-height: 520px;
  bottom: -20%; right: -10%;
  background: radial-gradient(circle, rgba(var(--fx-2, var(--violet-rgb)), 0.26), transparent 68%);
  animation: fx-drift-b 32s ease-in-out infinite alternate;
}

@keyframes fx-drift-a {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(8vw, 6vh, 0) scale(1.18); }
}
@keyframes fx-drift-b {
  from { transform: translate3d(0, 0, 0) scale(1.1); }
  to   { transform: translate3d(-7vw, -5vh, 0) scale(0.92); }
}

/* a hairline that catches the eye where two sections meet */
.fx-seam { position: relative; }
.fx-seam::before {
  content: "";
  position: absolute; top: 0; left: 12%; right: 12%; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(var(--accent-rgb), 0.45), transparent);
}

/* ============================================================
   7. Reduced motion
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
  .is-track { scroll-behavior: auto; }
  .is-track > .is-active { transform: none; }
  .track-timer.is-running i { animation: none; transform: scaleX(1); }
  .fx::before, .fx::after { animation: none; }
}

/* ============================================================
   8. Script → score
   The AI's working, shown beside the student's answer. Pairs
   with .paper on the left; this is the right-hand panel.
   ============================================================ */

.mark {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--ink-2);
  padding: clamp(1.1rem, 2.5vw, 1.6rem);
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}

.mark__head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.mark__head b {
  font-family: var(--font-display); font-size: 0.94rem; font-weight: 700;
  color: var(--paper); letter-spacing: -0.01em;
}
.mark__conf {
  display: flex; align-items: center; gap: 0.35rem;
  font-size: 0.78rem; font-weight: 700; letter-spacing: 0.04em;
  padding: 0.22rem 0.5rem; border-radius: 999px;
  background: rgba(var(--accent-rgb), 0.12);
  color: var(--accent-ink); border: 1px solid rgba(var(--accent-rgb), 0.3);
}
.mark__conf i {
  width: 5px; height: 5px; border-radius: 50%; background: currentColor;
  animation: paper-pulse 1.8s ease-in-out infinite;
}

.mark__label {
  font-size: 0.76rem; font-weight: 700; letter-spacing: 0.11em;
  text-transform: uppercase; color: var(--muted);
}

/* the expected-points checklist */
.mark__pts { display: flex; flex-direction: column; gap: 0.4rem; }

.mark__pt {
  display: grid; grid-template-columns: 20px 1fr auto; gap: 0.6rem; align-items: start;
  padding: 0.5rem 0.6rem; border-radius: var(--radius);
  border: 1px solid var(--line-soft);
  background: rgba(var(--ink-rgb), 0.45);
  opacity: 0; transform: translateX(-8px);
  transition: opacity 0.45s ease var(--d), transform 0.45s var(--ease) var(--d);
}
.is-on .mark__pt { opacity: 1; transform: none; }

.mark__tick {
  width: 20px; height: 20px; border-radius: 50%;
  display: grid; place-items: center; flex: none;
  background: var(--line); color: var(--muted-2);
}
.mark__pt--hit .mark__tick { background: var(--accent); color: var(--on-accent); }
.mark__pt--miss .mark__tick { background: rgba(var(--p7-rgb), 0.22); color: var(--p7); }
.mark__pt--part .mark__tick { background: rgba(var(--p8-rgb), 0.25); color: var(--p8); }

.mark__pt p { margin: 0; font-size: 0.76rem; line-height: 1.45; color: var(--text-2); }
.mark__pt em {
  display: block; font-style: normal; margin-top: 0.15rem;
  font-size: 0.79rem; color: var(--muted-2);
}
.mark__pt b {
  font-family: var(--font-display); font-size: 0.76rem; font-weight: 700;
  color: var(--paper); font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* running total */
.mark__total {
  display: flex; align-items: center; gap: 0.8rem;
  padding-top: 0.9rem; border-top: 1px solid var(--line-soft);
}
.mark__score {
  font-family: var(--font-display); font-size: 1.7rem; font-weight: 700;
  color: var(--accent-ink); letter-spacing: -0.02em; font-variant-numeric: tabular-nums;
  line-height: 1;
}
.mark__of { font-size: 0.78rem; color: var(--muted); }
.mark__meter {
  flex: 1; height: 7px; border-radius: 4px; background: var(--ink-3); overflow: hidden;
}
.mark__meter i {
  display: block; height: 100%; width: 0; border-radius: 4px;
  background: linear-gradient(90deg, var(--accent-ink), var(--accent));
  transition: width 1.1s var(--ease) var(--d, 1.4s);
}
.is-on .mark__meter i { width: var(--w); }

/* teacher override */
.mark__over {
  display: flex; align-items: center; justify-content: space-between; gap: 0.7rem;
  flex-wrap: wrap;
  padding: 0.65rem 0.75rem; border-radius: var(--radius);
  border: 1px dashed var(--line);
  background: rgba(var(--ink-rgb), 0.4);
}
.mark__over span { font-size: 0.72rem; color: var(--muted); }
.mark__btns { display: flex; gap: 0.35rem; }
.mark__btn {
  font: inherit; font-size: 0.7rem; font-weight: 600;
  padding: 0.25rem 0.6rem; border-radius: 7px;
  border: 1px solid var(--line); background: none; color: var(--text-2);
  cursor: default; transition: all 0.3s var(--ease);
}
.mark__btn--go { border-color: var(--accent-ink); color: var(--accent-ink); }

@media (prefers-reduced-motion: reduce) {
  .mark__pt { opacity: 1; transform: none; transition: none; }
  .mark__meter i { width: var(--w); transition: none; }
  .mark__conf i { animation: none; }
}

/* ============================================================
   9. Hero banners, per feature page
   Same slot and parallax behaviour as .sbn__eval in service.css,
   holding a picture specific to the page it sits on.
   ============================================================ */

.sbn__scr,
.sbn__draft {
  position: absolute;
  top: 50%;
  right: 1.5%;
  width: min(31%, 460px);
  translate: calc(var(--px) * 22px) calc(-50% + var(--py) * 20px);
  transition: translate 1.3s var(--ease);
  overflow: visible;
  mask-image: linear-gradient(90deg, transparent 0, #000 22%);
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 22%);
}

@media (max-width: 1279px) {
  .sbn__scr, .sbn__draft { display: none; }
}

/* ---- Instant AI Evaluation: a script being marked ---- */

/* the written lines of a candidate's answer */
.sbn__line {
  fill: var(--line);
  opacity: 0;
  transform-box: fill-box;
  transform-origin: left center;
  animation: sbn-line 0.55s var(--ease) forwards;
  animation-delay: calc(var(--d, 0) * 0.12s);
}
@keyframes sbn-line {
  from { opacity: 0; scale: 0 1; }
  to   { opacity: 1; scale: 1 1; }
}

/* the tick, half-mark or cross the evaluator leaves beside each point */
.sbn__mk {
  fill: none;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  /* drawn on, then held, then cleared so the pass repeats forever */
  animation: sbn-mk 7s var(--ease) infinite;
  animation-delay: calc(1.1s + var(--d, 0) * 0.4s);
}
.sbn__mk--hit  { stroke: var(--deco); }
.sbn__mk--part { stroke: var(--p8); }
.sbn__mk--miss { stroke: var(--coral); }

@keyframes sbn-mk {
  0%        { opacity: 0; scale: 0.3; rotate: -18deg; }
  7%        { opacity: 1; scale: 1;   rotate: 0deg; }
  72%       { opacity: 1; scale: 1; }
  82%, 100% { opacity: 0; scale: 1; }
}

/* running total in the corner of the script */
.sbn__tot {
  fill: rgba(var(--deco-rgb), 0.14);
  stroke: rgba(var(--deco-rgb), 0.45);
  stroke-width: 1.2;
}
.sbn__totn {
  fill: var(--deco);
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 700;
  opacity: 0;
  animation: sbn-tot 7s var(--ease) 3.2s infinite;
}
@keyframes sbn-tot {
  0%, 4%    { opacity: 0; }
  12%       { opacity: 1; }
  72%       { opacity: 1; }
  82%, 100% { opacity: 0; }
}

/* a marking sweep passing down the script, forever */
.sbn__sweep {
  fill: rgba(var(--deco-rgb), 0.10);
  animation: sbn-sweep 7s var(--ease) infinite;
}
@keyframes sbn-sweep {
  0%   { transform: translateY(-10px); opacity: 0; }
  6%   { opacity: 1; }
  60%  { transform: translateY(120px); opacity: 1; }
  70%, 100% { transform: translateY(120px); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .sbn__line, .sbn__mk, .sbn__totn, .sbn__sweep { animation: none; opacity: 1; }
  .sbn__sweep { opacity: 0; }
}

/* ---- AI Question Paper Generator: a sheet filling itself ---- */

.sbn__sec {
  fill: rgba(var(--deco-rgb), 0.55);
  opacity: 0;
  transform-box: fill-box;
  transform-origin: left center;
  animation: sbn-line 0.5s var(--ease) forwards;
  animation-delay: calc(var(--d, 0) * 0.12s);
}

/* the marks column arriving after its question */
.sbn__mnum {
  fill: var(--muted-2);
  font-family: var(--font-display);
  font-size: 9px;
  font-weight: 700;
  opacity: 0;
  animation: sbn-num 0.5s var(--ease) forwards;
  animation-delay: calc(0.25s + var(--d, 0) * 0.12s);
}
@keyframes sbn-num { from { opacity: 0; } to { opacity: 1; } }

/* the caret that writes the sheet, running down it forever */
.sbn__caret {
  fill: var(--deco);
  animation: sbn-caret 7s steps(1) infinite;
}
@keyframes sbn-caret {
  0%,  6%   { transform: translateY(0);    opacity: 1; }
  7%,  20%  { transform: translateY(24px); opacity: 1; }
  21%, 40%  { transform: translateY(56px); opacity: 1; }
  41%, 60%  { transform: translateY(88px); opacity: 1; }
  61%, 76%  { transform: translateY(120px); opacity: 1; }
  77%, 100% { transform: translateY(120px); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .sbn__sec, .sbn__mnum { animation: none; opacity: 1; }
  .sbn__caret { animation: none; opacity: 0; }
}

/* ============================================================
   10. Shared motion layer
   Applies across every section of a feature page rather than to
   one component: headings resolve word by word, panels follow
   the pointer with a light source, and buttons lean toward it.
   All of it is opt-in by markup and disabled for reduced motion.
   ============================================================ */

/* ---- 10a. Word-by-word heading reveal ---- */

.wsplit { display: inline-block; overflow: hidden; vertical-align: bottom; }
.wsplit > i {
  display: inline-block;
  font-style: inherit;
  transform: translateY(110%);
  transition: transform 0.75s var(--ease) var(--d, 0s);
}
.is-lit .wsplit > i { transform: none; }

/* ---- 10b. Pointer spotlight on panels ----
   One radial highlight tracking the cursor. --mx/--my are written
   by feature.js in element-local percentages. */

.lift {
  position: relative;
  transition: transform 0.5s var(--ease), border-color 0.4s ease, box-shadow 0.5s var(--ease);
}
.lift::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.45s ease;
  background: radial-gradient(
    240px circle at var(--mx, 50%) var(--my, 50%),
    rgba(var(--accent-rgb), 0.12),
    transparent 62%
  );
}
.lift:hover::after,
.lift:focus-within::after { opacity: 1; }

.lift:hover {
  transform: translateY(-5px);
  box-shadow: 0 26px 50px -34px rgba(0, 0, 0, 0.9);
}

/* a hairline that draws along the top edge on hover */
.lift::before {
  content: "";
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 1px;
  border-radius: inherit;
  background: linear-gradient(90deg, transparent, rgba(var(--accent-rgb), 0.75), transparent);
  transform: scaleX(0);
  transform-origin: 50% 50%;
  transition: transform 0.6s var(--ease);
  pointer-events: none;
  z-index: 1;
}
.lift:hover::before, .lift:focus-within::before { transform: scaleX(1); }

/* ---- 10c. Magnetic buttons ---- */

/* The magnetic pull composes with .btn:hover from style.css rather than
   replacing it, so a hero button lifts, throws its shadow, sweeps its fill
   and moves its arrow exactly as it does everywhere else — and leans toward
   the pointer on top of that. */
.mag {
  --pull-x: 0px;
  --pull-y: 0px;
  transform: translate(var(--pull-x), var(--pull-y));
  transition: transform 0.35s var(--ease);
  will-change: transform;
}
.mag:hover { transform: translate(var(--pull-x), calc(var(--pull-y) - 2px)); }

/* ---- 10d. Section entrance ----
   The band itself rises a little as it arrives, so a section reads
   as one object instead of a set of independently animating parts. */

.rise { opacity: 0; transform: translateY(26px); }
.rise.is-lit {
  opacity: 1;
  transform: none;
  transition: opacity 0.8s ease, transform 0.9s var(--ease);
}

/* ---- 10e. Eyebrow ticker ---- */

.eyebrow { position: relative; }
.is-lit > .eyebrow::after,
.is-lit .eyebrow::after {
  content: "";
  position: absolute;
  left: 0; bottom: -5px;
  height: 1px; width: 100%;
  background: linear-gradient(90deg, var(--accent-ink), transparent);
  transform: scaleX(0);
  transform-origin: 0 50%;
  animation: eb-draw 0.9s var(--ease) 0.25s forwards;
}
@keyframes eb-draw { to { transform: scaleX(1); } }

@media (prefers-reduced-motion: reduce) {
  .wsplit > i { transform: none; transition: none; }
  .rise { opacity: 1; transform: none; }
  .lift:hover { transform: none; box-shadow: none; }
  .lift::before { display: none; }
  .mag { transition: none; }
  .is-lit .eyebrow::after { animation: none; transform: scaleX(1); }
}

/* ---- 9b. Banner depth layers ----
   A second, slower plane behind the sheet and a faster one in front,
   so the banner separates as the pointer moves rather than sliding
   as one flat picture. */

.sbn__far {
  position: absolute;
  top: 50%;
  right: 1.5%;
  width: min(31%, 460px);
  translate: calc(var(--px) * 10px) calc(-50% + var(--py) * 9px);
  transition: translate 1.6s var(--ease);
  overflow: visible;
  opacity: 0.55;
}
.sbn__near {
  position: absolute;
  top: 50%;
  right: 1.5%;
  width: min(31%, 460px);
  translate: calc(var(--px) * 36px) calc(-50% + var(--py) * 31px);
  transition: translate 1.05s var(--ease);
  overflow: visible;
}
@media (max-width: 1279px) { .sbn__far, .sbn__near { display: none; } }

/* faint stacked sheets behind the live one */
.sbn__ghost { fill: none; stroke: var(--line-soft); stroke-width: 1; }

/* floating chips on the near plane */
.sbn__chip { fill: var(--ink-2); stroke: rgba(var(--deco-rgb), 0.45); stroke-width: 1.1; }
.sbn__chipt {
  fill: var(--deco);
  font-family: var(--font-display);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.04em;
}
.sbn__float { animation: sbn-float 6s ease-in-out infinite; }
.sbn__float--b { animation-duration: 7.5s; animation-delay: -2s; }
@keyframes sbn-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-7px); }
}

/* a confidence meter that fills with the marks */
.sbn__meter { fill: var(--line); }
.sbn__meterf {
  fill: var(--deco);
  transform-box: fill-box;
  transform-origin: left center;
  animation: sbn-meter 7s var(--ease) infinite;
}
@keyframes sbn-meter {
  0%, 8%    { scale: 0 1; }
  55%       { scale: 1 1; }
  72%       { scale: 1 1; }
  82%, 100% { scale: 0 1; }
}

@media (prefers-reduced-motion: reduce) {
  .sbn__float { animation: none; }
  .sbn__meterf { animation: none; scale: 1 1; }
}

/* ============================================================
   11. Chapter heatmap (Performance Analytics)
   Students down, chapters across, one cell per intersection.
   Colour is mastery; the row and column summaries are what a
   teacher actually acts on.
   ============================================================ */

.heat {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--ink-2);
  padding: clamp(1rem, 2.4vw, 1.5rem);
  overflow-x: auto;
}

.heat__grid {
  display: grid;
  grid-template-columns: 5.4rem repeat(8, minmax(30px, 1fr)) 3.2rem;
  gap: 4px;
  min-width: 520px;
}

.heat__ch,
.heat__sum-h {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted);
  text-align: center;
  padding-bottom: 0.2rem;
  white-space: nowrap;
}
.heat__corner { }

.heat__name {
  font-size: 0.72rem;
  color: var(--text-2);
  display: flex;
  align-items: center;
  white-space: nowrap;
  padding-right: 0.4rem;
}

/* one cell = one student on one chapter */
.heat__c {
  aspect-ratio: 1 / 1;
  border-radius: 5px;
  background: var(--hc, var(--line));
  position: relative;
  opacity: 0;
  transform: scale(0.5);
  transition: opacity 0.4s ease var(--d), transform 0.5s var(--ease) var(--d),
              outline-color 0.25s ease;
  outline: 1.5px solid transparent;
  outline-offset: 1px;
}
.is-on .heat__c { opacity: 1; transform: none; }
.heat__c:hover { outline-color: var(--paper); z-index: 2; }

/* mastery bands, driven by --hc on each cell */
.heat__c--hi   { --hc: var(--accent); }
.heat__c--good { --hc: rgba(var(--accent-rgb), 0.55); }
.heat__c--mid  { --hc: rgba(var(--p8-rgb), 0.6); }
.heat__c--low  { --hc: rgba(var(--p7-rgb), 0.62); }
.heat__c--na   { --hc: var(--ink-3); }

/* per-student and per-chapter summaries */
.heat__sum {
  display: flex; align-items: center; justify-content: flex-end;
  font-size: 0.72rem; font-weight: 700; font-variant-numeric: tabular-nums;
  color: var(--text-2); padding-left: 0.3rem;
}
.heat__sum--weak { color: var(--p7); }

.heat__foot {
  display: grid;
  grid-template-columns: 5.4rem repeat(8, minmax(30px, 1fr)) 3.2rem;
  gap: 4px;
  min-width: 520px;
  margin-top: 6px;
  padding-top: 0.6rem;
  border-top: 1px solid var(--line-soft);
}
.heat__flabel {
  font-size: 0.74rem; color: var(--muted); display: flex; align-items: center;
  white-space: nowrap;
}
.heat__f {
  font-size: 0.74rem; font-weight: 700; text-align: center;
  color: var(--muted-2); font-variant-numeric: tabular-nums;
}
.heat__f--weak { color: var(--p7); }

.heat__legend {
  display: flex; flex-wrap: wrap; gap: 0.9rem;
  margin-top: 1.1rem; padding-top: 0.9rem;
  border-top: 1px solid var(--line-soft);
}
.heat__legend span {
  display: flex; align-items: center; gap: 0.35rem;
  font-size: 0.79rem; color: var(--muted);
}
.heat__legend i { width: 11px; height: 11px; border-radius: 3px; background: var(--hc); }

/* the callout a teacher reads first */
.heat__call {
  display: flex; align-items: flex-start; gap: 0.6rem;
  margin-top: 1.1rem; padding: 0.75rem 0.85rem;
  border-radius: var(--radius);
  border: 1px solid rgba(var(--p7-rgb), 0.35);
  background: rgba(var(--p7-rgb), 0.07);
}
.heat__call svg { flex: none; color: var(--p7); margin-top: 1px; }
.heat__call p { margin: 0; font-size: 0.78rem; line-height: 1.5; color: var(--text-2); }
.heat__call b { color: var(--paper); }

@media (prefers-reduced-motion: reduce) {
  .heat__c { opacity: 1; transform: none; transition: none; }
}

/* ---- Performance Analytics: chapter bars settling ---- */

.sbn__bars {
  position: absolute; top: 50%; right: 1.5%;
  width: min(31%, 460px);
  translate: calc(var(--px) * 22px) calc(-50% + var(--py) * 20px);
  transition: translate 1.3s var(--ease);
  overflow: visible;
  mask-image: linear-gradient(90deg, transparent 0, #000 22%);
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 22%);
}
@media (max-width: 1279px) { .sbn__bars { display: none; } }

.sbn__bar {
  transform-box: fill-box;
  transform-origin: bottom center;
  animation: sbn-bar 7s var(--ease) infinite;
  animation-delay: calc(var(--d, 0) * 0.13s);
  fill: rgba(var(--deco-rgb), 0.5);
}
.sbn__bar--weak { fill: var(--coral); }
.sbn__bar--mid  { fill: var(--p8); }

@keyframes sbn-bar {
  0%        { scale: 1 0; opacity: 0; }
  10%       { opacity: 1; }
  38%       { scale: 1 1; }
  72%       { scale: 1 1; opacity: 1; }
  84%, 100% { scale: 1 1; opacity: 0; }
}

/* the threshold a chapter has to clear */
.sbn__thresh {
  stroke: var(--muted-3); stroke-width: 1; stroke-dasharray: 4 4;
  opacity: 0; animation: sbn-tot 7s var(--ease) 1s infinite;
}

@media (prefers-reduced-motion: reduce) {
  .sbn__bar { animation: none; scale: 1 1; }
  .sbn__thresh { animation: none; opacity: 1; }
}

/* ============================================================
   12. Live exam screen (Smart Online Exams)
   What a student is actually looking at: timer, question
   navigator, the current question, and the autosave state.
   ============================================================ */

.exam {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--ink-2);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.exam__bar {
  display: flex; align-items: center; gap: 0.7rem; flex-wrap: wrap;
  padding: 0.7rem 0.9rem;
  border-bottom: 1px solid var(--line-soft);
  background: rgba(var(--ink-rgb), 0.5);
}
.exam__name { font-size: 0.76rem; font-weight: 600; color: var(--text-2); }
.exam__timer {
  margin-left: auto;
  display: flex; align-items: center; gap: 0.35rem;
  font-family: var(--font-display); font-size: 0.82rem; font-weight: 700;
  color: var(--accent-ink); font-variant-numeric: tabular-nums;
  padding: 0.2rem 0.5rem; border-radius: 7px;
  background: rgba(var(--accent-rgb), 0.1);
  border: 1px solid rgba(var(--accent-rgb), 0.28);
}
.exam__save {
  display: flex; align-items: center; gap: 0.32rem;
  font-size: 0.76rem; font-weight: 600; color: var(--muted-2);
}
.exam__save i {
  width: 6px; height: 6px; border-radius: 50%; background: var(--accent);
  animation: paper-pulse 2.4s ease-in-out infinite;
}

.exam__body { display: grid; grid-template-columns: 128px 1fr; gap: 0; }
@media (max-width: 620px) { .exam__body { grid-template-columns: 1fr; } }

/* question navigator */
.exam__nav {
  padding: 0.8rem; border-right: 1px solid var(--line-soft);
  display: flex; flex-direction: column; gap: 0.6rem;
}
@media (max-width: 620px) { .exam__nav { border-right: 0; border-bottom: 1px solid var(--line-soft); } }

.exam__navt {
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--muted);
}
.exam__grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 4px; }
.exam__q {
  aspect-ratio: 1; border-radius: 5px;
  display: grid; place-items: center;
  font-size: 0.7rem; font-weight: 700;
  background: var(--ink-3); color: var(--muted-2);
  border: 1px solid transparent;
  opacity: 0; transform: scale(0.6);
  transition: opacity 0.3s ease var(--d), transform 0.4s var(--ease) var(--d);
}
.is-on .exam__q { opacity: 1; transform: none; }
.exam__q--done { background: rgba(var(--accent-rgb), 0.55); color: var(--on-accent); }
.exam__q--flag { background: rgba(var(--p8-rgb), 0.5); color: var(--ink); }
.exam__q--here { border-color: var(--accent); color: var(--accent); background: var(--ink); }

.exam__legend { display: flex; flex-direction: column; gap: 0.22rem; }
.exam__legend span { display: flex; align-items: center; gap: 0.3rem; font-size: 0.7rem; color: var(--muted-2); }
.exam__legend i { width: 8px; height: 8px; border-radius: 2px; }

/* the question itself */
.exam__main { padding: 0.9rem 1rem 1rem; display: flex; flex-direction: column; gap: 0.6rem; }
.exam__meta { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; font-size: 0.74rem; color: var(--muted-2); }
.exam__meta b { color: var(--text-2); font-size: 0.7rem; }
.exam__stem { margin: 0; font-size: 0.82rem; line-height: 1.55; color: var(--paper); }

.exam__opts { display: flex; flex-direction: column; gap: 0.35rem; margin-top: 0.15rem; }
.exam__opt {
  display: grid; grid-template-columns: 18px 1fr; gap: 0.5rem; align-items: center;
  padding: 0.45rem 0.6rem; border-radius: var(--radius);
  border: 1px solid var(--line-soft); background: rgba(var(--ink-rgb), 0.4);
  font-size: 0.76rem; color: var(--text-2);
  transition: border-color 0.3s ease, background 0.3s ease;
}
.exam__opt i {
  width: 16px; height: 16px; border-radius: 50%;
  border: 1.5px solid var(--line); display: block;
}
.exam__opt--on { border-color: var(--accent-ink); background: rgba(var(--accent-rgb), 0.07); }
.exam__opt--on i { border-color: var(--accent); background: var(--accent); box-shadow: inset 0 0 0 3px var(--ink-2); }

.exam__foot {
  display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;
  margin-top: auto; padding-top: 0.7rem; border-top: 1px solid var(--line-soft);
}
.exam__btn {
  font-size: 0.7rem; font-weight: 600; padding: 0.3rem 0.7rem;
  border-radius: 7px; border: 1px solid var(--line); color: var(--muted-2);
}
.exam__btn--go { border-color: var(--accent-ink); color: var(--accent-ink); margin-left: auto; }

/* the toast that proves a dropped connection costs nothing */
.exam__toast {
  display: flex; align-items: center; gap: 0.45rem;
  margin: 0.6rem 1rem 0.9rem;
  padding: 0.5rem 0.7rem; border-radius: var(--radius);
  border: 1px solid rgba(var(--p8-rgb), 0.4);
  background: rgba(var(--p8-rgb), 0.08);
  font-size: 0.7rem; color: var(--text-2);
}
.exam__toast svg { flex: none; color: var(--p8); }
.exam__toast b { color: var(--paper); }

@media (prefers-reduced-motion: reduce) {
  .exam__q { opacity: 1; transform: none; transition: none; }
  .exam__save i { animation: none; }
}

/* ---- Smart Online Exams: a live window ticking down ---- */

.sbn__win {
  position: absolute; top: 50%; right: 1.5%;
  width: min(31%, 460px);
  translate: calc(var(--px) * 22px) calc(-50% + var(--py) * 20px);
  transition: translate 1.3s var(--ease);
  overflow: visible;
  mask-image: linear-gradient(90deg, transparent 0, #000 22%);
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 22%);
}
@media (max-width: 1279px) { .sbn__win { display: none; } }

/* seats filling as students join the window */
.sbn__seat {
  fill: var(--line);
  transform-box: fill-box; transform-origin: center;
  animation: sbn-seat 7s var(--ease) infinite;
  animation-delay: calc(var(--d, 0) * 0.09s);
}
.sbn__seat--in { fill: rgba(var(--deco-rgb), 0.6); }
.sbn__seat--done { fill: var(--deco); }
@keyframes sbn-seat {
  0%, 4%    { scale: 0.5; opacity: 0; }
  14%       { scale: 1; opacity: 1; }
  72%       { scale: 1; opacity: 1; }
  84%, 100% { scale: 1; opacity: 0; }
}

/* the countdown ring on the window */
.sbn__ring { stroke: var(--line); stroke-width: 4; fill: none; }
.sbn__ringf {
  stroke: var(--deco); stroke-width: 4; fill: none; stroke-linecap: round;
  stroke-dasharray: 138; stroke-dashoffset: 138;
  transform: rotate(-90deg); transform-origin: center;
  animation: sbn-ring 7s linear infinite;
}
@keyframes sbn-ring {
  0%   { stroke-dashoffset: 138; }
  72%  { stroke-dashoffset: 20; }
  84%, 100% { stroke-dashoffset: 20; opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .sbn__seat { animation: none; }
  .sbn__ringf { animation: none; stroke-dashoffset: 20; }
}


/* ============================================================
   13. Proctoring — the session, replayed
   One wide panel on a time axis: an activity band for the whole
   window, with a pip at the moment each signal fired. Carries
   the division norm in the hover note so a count means something.
   ============================================================ */

.psn {
  padding: clamp(1.2rem, 2.6vw, 1.8rem);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-lg);
  background: var(--ink-2);
  display: flex; flex-direction: column; gap: 1.1rem;
}

.psn__head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.psn__head b {
  font-family: var(--font-display); font-size: 0.94rem; font-weight: 700;
  color: var(--paper); letter-spacing: -0.01em;
}
.psn__head span { font-size: 0.7rem; color: var(--muted-3); }

.psn__axis {
  display: flex; justify-content: space-between;
  font-size: 0.74rem; color: var(--muted-3); font-variant-numeric: tabular-nums;
}

/* the activity band, with room above it for the pips */
/* clearance for the full pip stack: dot 24 + stem 16 + label ~14, plus air */
.psn__wrap { position: relative; padding-top: 68px; }

.psn__band {
  height: 34px; border-radius: 8px; overflow: hidden;
  display: flex; background: var(--ink-3);
  border: 1px solid var(--line-soft);
}
.psn__seg { height: 100%; width: 0; transition: width 0.8s var(--ease) var(--d, 0s); }
.psn.is-on .psn__seg { width: var(--w); }
.psn__seg--on   { background: rgba(var(--accent-rgb), 0.42); }
.psn__seg--idle { background: rgba(var(--ink-rgb), 0.5); }
.psn__seg--off  { background: repeating-linear-gradient(45deg, rgba(var(--p7-rgb), .55) 0 5px, transparent 5px 10px); }

/* event pips, positioned by time */
.psn__pip {
  position: absolute; top: 0; left: var(--at);
  transform: translateX(-50%) translateY(6px);
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  opacity: 0;
  transition: opacity 0.4s ease var(--d, 0s), transform 0.45s var(--ease) var(--d, 0s);
}
.psn.is-on .psn__pip { opacity: 1; transform: translateX(-50%); }
.psn__dot {
  width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--ink); border: 1.5px solid var(--pc, var(--accent-ink));
  color: var(--pc, var(--accent-ink));
  transition: transform 0.35s var(--ease), box-shadow 0.35s ease;
}
.psn__pip:hover .psn__dot, .psn__pip:focus-within .psn__dot {
  transform: scale(1.2); box-shadow: 0 0 0 5px rgba(var(--ink-3-rgb), 0.7);
}
.psn__stem { width: 1px; height: 16px; background: var(--pc, var(--accent-ink)); opacity: 0.5; }
.psn__t {
  font-size: 0.72rem; color: var(--muted); font-variant-numeric: tabular-nums;
  white-space: nowrap; font-weight: 600;
  padding: 0.05rem 0.3rem; border-radius: 4px;
  background: var(--ink); border: 1px solid var(--line-soft);
}
.psn__pip:hover .psn__t { color: var(--paper); border-color: var(--pc, var(--accent-ink)); }

/* the detail, including the division norm */
.psn__note {
  position: absolute; bottom: calc(100% + 6px); left: 50%;
  transform: translateX(-50%) translateY(4px);
  width: max-content; max-width: 230px;
  padding: 0.5rem 0.65rem; border-radius: 9px;
  background: var(--ink); border: 1px solid var(--line);
  box-shadow: 0 18px 34px -20px rgba(0, 0, 0, 0.9);
  font-size: 0.79rem; line-height: 1.45; color: var(--text-2);
  opacity: 0; pointer-events: none; z-index: 4;
  transition: opacity 0.25s ease, transform 0.3s var(--ease);
}
.psn__note b { display: block; color: var(--paper); font-size: 0.72rem; margin-bottom: 0.15rem; }
.psn__pip:hover .psn__note, .psn__pip:focus-within .psn__note { opacity: 1; transform: translateX(-50%); }

.psn__key { display: flex; flex-wrap: wrap; gap: 1rem; }
.psn__key span { display: flex; align-items: center; gap: 0.35rem; font-size: 0.79rem; color: var(--muted); }
.psn__key i { width: 14px; height: 9px; border-radius: 2px; flex: none; }

/* what is never collected, as a horizontal strip */
.psn__never { display: flex; flex-wrap: wrap; gap: 0.45rem; padding-top: 0.95rem; border-top: 1px solid var(--line-soft); }
.psn__nchip {
  display: inline-flex; align-items: center; gap: 0.35rem;
  padding: 0.32rem 0.6rem; border-radius: 999px;
  border: 1px solid rgba(var(--p7-rgb), 0.34);
  background: rgba(var(--p7-rgb), 0.06);
  font-size: 0.7rem; font-weight: 600; color: var(--muted);
}
.psn__nchip svg { color: var(--p7); flex: none; }
.psn__nchip s { text-decoration: none; position: relative; }
.psn__nchip s::after {
  content: ""; position: absolute; left: 0; right: 0; top: 53%; height: 1.4px;
  background: var(--p7); transform: scaleX(0); transform-origin: 0 50%;
  transition: transform 0.45s var(--ease) calc(var(--d, 0s) + 0.25s);
}
.psn.is-on .psn__nchip s::after { transform: scaleX(1); }

.psn__foot { font-size: 0.76rem; line-height: 1.55; color: var(--muted); margin: 0; }
.psn__foot b { color: var(--paper); }

@media (prefers-reduced-motion: reduce) {
  .psn__seg { width: var(--w); transition: none; }
  .psn__pip { opacity: 1; transform: translateX(-50%); transition: none; }
  .psn__nchip s::after { transform: scaleX(1); transition: none; }
  .psn__note { transition: none; }
}

/* ============================================================
   14. The invigilator's console (Smart Online Exams)
   The teacher's live view of a division mid-exam: a roster of
   every candidate, a summary strip, and the actions available
   on whoever needs one.
   ============================================================ */

.inv {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--ink-2);
  overflow: hidden;
}

.inv__bar {
  display: flex; align-items: center; gap: 0.7rem; flex-wrap: wrap;
  padding: 0.75rem 0.95rem;
  border-bottom: 1px solid var(--line-soft);
  background: rgba(var(--ink-rgb), 0.5);
}
.inv__name { font-size: 0.78rem; font-weight: 600; color: var(--text-2); }
.inv__live {
  display: flex; align-items: center; gap: 0.32rem;
  font-size: 0.74rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--accent-ink);
  padding: 0.16rem 0.45rem; border-radius: 999px;
  background: rgba(var(--accent-rgb), 0.12); border: 1px solid rgba(var(--accent-rgb), 0.3);
}
.inv__live i { width: 5px; height: 5px; border-radius: 50%; background: currentColor; animation: paper-pulse 1.8s ease-in-out infinite; }
.inv__clock {
  margin-left: auto;
  font-family: var(--font-display); font-size: 0.8rem; font-weight: 700;
  color: var(--paper); font-variant-numeric: tabular-nums;
}

/* summary counts */
.inv__stats { display: grid; grid-template-columns: repeat(4, 1fr); border-bottom: 1px solid var(--line-soft); }
@media (max-width: 620px) { .inv__stats { grid-template-columns: repeat(2, 1fr); } }
.inv__stat {
  padding: 0.7rem 0.9rem;
  border-right: 1px solid var(--line-soft);
  display: flex; flex-direction: column; gap: 0.1rem;
}
.inv__stat:last-child { border-right: 0; }
.inv__stat b {
  font-family: var(--font-display); font-size: 1.25rem; font-weight: 700;
  color: var(--paper); font-variant-numeric: tabular-nums; line-height: 1;
}
.inv__stat span { font-size: 0.78rem; color: var(--muted-2); display: flex; align-items: center; gap: 0.3rem; }
.inv__stat span i { width: 8px; height: 8px; border-radius: 2px; flex: none; }

/* the roster */
.inv__roster {
  padding: 0.9rem;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(46px, 1fr));
  gap: 5px;
}

.inv__c {
  position: relative;
  aspect-ratio: 1;
  border-radius: 6px;
  display: grid; place-items: center;
  font-size: 0.72rem; font-weight: 700;
  font-variant-numeric: tabular-nums;
  background: var(--ink-3); color: var(--muted-2);
  border: 1px solid transparent;
  opacity: 0; transform: scale(0.6);
  transition: opacity 0.3s ease var(--d), transform 0.4s var(--ease) var(--d),
              border-color 0.25s ease, box-shadow 0.3s ease;
  cursor: default;
}
.inv.is-on .inv__c { opacity: 1; transform: none; }
.inv__c:hover { border-color: var(--paper); z-index: 3; }

.inv__c--go   { background: rgba(var(--accent-rgb), 0.5); color: var(--on-accent); }
.inv__c--done { background: var(--accent); color: var(--on-accent); }
.inv__c--none { background: var(--ink-3); color: var(--muted-3); border-color: var(--line); }
.inv__c--flag { background: rgba(var(--p8-rgb), 0.55); color: var(--ink); }
.inv__c--help { background: rgba(var(--p7-rgb), 0.6); color: var(--paper); }

/* a small mark for candidates needing attention */
.inv__c--flag::after, .inv__c--help::after {
  content: ""; position: absolute; top: 3px; right: 3px;
  width: 5px; height: 5px; border-radius: 50%; background: currentColor;
}

/* tooltip */
.inv__tip {
  position: absolute; bottom: calc(100% + 6px); left: 50%; transform: translateX(-50%) translateY(4px);
  width: max-content; max-width: 200px;
  padding: 0.4rem 0.55rem; border-radius: 8px;
  background: var(--ink); border: 1px solid var(--line);
  box-shadow: 0 16px 30px -18px rgba(0, 0, 0, 0.9);
  font-size: 0.76rem; line-height: 1.4; color: var(--text-2);
  font-weight: 400; letter-spacing: 0;
  opacity: 0; pointer-events: none; z-index: 5;
  transition: opacity 0.22s ease, transform 0.28s var(--ease);
}
.inv__tip b { display: block; color: var(--paper); font-size: 0.79rem; }
.inv__c:hover .inv__tip { opacity: 1; transform: translateX(-50%); }

/* the action strip */
.inv__act {
  display: grid; grid-template-columns: auto 1fr auto; gap: 0.8rem; align-items: center;
  padding: 0.8rem 0.95rem;
  border-top: 1px solid var(--line-soft);
  background: rgba(var(--p7-rgb), 0.05);
  flex-wrap: wrap;
}
@media (max-width: 700px) { .inv__act { grid-template-columns: 1fr; } }
.inv__act .inv__who {
  display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: 8px;
  background: rgba(var(--p7-rgb), 0.6); color: var(--paper);
  font-size: 0.74rem; font-weight: 700;
}
.inv__act p { margin: 0; font-size: 0.76rem; line-height: 1.45; color: var(--text-2); }
.inv__act p b { color: var(--paper); }
.inv__btns { display: flex; gap: 0.35rem; flex-wrap: wrap; }
.inv__btn {
  font-size: 0.7rem; font-weight: 600; padding: 0.3rem 0.65rem;
  border-radius: 7px; border: 1px solid var(--line); color: var(--muted-2);
  white-space: nowrap;
}
.inv__btn--go { border-color: var(--accent-ink); color: var(--accent-ink); }

@media (prefers-reduced-motion: reduce) {
  .inv__c { opacity: 1; transform: none; transition: none; }
  .inv__live i { animation: none; }
}

/* ============================================================
   15. Window outcomes (Smart Online Exams)
   Forty-two candidates as one bar, split by what actually
   happened to them. The honest version of "it worked".
   ============================================================ */

.wout {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--ink-2);
  padding: clamp(1.2rem, 2.6vw, 1.8rem);
  display: flex; flex-direction: column; gap: 1.1rem;
}

.wout__top { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.wout__top b {
  font-family: var(--font-display); font-size: 1.5rem; font-weight: 700;
  color: var(--paper); letter-spacing: -0.02em;
}
.wout__top b em { font-style: normal; font-size: 0.8rem; font-weight: 500; color: var(--muted); margin-left: 0.4rem; }
.wout__zero {
  font-size: 0.79rem; font-weight: 700; letter-spacing: 0.05em;
  padding: 0.24rem 0.55rem; border-radius: 999px;
  background: rgba(var(--accent-rgb), 0.14); color: var(--accent-ink);
  border: 1px solid rgba(var(--accent-rgb), 0.3);
}

.wout__bar {
  display: flex; height: 40px; border-radius: 9px; overflow: hidden;
  border: 1px solid var(--line-soft); background: var(--ink-3);
}
.wout__seg {
  width: 0; position: relative;
  transition: width 1s var(--ease) var(--d, 0s), filter 0.3s ease;
  background: var(--sc);
}
.wout.is-on .wout__seg { width: var(--w); }
.wout__seg.on { filter: brightness(1.35); }

.wout__rows { display: flex; flex-direction: column; gap: 0.3rem; }
.wout__row {
  display: grid; grid-template-columns: 12px 1fr auto auto; gap: 0.7rem; align-items: center;
  width: 100%; padding: 0.55rem 0.6rem;
  border: 1px solid transparent; border-radius: var(--radius);
  background: none; color: inherit; font: inherit; text-align: left; cursor: pointer;
  opacity: 0; transform: translateX(8px);
  transition: opacity 0.4s ease var(--d), transform 0.45s var(--ease) var(--d),
              background 0.3s ease, border-color 0.3s ease;
}
.wout.is-on .wout__row { opacity: 1; transform: none; }
.wout__row:hover, .wout__row.on { background: rgba(var(--ink-rgb), 0.5); border-color: var(--line-soft); }
.wout__row i { width: 12px; height: 12px; border-radius: 3px; background: var(--sc); }
.wout__row b { font-family: var(--font-display); font-size: 0.82rem; font-weight: 600; color: var(--paper); }
.wout__row span { font-size: 0.72rem; color: var(--muted-2); line-height: 1.4; grid-column: 2 / -1; }
.wout__row em {
  font-style: normal; font-family: var(--font-display); font-size: 0.86rem; font-weight: 700;
  color: var(--text-2); font-variant-numeric: tabular-nums;
}

.wout__foot { font-size: 0.76rem; line-height: 1.55; color: var(--muted); margin: 0; padding-top: 0.4rem; border-top: 1px solid var(--line-soft); }
.wout__foot b { color: var(--paper); }

@media (prefers-reduced-motion: reduce) {
  .wout__seg { width: var(--w); transition: none; }
  .wout__row { opacity: 1; transform: none; transition: none; }
}

/* ============================================================
   16. Interaction layer
   Panels that respond to a click, not only a hover. Every
   control below is a real button with a focus state, so the
   whole page is operable from a keyboard.
   ============================================================ */

/* ---- exam navigator: pick a question ---- */
.exam__q {
  border: 1px solid transparent;
  font: inherit; font-size: 0.72rem; font-weight: 700;
  cursor: pointer; padding: 0;
  transition: opacity .3s ease var(--d), transform .4s var(--ease) var(--d),
              background .25s ease, color .25s ease, border-color .25s ease, box-shadow .3s ease;
}
.exam__q:hover { border-color: var(--paper); transform: translateY(-2px) scale(1.06); z-index: 2; }
.exam__q:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.exam__q--here {
  box-shadow: 0 0 0 2px rgba(var(--accent-rgb), .35), 0 8px 20px -8px var(--accent);
}

/* the question body crossfades when the navigator changes it */
.exam__main { position: relative; }
.exam__slide { transition: opacity .28s ease, transform .32s var(--ease); }
.exam__main.is-swapping .exam__slide { opacity: 0; transform: translateY(6px); }

/* ---- invigilator roster: pick a candidate ---- */
.inv__c {
  border: 1px solid transparent; font: inherit;
  font-size: 0.72rem; font-weight: 700; cursor: pointer; padding: 0;
}
.inv__c:hover { transform: translateY(-2px) scale(1.08); z-index: 4; }
.inv__c:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; z-index: 4; }
.inv__c.is-sel {
  border-color: var(--paper);
  box-shadow: 0 0 0 2px rgba(var(--paper-rgb, 244,244,240), .25), 0 10px 22px -10px rgba(0,0,0,.9);
  transform: translateY(-2px) scale(1.08);
}

/* the action strip swaps with the selection */
.inv__act { transition: background .35s ease; }
.inv__act.is-calm { background: rgba(var(--ink-rgb), .4); }
.inv__act .inv__who { transition: background .35s ease, color .35s ease; }
.inv__act.is-calm .inv__who { background: var(--ink-3); color: var(--muted-2); }
.inv__fade { transition: opacity .25s ease; }
.inv__act.is-swapping .inv__fade { opacity: 0; }

/* ---- proctoring pips: click to pin the note open ---- */
.psn__pip { border: 0; background: none; padding: 0; font: inherit; cursor: pointer; }
.psn__pip:focus-visible .psn__dot { outline: 2px solid var(--accent); outline-offset: 3px; }
.psn__pip.is-pinned .psn__note { opacity: 1; transform: translateX(-50%); }
.psn__pip.is-pinned .psn__dot { background: var(--pc, var(--accent-ink)); color: var(--ink); }

/* ---- gates: click to expand the detail ---- */
.gate__row { cursor: pointer; }
.gate__more {
  grid-column: 2 / -1;
  max-height: 0; overflow: hidden; opacity: 0;
  transition: max-height .45s var(--ease), opacity .3s ease, margin .45s var(--ease);
  font-size: 0.78rem; line-height: 1.55; color: var(--muted-2);
}
.gate__row.is-open .gate__more { max-height: 160px; opacity: 1; margin-top: .55rem; }
.gate__row .gate__tick { transition: transform .4s var(--ease), background .3s ease, border-color .3s ease; }
.gate__row.is-open .gate__tick { transform: scale(1.25); }

/* ---- shared: a ripple that follows the click ---- */
.rip { position: relative; overflow: hidden; }
.rip::after {
  content: ""; position: absolute; left: var(--rx, 50%); top: var(--ry, 50%);
  width: 8px; height: 8px; border-radius: 50%; translate: -50% -50%;
  background: rgba(var(--accent-rgb), .5);
  transform: scale(0); opacity: 0; pointer-events: none;
}
.rip.is-rip::after { animation: rip .55s var(--ease) forwards; }
@keyframes rip {
  0%   { transform: scale(0); opacity: .7; }
  100% { transform: scale(18); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .exam__q:hover, .inv__c:hover, .inv__c.is-sel { transform: none; }
  .exam__slide, .inv__fade { transition: none; }
  .rip.is-rip::after { animation: none; }
}

/* ============================================================
   17. The parent's screen (Parent Dashboard)
   A phone, deliberately — the exam screen on Smart Online Exams
   is a desktop panel, and a parent reads this standing in a
   kitchen. Same staging engine as the other panels.
   ============================================================ */

.par-wrap {
  display: grid;
  grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
  gap: clamp(1.6rem, 4vw, 3rem);
  align-items: start;
}
@media (max-width: 880px) { .par-wrap { grid-template-columns: 1fr; } }

.par {
  position: relative;
  border: 1px solid var(--line);
  border-radius: 26px;
  background: var(--ink-2);
  padding: 0.75rem 0.7rem 1rem;
  box-shadow: 0 40px 80px -50px var(--shadow-strong);
  display: flex; flex-direction: column; gap: 0.6rem;
}
/* the earpiece slot, so it reads as a handset without a bezel image */
.par::before {
  content: ""; position: absolute; top: 0.5rem; left: 50%; translate: -50% 0;
  width: 62px; height: 5px; border-radius: 999px; background: var(--line);
}

.par__top { padding-top: 0.8rem; display: flex; align-items: center; gap: 0.5rem; }
.par__ava {
  width: 34px; height: 34px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  background: var(--accent); color: var(--on-accent);
  font-family: var(--font-display); font-weight: 700; font-size: 0.8rem;
}
.par__who b { display: block; font-size: 0.86rem; color: var(--paper); font-weight: 600; line-height: 1.2; }
.par__who span { font-size: 0.72rem; color: var(--muted-2); }

.par__card {
  border: 1px solid var(--line-soft); border-radius: var(--radius);
  background: rgba(var(--ink-rgb), 0.5);
  padding: 0.65rem 0.7rem;
  opacity: 0; transform: translateY(10px);
  transition: opacity 0.45s ease var(--d), transform 0.5s var(--ease) var(--d);
}
.is-on .par__card { opacity: 1; transform: none; }

.par__lab {
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--muted); display: block; margin-bottom: 0.3rem;
}

/* latest result */
.par__res { display: flex; align-items: baseline; gap: 0.5rem; }
.par__res b {
  font-family: var(--font-display); font-size: 1.5rem; font-weight: 700;
  color: var(--paper); letter-spacing: -0.02em; font-variant-numeric: tabular-nums;
}
.par__res span { font-size: 0.74rem; color: var(--muted-2); }
.par__res em {
  margin-left: auto; font-style: normal; font-size: 0.7rem; font-weight: 700;
  padding: 0.14rem 0.42rem; border-radius: 999px;
  background: rgba(var(--accent-rgb), 0.14); color: var(--accent-ink);
}

/* the two chapters that cost the most */
.par__ch { display: flex; flex-direction: column; gap: 0.35rem; }
.par__chr { display: grid; grid-template-columns: 1fr auto; gap: 0.5rem; align-items: center; }
.par__chr span { font-size: 0.76rem; color: var(--text-2); }
.par__chr i {
  font-style: normal; font-size: 0.72rem; font-weight: 700;
  color: var(--p7); font-variant-numeric: tabular-nums;
}
.par__bar { grid-column: 1 / -1; height: 5px; border-radius: 3px; background: var(--ink-3); overflow: hidden; }
.par__bar i { display: block; height: 100%; width: 0; border-radius: 3px; background: var(--p7); transition: width 0.8s var(--ease) var(--d, 0s); }
.is-on .par__bar i { width: var(--w); }

/* term trend */
.par__spark { display: flex; align-items: flex-end; gap: 4px; height: 40px; }
.par__spark i {
  flex: 1; border-radius: 2px 2px 0 0; height: 0;
  background: linear-gradient(180deg, var(--accent-ink), rgba(var(--accent-rgb), 0.2));
  transition: height 0.7s var(--ease) var(--d, 0s);
}
.is-on .par__spark i { height: var(--h); }
.par__spark i:last-child { background: linear-gradient(180deg, var(--accent), rgba(var(--accent-rgb), 0.3)); }

/* the one action */
.par__do {
  display: flex; align-items: center; gap: 0.5rem;
  border: 1px solid var(--accent-ink); border-radius: var(--radius);
  background: rgba(var(--accent-rgb), 0.08);
  padding: 0.6rem 0.7rem;
}
.par__do svg { color: var(--accent-ink); flex: none; }
.par__do b { display: block; font-size: 0.78rem; color: var(--paper); font-weight: 600; }
.par__do span { font-size: 0.72rem; color: var(--muted-2); }

/* ---- the alert switchboard beside the phone ---- */
.alerts { display: flex; flex-direction: column; gap: 0.5rem; }
.alerts__row {
  display: grid; grid-template-columns: 34px 1fr auto; gap: 0.75rem; align-items: center;
  padding: 0.7rem 0.8rem; border-radius: var(--radius);
  border: 1px solid var(--line-soft); background: var(--ink-2);
  opacity: 0; transform: translateX(10px);
  transition: opacity 0.4s ease var(--d), transform 0.45s var(--ease) var(--d),
              border-color 0.3s ease;
}
.is-on .alerts__row { opacity: 1; transform: none; }
.alerts__row:hover { border-color: var(--line); }

.alerts__ic { width: 34px; height: 34px; border-radius: 9px; display: grid; place-items: center; background: var(--ink-3); color: var(--muted-2); }
.alerts__row--on .alerts__ic { background: rgba(var(--accent-rgb), 0.14); color: var(--accent-ink); }
.alerts__row--off .alerts__ic { background: rgba(var(--p7-rgb), 0.12); color: var(--p7); }

.alerts__tx b { display: block; font-size: 0.82rem; color: var(--paper); font-weight: 600; }
.alerts__tx span { font-size: 0.74rem; color: var(--muted-2); line-height: 1.45; }

/* a switch that reads as on or off without being interactive theatre */
.alerts__sw {
  width: 38px; height: 21px; border-radius: 999px; position: relative; flex: none;
  background: var(--ink-3); border: 1px solid var(--line);
  transition: background 0.35s ease, border-color 0.35s ease;
}
.alerts__sw::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 15px; height: 15px; border-radius: 50%;
  background: var(--muted-3); transition: transform 0.35s var(--ease), background 0.35s ease;
}
.alerts__row--on .alerts__sw { background: rgba(var(--accent-rgb), 0.25); border-color: var(--accent-ink); }
.alerts__row--on .alerts__sw::after { transform: translateX(17px); background: var(--accent); }

.alerts__note {
  margin-top: 0.5rem; padding-top: 0.9rem; border-top: 1px solid var(--line-soft);
  font-size: 0.78rem; line-height: 1.55; color: var(--muted);
}
.alerts__note b { color: var(--paper); }

@media (prefers-reduced-motion: reduce) {
  .par__card, .alerts__row { opacity: 1; transform: none; transition: none; }
  .par__bar i { width: var(--w); transition: none; }
  .par__spark i { height: var(--h); transition: none; }
}

/* ============================================================
   18. The verdict (Parent Dashboard, section 6)
   The line used to sit alone in a wide empty band. It now argues
   its own case: a leaderboard that tells you nothing, beside a
   chapter breakdown that tells you what to do. The sentence and
   the evidence, side by side.
   ============================================================ */

.verdict {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
}
@media (max-width: 900px) { .verdict { grid-template-columns: 1fr; } }

/* ---- the sentence, set much larger ---- */
.verdict__line {
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 3.6vw, 2.6rem);
  font-weight: 600;
  line-height: 1.18;
  letter-spacing: -0.025em;
  color: var(--paper);
  margin: 0;
  text-wrap: balance;
}
.verdict__line .ln { display: block; overflow: hidden; }
.verdict__line .ln > span {
  display: inline-block;
  transform: translateY(108%);
  transition: transform 0.9s var(--ease) var(--d, 0s);
}
.is-lit .verdict__line .ln > span { transform: none; }

.verdict__line .dead { color: var(--muted-3); position: relative; }
.verdict__line .dead::after {
  content: ""; position: absolute; left: -0.06em; right: -0.06em; top: 55%;
  height: 2px; background: var(--muted-3);
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform 0.6s var(--ease) 1.05s;
}
.is-lit .verdict__line .dead::after { transform: scaleX(1); }

.verdict__line em { font-style: normal; color: var(--accent-ink); position: relative; }
.verdict__line em::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -0.12em; height: 3px; border-radius: 2px;
  background: linear-gradient(90deg, var(--accent-ink), var(--accent), var(--accent-ink));
  background-size: 220% 100%;
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform 0.75s var(--ease) 1.15s;
}
.is-lit .verdict__line em::after { transform: scaleX(1); animation: vd-run 4s linear 1.9s infinite; }
@keyframes vd-run { to { background-position: 220% 0; } }

.verdict__sub {
  margin: 1.4rem 0 0; max-width: 46ch;
  font-size: var(--step--1); line-height: 1.65; color: var(--muted);
}

/* ---- the evidence ---- */
.verdict__show { display: grid; gap: 0.8rem; position: relative; }

.vcard {
  border: 1px solid var(--line-soft);
  border-radius: var(--radius-lg);
  background: var(--ink-2);
  padding: clamp(0.9rem, 2vw, 1.25rem);
  position: relative;
  opacity: 0; transform: translateY(16px);
  transition: opacity 0.6s ease var(--d), transform 0.7s var(--ease) var(--d),
              border-color 0.4s ease, filter 0.6s ease;
}
.is-lit .vcard { opacity: 1; transform: none; }

.vcard__tag {
  display: inline-flex; align-items: center; gap: 0.35rem;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
  padding: 0.2rem 0.5rem; border-radius: 999px; margin-bottom: 0.7rem;
}

/* the useless one: greys itself out once it has been read */
.vcard--dead .vcard__tag { background: rgba(var(--ink-rgb), 0.6); color: var(--muted-3); border: 1px solid var(--line); }
.vcard--dead { filter: saturate(0.35); }
.is-lit .vcard--dead { filter: saturate(0.2) brightness(0.86); transition-delay: calc(var(--d) + 1.5s); }
.vcard--dead:hover { filter: none; border-color: var(--line); }

.vcard__rank { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.18rem; }
.vcard__rank li {
  display: grid; grid-template-columns: 1.6rem 1fr auto; gap: 0.6rem; align-items: center;
  padding: 0.3rem 0.35rem; border-radius: 7px;
  font-size: 0.82rem; color: var(--muted-2);
}
.vcard__rank li b { font-family: var(--font-display); font-weight: 700; color: var(--muted-3); font-variant-numeric: tabular-nums; }
.vcard__rank li i { font-style: normal; font-variant-numeric: tabular-nums; color: var(--muted-3); }
.vcard__rank li.is-you {
  background: rgba(var(--ink-rgb), 0.6); color: var(--text-2);
  outline: 1px solid var(--line);
}
.vcard__rank li.is-you b, .vcard__rank li.is-you i { color: var(--text-2); }

/* the useful one */
.vcard--live { border-color: var(--accent-ink); }
.vcard--live .vcard__tag { background: rgba(var(--accent-rgb), 0.14); color: var(--accent-ink); border: 1px solid rgba(var(--accent-rgb), 0.32); }
.vcard--live::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0.28);
  animation: vd-halo 3.6s var(--ease) 2s infinite;
}
@keyframes vd-halo {
  0%, 100% { box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0); }
  40%      { box-shadow: 0 0 0 6px rgba(var(--accent-rgb), 0.07); }
}

.vch { display: flex; flex-direction: column; gap: 0.5rem; }
.vch__row { display: grid; grid-template-columns: 1fr auto; gap: 0.5rem; align-items: center; }
.vch__row span { font-size: 0.84rem; color: var(--text-2); }
.vch__row i { font-style: normal; font-size: 0.8rem; font-weight: 700; color: var(--p7); font-variant-numeric: tabular-nums; }
.vch__bar { grid-column: 1 / -1; height: 6px; border-radius: 3px; background: var(--ink-3); overflow: hidden; }
.vch__bar i {
  display: block; height: 100%; width: 0; border-radius: 3px; background: var(--p7);
  transition: width 0.9s var(--ease) var(--d, 0s);
}
.is-lit .vch__bar i { width: var(--w); }

.vcard__do {
  display: flex; align-items: center; gap: 0.5rem; margin-top: 0.9rem;
  padding: 0.6rem 0.7rem; border-radius: var(--radius);
  border: 1px solid var(--accent-ink); background: rgba(var(--accent-rgb), 0.08);
}
.vcard__do svg { color: var(--accent-ink); flex: none; }
.vcard__do b { display: block; font-size: 0.82rem; color: var(--paper); font-weight: 600; }
.vcard__do span { font-size: 0.76rem; color: var(--muted-2); }

.vcard__note { margin: 0.7rem 0 0; font-size: 0.78rem; line-height: 1.5; color: var(--muted-3); }
.vcard--live .vcard__note { color: var(--muted); }
.vcard__note b { color: var(--paper); }

/* the pivot between the two cards */
.verdict__vs {
  justify-self: center;
  display: inline-flex; align-items: center; gap: 0.5rem;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--muted-3);
}
.verdict__vs::before, .verdict__vs::after {
  content: ""; width: 34px; height: 1px;
  background: linear-gradient(90deg, transparent, var(--line));
}
.verdict__vs::after { background: linear-gradient(90deg, var(--line), transparent); }

@media (prefers-reduced-motion: reduce) {
  .verdict__line .ln > span { transform: none; }
  .verdict__line .dead::after, .verdict__line em::after { transform: scaleX(1); animation: none; }
  .vcard { opacity: 1; transform: none; }
  .vcard--live::after { animation: none; }
  .vch__bar i { width: var(--w); transition: none; }
}

/* ---- 19b. Breathing room between hero buttons ----
   These buttons lean toward the pointer and throw a shadow, so the
   site-wide 0.8rem gap closes up under hover. Widened only where a
   button is magnetic; every other .hero__cta keeps its own spacing. */
.svc-hero .hero__cta,
.cta-band .hero__cta { gap: 1.15rem; }

/* the shadow is generous, so give it somewhere to land */
.svc-hero .hero__cta > .btn { margin-block: 2px; }


/* ============================================================
   20. Script <-> rubric cross-highlight   (#script, ai-evaluation)

   The <em> under each rubric point is quoted verbatim out of the
   candidate's answer, but nothing said so. Hovering a point now paints
   the exact words it was awarded for, and hovering the words lights the
   point that credited them. Done with :has() on the shared .io wrapper,
   so the link needs no JS — the two panels are separate subtrees and a
   sibling combinator can't reach across them.
   ============================================================ */

.paper__hl {
  background-color: transparent;
  color: inherit;
  padding: 0 2px;
  border-radius: 3px;
  cursor: default;
  -webkit-box-decoration-break: clone;
          box-decoration-break: clone;
  /* the wash is a sized background-image so it can wipe in horizontally;
     the inset shadow is the resting hint that the words are clickable */
  background-image: linear-gradient(
    rgba(var(--accent-rgb), 0.22), rgba(var(--accent-rgb), 0.22));
  background-repeat: no-repeat;
  background-size: 0% 100%;
  box-shadow: inset 0 -1px 0 rgba(var(--accent-rgb), 0.3);
  transition: background-size 0.45s var(--ease), color 0.3s ease,
              box-shadow 0.3s ease;
}

/* point -> phrase, and phrase -> itself */
.io:has(.mark__pt[data-pt="1"]:hover) .paper__hl[data-pt="1"],
.io:has(.mark__pt[data-pt="2"]:hover) .paper__hl[data-pt="2"],
.io:has(.mark__pt[data-pt="3"]:hover) .paper__hl[data-pt="3"],
.paper__hl:hover {
  background-size: 100% 100%;
  color: var(--paper);
  box-shadow: inset 0 -1px 0 rgba(var(--accent-rgb), 0.8);
}

/* phrase -> point */
.io:has(.paper__hl[data-pt="1"]:hover) .mark__pt[data-pt="1"],
.io:has(.paper__hl[data-pt="2"]:hover) .mark__pt[data-pt="2"],
.io:has(.paper__hl[data-pt="3"]:hover) .mark__pt[data-pt="3"],
.io .mark__pt:hover {
  border-color: rgba(var(--accent-rgb), 0.55);
  background: rgba(var(--accent-rgb), 0.07);
  box-shadow: 0 0 26px -14px rgba(var(--accent-rgb), 0.8);
}

/* the tick reacts on its own element, so it escapes the entrance
   transition's var(--d) delay and responds immediately */
.mark__tick {
  transition: transform 0.35s var(--ease), box-shadow 0.35s ease;
}
.io:has(.paper__hl[data-pt="1"]:hover) .mark__pt[data-pt="1"] .mark__tick,
.io:has(.paper__hl[data-pt="2"]:hover) .mark__pt[data-pt="2"] .mark__tick,
.io:has(.paper__hl[data-pt="3"]:hover) .mark__pt[data-pt="3"] .mark__tick,
.io .mark__pt:hover .mark__tick {
  transform: scale(1.18);
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.14);
}

/* the bonus point was never attempted, so there is nothing to paint.
   Hovering it dims all three highlights instead — the honest answer
   to "where did this one come from?" is "nowhere". */
.io:has(.mark__pt[data-pt="4"]:hover) .paper__hl {
  box-shadow: inset 0 -1px 0 rgba(var(--accent-rgb), 0.08);
  color: var(--muted);
}
.io .mark__pt[data-pt="4"]:hover {
  border-color: rgba(var(--p7-rgb), 0.5);
  background: rgba(var(--p7-rgb), 0.07);
  box-shadow: 0 0 26px -14px rgba(var(--p7-rgb), 0.7);
}

@media (prefers-reduced-motion: reduce) {
  .paper__hl { transition: none; }
  .io .mark__pt:hover .mark__tick { transform: none; }
}


/* ============================================================
   21. Heatmap crosshair   (#heatmap, analytics)

   The lede tells you to "read a row" and "read a column", so hovering a
   cell now does exactly that: its row and column stay lit, everything
   else falls back, and both labels come up in the accent.

   Dimming uses filter, not opacity, deliberately — .heat__c animates in
   on opacity via .is-on, and a second opacity rule would fight the
   entrance. Built with :has() so no JS is needed for 48 cells.
   ============================================================ */

.heat__c,
.heat__name,
.heat__ch {
  transition: filter 0.3s ease, color 0.3s ease, outline-color 0.3s ease,
              transform 0.3s var(--ease);
}

/* everything recedes the moment any cell is under the pointer */
.heat__grid:has(.heat__c:hover) .heat__c {
  filter: grayscale(0.75) brightness(0.55);
}

/* ...except the row and the column it sits in */
.heat__grid:has([data-r="0"]:hover) [data-r="0"],
.heat__grid:has([data-r="1"]:hover) [data-r="1"],
.heat__grid:has([data-r="2"]:hover) [data-r="2"],
.heat__grid:has([data-r="3"]:hover) [data-r="3"],
.heat__grid:has([data-r="4"]:hover) [data-r="4"],
.heat__grid:has([data-r="5"]:hover) [data-r="5"],
.heat__grid:has([data-c="0"]:hover) [data-c="0"],
.heat__grid:has([data-c="1"]:hover) [data-c="1"],
.heat__grid:has([data-c="2"]:hover) [data-c="2"],
.heat__grid:has([data-c="3"]:hover) [data-c="3"],
.heat__grid:has([data-c="4"]:hover) [data-c="4"],
.heat__grid:has([data-c="5"]:hover) [data-c="5"],
.heat__grid:has([data-c="6"]:hover) [data-c="6"],
.heat__grid:has([data-c="7"]:hover) [data-c="7"] {
  filter: none;
}

/* the two labels that name what you are reading */
.heat__grid:has([data-r="0"]:hover) .heat__name[data-r="0"],
.heat__grid:has([data-r="1"]:hover) .heat__name[data-r="1"],
.heat__grid:has([data-r="2"]:hover) .heat__name[data-r="2"],
.heat__grid:has([data-r="3"]:hover) .heat__name[data-r="3"],
.heat__grid:has([data-r="4"]:hover) .heat__name[data-r="4"],
.heat__grid:has([data-r="5"]:hover) .heat__name[data-r="5"],
.heat__grid:has([data-c="0"]:hover) .heat__ch[data-c="0"],
.heat__grid:has([data-c="1"]:hover) .heat__ch[data-c="1"],
.heat__grid:has([data-c="2"]:hover) .heat__ch[data-c="2"],
.heat__grid:has([data-c="3"]:hover) .heat__ch[data-c="3"],
.heat__grid:has([data-c="4"]:hover) .heat__ch[data-c="4"],
.heat__grid:has([data-c="5"]:hover) .heat__ch[data-c="5"],
.heat__grid:has([data-c="6"]:hover) .heat__ch[data-c="6"],
.heat__grid:has([data-c="7"]:hover) .heat__ch[data-c="7"] {
  color: var(--accent-ink);
  transform: translateY(-1px);
}

/* the cell itself still wins over its own row and column */
.heat__grid .heat__c:hover {
  outline-color: var(--paper);
  z-index: 2;
  transform: scale(1.14);
}

@media (prefers-reduced-motion: reduce) {
  .heat__grid .heat__c:hover { transform: none; }
  .heat__grid:has([data-r]:hover) .heat__name,
  .heat__grid:has([data-c]:hover) .heat__ch { transform: none; }
}


/* ============================================================
   22. Console legend filters the roster   (#console, online-exams)

   The lede claims the two who never signed in are "visible from the
   first minute". They weren't — 42 tiles is exactly the count at which
   two of anything stops being visible. Hovering a legend chip now pulls
   its own tiles forward and pushes the other forty back.

   "Needs a look" covers two classes, --flag and --help, because that is
   how the count of 4 was reached (3 flagged + 1 asking for help).

   Dimming is filter, not opacity: .inv__c fades in via .inv.is-on and a
   competing opacity rule would fight the entrance.
   ============================================================ */

.inv__stat {
  cursor: default;
  transition: background 0.3s ease, color 0.3s ease;
}
.inv__stat:hover { background: rgba(var(--accent-rgb), 0.06); }
.inv__stat:hover b { color: var(--accent-ink); }
.inv__stat span i { transition: transform 0.3s var(--ease), box-shadow 0.3s ease; }
.inv__stat:hover span i {
  transform: scale(1.5);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.15);
}

.inv__c { transition: filter 0.3s ease, transform 0.3s var(--ease),
                      border-color 0.3s ease, opacity 0.45s ease var(--d); }

.inv:has(.inv__stat[data-state="go"]:hover)    .inv__c:not(.inv__c--go),
.inv:has(.inv__stat[data-state="done"]:hover)  .inv__c:not(.inv__c--done),
.inv:has(.inv__stat[data-state="none"]:hover)  .inv__c:not(.inv__c--none),
.inv:has(.inv__stat[data-state="watch"]:hover) .inv__c:not(.inv__c--flag):not(.inv__c--help) {
  filter: grayscale(0.85) brightness(0.45);
}

.inv:has(.inv__stat[data-state="go"]:hover)    .inv__c--go,
.inv:has(.inv__stat[data-state="done"]:hover)  .inv__c--done,
.inv:has(.inv__stat[data-state="none"]:hover)  .inv__c--none,
.inv:has(.inv__stat[data-state="watch"]:hover) .inv__c--flag,
.inv:has(.inv__stat[data-state="watch"]:hover) .inv__c--help {
  transform: scale(1.08);
  border-color: var(--paper);
  z-index: 2;
}

@media (prefers-reduced-motion: reduce) {
  .inv:has(.inv__stat:hover) .inv__c { transform: none; }
  .inv__stat:hover span i { transform: none; }
}


/* ============================================================
   23. Verdict cards react in character   (#verdict, parent-dashboard)

   The section argues that a rank is inert and a chapter is actionable.
   The hover states say the same thing rather than contradicting it: the
   chapter card's rows come alive one at a time and each bar can be
   picked out, while the rank card just resolves into focus and offers
   nothing further — which is the point being made about it.

   Kept restrained on purpose; this page's layout was reverted to an
   earlier approved version, so nothing here moves the composition.
   ============================================================ */

.vcard--live { transition: border-color 0.35s ease, box-shadow 0.45s ease; }
.vcard--live:hover {
  border-color: var(--accent);
  box-shadow: 0 0 42px -22px rgba(var(--accent-rgb), 0.85);
}

.vch__row { border-radius: var(--radius); transition: background 0.3s ease; }
.vch__row span,
.vch__row i { transition: color 0.3s ease, transform 0.35s var(--ease); }
.vch__bar > i { transition: filter 0.35s ease, box-shadow 0.35s ease; }

.vch__row:hover { background: rgba(var(--accent-rgb), 0.06); }
.vch__row:hover span { color: var(--paper); }
.vch__row:hover i { transform: scale(1.14); }
.vch__row:hover .vch__bar > i {
  filter: brightness(1.25);
  box-shadow: 0 0 16px -4px rgba(var(--accent-rgb), 0.75);
}

/* the other rows step back, so one chapter reads at a time */
.vch:has(.vch__row:hover) .vch__row:not(:hover) span,
.vch:has(.vch__row:hover) .vch__row:not(:hover) i { color: var(--muted-3); }

/* the rank card resolves, and does nothing else. .vcard--dead:hover
   already clears the saturation filter; this only softens the arrival. */
.vcard--dead { transition: filter 0.5s ease, border-color 0.4s ease; }

@media (prefers-reduced-motion: reduce) {
  .vch__row:hover i { transform: none; }
}


/* ============================================================
   24. "What it won't do" — the anti block

   A list of refusals should not look like a list of features. Each item
   carries a coral cross, the border answers in coral rather than accent,
   and the items arrive one at a time so the four read as a stated
   position rather than a grid of boxes.
   ============================================================ */

.anti__head svg { animation: anti-warn 3.4s ease-in-out infinite; transform-origin: 50% 50%; }
@keyframes anti-warn {
  0%, 88%, 100% { transform: none; }
  91%           { transform: rotate(-7deg) scale(1.08); }
  94%           { transform: rotate(6deg) scale(1.08); }
  97%           { transform: rotate(-3deg) scale(1.03); }
}

.anti__item {
  position: relative;
  overflow: hidden;
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 0.55s ease var(--d, 0s), transform 0.6s var(--ease) var(--d, 0s),
              border-color 0.35s ease, background 0.35s ease, box-shadow 0.4s ease;
}
.anti.is-in .anti__item,
.anti__list.is-on .anti__item { opacity: 1; transform: none; }

/* nothing writes --d onto these, so the stagger is declared here */
.anti__item:nth-child(1) { --d: 0.05s; }
.anti__item:nth-child(2) { --d: 0.13s; }
.anti__item:nth-child(3) { --d: 0.21s; }
.anti__item:nth-child(4) { --d: 0.29s; }

.anti__item:hover {
  border-color: rgba(var(--p7-rgb), 0.55);
  background: rgba(var(--p7-rgb), 0.05);
  box-shadow: 0 18px 40px -30px rgba(0, 0, 0, 0.95),
              0 0 34px -22px rgba(var(--p7-rgb), 0.9);
  transform: translateY(-4px);
}
.anti__item:hover b { color: var(--coral); }
.anti__item b { transition: color 0.3s ease; }
.anti__item p { transition: color 0.3s ease; }
.anti__item:hover p { color: var(--text-2); }

/* the closing line is the point of the section, so it gets a ground */
.anti__note {
  padding: 0.95rem 1.15rem;
  border-radius: var(--radius);
  border-left: 2px solid var(--coral);
  background: rgba(var(--p7-rgb), 0.06);
}

@media (prefers-reduced-motion: reduce) {
  .anti__head svg { animation: none; }
  .anti__item { opacity: 1; transform: none; transition: none; }
  .anti__item:hover { transform: none; }
}


/* ============================================================
   25. Step ticker (.stp)

   Sits above a staged panel and names what is happening as it happens.
   The delays are set in feature.js from the same clock that drives the
   panel's --d values, so a label lights as its stage begins.
   ============================================================ */

.stp {
  list-style: none;
  margin: 0 0 1.1rem;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 190px), 1fr));
  gap: 0.5rem;
  counter-reset: stp;
}

.stp__s {
  position: relative;
  display: grid;
  grid-template-columns: 1.65rem 1fr;
  grid-template-rows: auto auto;
  gap: 0.1rem 0.6rem;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: rgba(var(--ink-rgb), 0.45);
  counter-increment: stp;
  transition: border-color 0.4s ease, background 0.4s ease, box-shadow 0.5s ease;
}

/* the marker: a number that becomes a tick once its stage is finished */
.stp__s i {
  grid-row: 1 / 3;
  align-self: center;
  display: grid;
  place-items: center;
  width: 1.65rem;
  height: 1.65rem;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--ink-3);
  color: var(--muted-2);
  font-family: var(--font-display);
  font-size: 0.76rem;
  font-weight: 600;
  font-style: normal;
  transition: background 0.4s ease, border-color 0.4s ease, color 0.4s ease,
              transform 0.45s var(--ease);
}
.stp__s i::before { content: counter(stp); }

.stp__s b {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--muted-2);
  transition: color 0.4s ease;
}
.stp__s span {
  font-size: 0.8rem;
  line-height: 1.45;
  color: var(--muted-3);
  transition: color 0.4s ease;
}

/* the stage currently running */
.stp__s.is-live {
  border-color: rgba(var(--accent-rgb), 0.5);
  background: rgba(var(--accent-rgb), 0.07);
  box-shadow: 0 0 34px -20px rgba(var(--accent-rgb), 0.9);
}
.stp__s.is-live i {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  transform: scale(1.1);
  animation: stp-beat 1s ease-in-out infinite;
}
.stp__s.is-live b { color: var(--paper); }
.stp__s.is-live span { color: var(--muted); }

@keyframes stp-beat {
  0%, 100% { box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0.45); }
  60%      { box-shadow: 0 0 0 7px rgba(var(--accent-rgb), 0); }
}

/* a stage that has finished swaps its number for a tick */
.stp__s.is-done { border-color: var(--line); background: rgba(var(--ink-rgb), 0.5); }
.stp__s.is-done i {
  background: rgba(var(--accent-rgb), 0.16);
  border-color: rgba(var(--accent-rgb), 0.5);
  color: var(--accent-ink);
  transform: none;
  animation: none;
}
.stp__s.is-done i::before { content: "\2713"; }
.stp__s.is-done b { color: var(--text-2); }

/* the run travels left to right along the strip */
.stp__s:not(:last-child)::after {
  content: "";
  position: absolute;
  top: 50%;
  right: -0.5rem;
  width: 0.5rem;
  height: 1px;
  background: var(--line);
  transition: background 0.4s ease;
}
.stp__s.is-done::after { background: rgba(var(--accent-rgb), 0.55); }
@media (max-width: 760px) { .stp__s::after { display: none; } }

@media (prefers-reduced-motion: reduce) {
  .stp__s.is-live i { animation: none; transform: none; }
  .stp__s { transition: none; }
}


/* ---------- #paper: spacing around the staged pair ----------

   The gap above was two paddings stacking: the pull-quote band's section
   already carries its own generous internal padding, and .section adds up to
   8.5rem on top of that. Trimmed at both ends, then the three blocks inside
   — head, ticker, panels — pushed further apart so the sequence reads as
   three moments rather than one column. */

.section--tight:has(.svc-line) { padding-bottom: clamp(1.4rem, 3vw, 2.4rem); }

#paper { padding-top: clamp(2.4rem, 4.5vw, 4rem); }

#paper .section-head { margin-bottom: clamp(3rem, 5.5vw, 4.8rem); }

.stp { margin: 0 0 clamp(2.2rem, 4.5vw, 3.6rem); }


/* ============================================================
   27. Waterfall — continuous motion   (#lost, analytics)

   A chart that has finished animating is a static image. These keep running:
   the hatching travels along each drop, the two solid columns breathe, and a
   slow light crosses the panel.
   ============================================================ */

.cost { position: relative; overflow: hidden; isolation: isolate; }
.cost__inner { position: relative; z-index: 1; }

/* a wide highlight crosses the panel on a long loop */
.cost::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: -35%;
  width: 28%;
  z-index: 0;
  pointer-events: none;
  transform: skewX(-14deg);
  background: linear-gradient(90deg, transparent, rgba(var(--accent-rgb), 0.07), transparent);
  animation: cost-sweep 9s var(--ease-io) infinite;
}
@keyframes cost-sweep {
  0%        { left: -35%; }
  60%, 100% { left: 128%; }
}

/* the hatching travels along each drop.
   The pattern is 45deg with a 12px period, so one full period along x is
   12 x root2 = 16.97px — shifting by exactly that loops seamlessly. */
.cost.is-in .cost__block--cut {
  background-size: 16.97px 16.97px;
  animation: cost-hatch 1.6s linear infinite;
}
@keyframes cost-hatch {
  from { background-position: 0 0; }
  to   { background-position: 16.97px 0; }
}

/* the two solid columns breathe rather than sitting dead */
.cost.is-in .cost__block--start { animation: cost-breathe 5s ease-in-out infinite; }
.cost.is-in .cost__block--end {
  animation: cost-breathe 5s ease-in-out infinite;
  animation-delay: 1.6s;
}
@keyframes cost-breathe {
  0%, 100% { filter: brightness(1); box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0); }
  50%      { filter: brightness(1.14); box-shadow: 0 0 26px -6px rgba(var(--accent-rgb), 0.55); }
}

/* the loss figures tick gently, so the eye keeps returning to them */
.cost.is-in .cost__val--cut { animation: cost-tick 3.2s ease-in-out infinite; }
.cost.is-in .cost__col:nth-child(2) .cost__val--cut { animation-delay: 0s; }
.cost.is-in .cost__col:nth-child(3) .cost__val--cut { animation-delay: 0.4s; }
.cost.is-in .cost__col:nth-child(4) .cost__val--cut { animation-delay: 0.8s; }
.cost.is-in .cost__col:nth-child(5) .cost__val--cut { animation-delay: 1.2s; }
@keyframes cost-tick {
  0%, 100% { opacity: 0.75; }
  50%      { opacity: 1; text-shadow: 0 0 12px rgba(var(--accent-rgb), 0.6); }
}

/* reading one column means the others step back */
.cost__col { transition: filter 0.35s ease, transform 0.35s var(--ease); }
.cost__chart:has(.cost__col:hover) .cost__col:not(:hover) { filter: grayscale(0.7) brightness(0.55); }
.cost__col:hover { transform: translateY(-4px); }
.cost__col:hover .cost__label { color: var(--accent-ink); }
.cost__label { transition: color 0.3s ease; }

@media (prefers-reduced-motion: reduce) {
  .sparks i { animation: none; opacity: 0.45; transform: none; }
  .cost::after,
  .cost.is-in .cost__block--cut,
  .cost.is-in .cost__block--start,
  .cost.is-in .cost__block--end,
  .cost.is-in .cost__val--cut { animation: none; }
  .cost__col:hover { transform: none; }
}


/* ============================================================
   28. Parent dashboard — the graphics answer back

   Four things on that page were shaped like controls and were not:
   the notification switches, the source map, the role comparison and
   the decision tree. Each keeps the markup and styling it already had;
   only state and behaviour are added.

   .alerts lives here; .hub, .cmpr and .tree live in service.css, but all
   four are used on one page only, so their new states are kept together.
   ============================================================ */

/* ---- 1 · alert switches ---- */
.alerts__row { width:100%; border:0; background:none; color:inherit; font:inherit; text-align:left; cursor:pointer; }
.alerts__row:focus-visible { outline:2px solid var(--accent-ink); outline-offset:-3px; }
.alerts__sw { position:relative; width:2.3rem; height:1.25rem; border-radius:999px; flex:none;
  background:var(--ink-3); border:1px solid var(--line); transition:background .3s ease, border-color .3s ease; }
.alerts__sw::after { content:""; position:absolute; top:2px; left:2px; width:.9rem; height:.9rem; border-radius:50%;
  background:var(--muted-2); transition:translate .3s var(--ease), background .3s ease; }
.alerts__row[aria-checked="true"] .alerts__sw { background:rgba(var(--accent-rgb),.25); border-color:var(--accent); }
.alerts__row[aria-checked="true"] .alerts__sw::after { translate:1.05rem 0; background:var(--accent); }
.alerts__row[aria-checked="false"] { opacity:.55; }
.alerts__row { transition:opacity .3s ease, background .3s ease; }
.alerts__row:hover { background:rgba(var(--accent-rgb),.05); }
.alerts__count { display:flex; align-items:center; gap:.6rem; margin-top:.9rem; padding-top:.9rem;
  border-top:1px solid var(--line-soft); font-size:.86rem; color:var(--muted); }
.alerts__count b { color:var(--accent-ink); font-variant-numeric:tabular-nums; }
.alerts__meter { flex:1; height:4px; border-radius:999px; background:var(--line-soft); overflow:hidden; }
.alerts__meter i { display:block; height:100%; width:100%; background:var(--accent); transition:width .45s var(--ease); }

/* ---- 2 · hub nodes ---- */
.hub__node, .hub__label { cursor:pointer; transition:opacity .3s ease, filter .3s ease; }
.hub[data-pick] .hub__node:not(.hub__core-shape):not(.is-pick),
.hub[data-pick] .hub__label:not(.hub__label--core):not(.is-pick) { opacity:.28; }
.hub[data-pick] .hub__spoke:not(.is-pick) { opacity:.16; }
.hub__node.is-pick { filter:drop-shadow(0 0 14px rgba(var(--accent-rgb),.8)); }
.hub__spoke.is-pick { stroke:var(--accent); stroke-width:2.4; }
.hub__note { transition:opacity .3s ease, border-color .3s ease, background .3s ease; }
.hub[data-pick] .hub__note:not(.is-pick) { opacity:.3; }
.hub__note.is-pick { border-color:rgba(var(--accent-rgb),.55)!important; background:rgba(var(--accent-rgb),.07); }

/* ---- 3 · comparison column picker ---- */
.cmpr__pick { border:0; background:none; color:inherit; font:inherit; cursor:pointer; padding:.2rem .4rem;
  border-radius:6px; transition:background .3s ease, color .3s ease; }
.cmpr__pick:hover { background:rgba(var(--accent-rgb),.1); color:var(--accent-ink); }
.cmpr__pick.is-on { background:var(--accent); color:var(--on-accent); }
.cmpr[data-col] .cmpr__cell { transition:opacity .35s ease, filter .35s ease; }
.cmpr[data-col="1"] .cmpr__cell:not(:nth-of-type(1)),
.cmpr[data-col="2"] .cmpr__cell:not(:nth-of-type(2)),
.cmpr[data-col="3"] .cmpr__cell:not(:nth-of-type(3)) { opacity:.26; filter:saturate(.5); }
.cmpr__row:hover { background:rgba(var(--accent-rgb),.05); }
.cmpr__row { transition:background .3s ease; }

/* ---- 4 · walkable tree ---- */
.tree__branch { width:100%; border:1px solid var(--line-soft); background:rgba(var(--ink-rgb),.45);
  color:inherit; font:inherit; text-align:left; cursor:pointer;
  transition:opacity .35s ease, border-color .3s ease, background .3s ease, transform .35s var(--ease); }
.tree__branch:hover { border-color:rgba(var(--accent-rgb),.5); transform:translateY(-2px); }
.tree__branch:focus-visible { outline:2px solid var(--accent-ink); outline-offset:2px; }
.tree__q[data-took] .tree__branch:not(.is-took) { opacity:.3; }
.tree__branch.is-took { border-color:var(--accent); background:rgba(var(--accent-rgb),.08);
  box-shadow:0 0 30px -18px rgba(var(--accent-rgb),.9); }
.tree__q { transition:opacity .4s ease; }
.tree__q.is-dim { opacity:.34; }
.tree__reset { margin-top:1rem; padding:.4rem .9rem; border-radius:999px; border:1px solid var(--line);
  background:var(--ink-3); color:var(--muted-2); font-size:.82rem; cursor:pointer; }
.tree__reset:hover { border-color:var(--accent); color:var(--accent-ink); }
