/* ============================================================
   HOOKED ON NIZONICS

   COPIED OUT OF THE APPROVED MOCKUP, NOT REDERIVED. Every rule below the token
   block is the mockup's own, extracted rather than retyped, because the last
   time a mockup was reinterpreted during a port the result drifted far enough
   to need unpicking.

   Three tokens are declared here because they belong to this page alone: a
   serif for the passage, and the correct/wrong pair that the passage and the
   transcript log share. Everything else comes from system.css and juq.css,
   which already define the same names the mockup used.

   PREFIXES ARE NOT OPTIONAL. juq.css owns short global class names, so every
   class here is nz-. The one shared name in the markup is .dot, which is the
   eyebrow dot, used on purpose.
   ============================================================ */

.nz-page {
  --f-read: Georgia, "Iowan Old Style", "Palatino Linotype", serif;
  --hit: #3fbe74;
  --miss: #ef6a6a;
}

/* ---------- the ladder ----------
   Five cards, and the point of the screen is that you can SEE the climb: the
   words get harder down the list and so do the three bars, together. */
.nz-tiers { display:grid; gap:var(--s-4); margin:var(--s-7) 0 var(--s-11); }
.nz-tier {
  display:grid; grid-template-columns:auto 1fr auto; gap:var(--s-6); align-items:center;
  width:100%; text-align:left; font:inherit; color:inherit; cursor:pointer;
  padding:var(--s-5) var(--s-6); border:var(--bw) solid var(--line-strong); border-radius:var(--radius);
  background:var(--surface); border-left:6px solid var(--tc);
}
/* HOVER IS THE LEVEL'S OWN COLOUR, not the site accent. The card already wears
   that colour on its number and its left edge, so lighting the rest of the
   border orange broke the one thing the row was saying. */
.nz-tier:hover { border-color:var(--tc); }
@media (max-width:860px) { .nz-tier { grid-template-columns:auto 1fr; } .nz-tier .nz-meters { grid-column:2 / -1; } }

.nz-num { font-family:var(--f-mono); font-size:clamp(26px,4vw,34px); font-weight:800; line-height:1; color:var(--tc); font-variant-numeric:tabular-nums; }
.nz-head { display:flex; align-items:baseline; gap:var(--s-4); flex-wrap:wrap; }
.nz-head h3 { margin:0; font-size:var(--t-md); font-weight:800; font-style:italic; text-transform:uppercase; letter-spacing:-.01em; }
.nz-diff { font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.14em; text-transform:uppercase; color:var(--tc); }
.nz-desc { margin:var(--s-1) 0 0; color:var(--muted); font-size:var(--t-sm); max-width:52ch; }
/* Grade, pace and personal best on ONE line, divided by a hairline rather than
   by punctuation, so the card loses three stacked paragraphs. */
.nz-facts { display:flex; flex-wrap:wrap; align-items:center; margin:var(--s-3) 0 0; font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.1em; text-transform:uppercase; color:var(--muted); }
.nz-facts span + span { margin-left:var(--s-4); padding-left:var(--s-4); border-left:1px solid var(--line-strong); }
.nz-facts b { color:var(--ink); font-weight:800; font-size:var(--t-sm); }

/* THE METERS SAY HOW STRICT, AND EACH ROW SAYS ITS OWN RULE.
   An earlier pass labelled these Spelling / Retries / Patience. "Spelling" was
   simply the wrong word, since nobody types anything: the axis measures how
   close the SPOKEN word has to land. And the other two read as amounts, so one
   filled segment beside "Retries" looked like ONE retry when tier 1 in fact
   gives you unlimited. The heading now states the direction once and every row
   carries its own rule in words, so a full bar can only mean harder. */
.nz-meters { display:flex; flex-direction:column; gap:var(--s-2); min-width:236px; }
.nz-mhead { display:flex; align-items:center; gap:var(--s-3); font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.16em; text-transform:uppercase; color:var(--muted); }
.nz-mhead::after { content:''; flex:1; height:1px; background:var(--line-strong); }
.nz-meter { display:grid; grid-template-columns:1fr auto; align-items:center; gap:var(--s-4); }
.nz-meter > span { font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.06em; text-transform:uppercase; color:var(--ink); }
.nz-bars { display:flex; gap:3px; }
.nz-bars i { width:12px; height:7px; border-radius:1px; background:var(--line-strong); }
.nz-bars i.on { background:var(--tc); }

/* ---------- reading ---------- */
.nz-read { margin:var(--s-8) 0 var(--s-11); }
.nz-readbar { display:flex; align-items:center; gap:var(--s-6); flex-wrap:wrap; margin-bottom:var(--s-6); }
.nz-readbar .spacer { flex:1; }
.nz-stat { display:flex; align-items:baseline; gap:var(--s-3); }
.nz-stat b { font-family:var(--f-mono); font-size:clamp(22px,4vw,32px); font-weight:800; line-height:1; font-variant-numeric:tabular-nums; letter-spacing:-.03em; }

.nz-passage {
  font-family:var(--f-read); font-size:clamp(19px,2.4vw,26px); line-height:1.85;
  padding:var(--s-9) var(--s-8); border:var(--bw) solid var(--ink); border-radius:var(--radius);
  background:var(--surface);
}
.nz-passage .w { padding:0 2px; border-radius:2px; transition:color .12s, background .12s; }
.nz-passage .w.hit { color:var(--hit); }
.nz-passage .w.miss { color:var(--miss); text-decoration:underline; text-decoration-style:wavy; text-decoration-thickness:1px; text-underline-offset:5px; }
.nz-passage .w.now { background:var(--accent); color:var(--accent-ink); }
.nz-passage .w.pending { color:var(--muted); }

/* ---------- WHAT THE SYSTEM HEARD ----------
   The single most useful thing this page can show, and v1 showed none of it.
   Speech recognition fails in three different ways and the reader could not
   tell them apart: a word marked wrong, a word quietly ignored as noise, and a
   word accepted despite coming back mangled all looked identical on screen.
   Every token the transcriber returns now lands here with the reason. */
.nz-log { margin-top:var(--s-5); border:var(--bw) solid var(--line-strong); border-radius:var(--radius); background:var(--surface); }
.nz-loghead { display:flex; align-items:center; gap:var(--s-3); padding:var(--s-3) var(--s-5); border-bottom:1px solid var(--line-strong); font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.16em; text-transform:uppercase; color:var(--muted); }
.nz-loghead .spacer { flex:1; }
.nz-logrows { max-height:216px; overflow-y:auto; padding:var(--s-3) var(--s-5) var(--s-4); font-family:var(--f-mono); font-size:var(--t-mono); line-height:1.9; }
.nz-logrow { display:flex; align-items:baseline; gap:var(--s-3); }
.nz-logrow .heard { color:var(--ink); }
.nz-logrow .exp { color:var(--muted); }
.nz-logrow .verdict { margin-left:auto; padding-left:var(--s-6); text-align:right; }
.nz-logrow.ok .verdict { color:var(--hit); }
.nz-logrow.bad .verdict { color:var(--miss); }
.nz-logrow.skip .heard { color:var(--muted); text-decoration:line-through; }
.nz-logrow.skip .verdict { color:var(--muted); }
.nz-logrow.end { margin-top:var(--s-3); padding-top:var(--s-3); border-top:1px solid var(--line-strong); color:var(--ink); }
.nz-logempty { color:var(--muted); }

/* The line that teaches the ladder: only ever shown when a LOWER level would
   have accepted the very word that was just marked, never as encouragement. */
.nz-why { display:flex; align-items:center; gap:var(--s-3); margin-top:var(--s-5); padding:var(--s-4) var(--s-5); border:var(--bw) solid var(--miss); border-radius:var(--radius); background:rgba(239,106,106,.10); font-family:var(--f-mono); font-size:var(--t-sm); }
.nz-why[hidden] { display:none; }
.nz-why b { color:var(--miss); }

/* ---------- leaderboards ----------
   One board per level. A Brutal 92 and a Beginner 92 are not the same thing,
   so they are never mixed into a single ranking. */
.nz-lb { margin:var(--s-10) 0 var(--s-11); }
.nz-lbhead { display:flex; align-items:baseline; gap:var(--s-6); flex-wrap:wrap; margin-bottom:var(--s-5); }
.nz-lbhead h2 { margin:0; font-size:var(--t-h2); font-weight:800; font-style:italic; text-transform:uppercase; letter-spacing:-.02em; }
.nz-lbtabs { display:flex; gap:var(--s-2); flex-wrap:wrap; }
.nz-lbtabs button { font-family:var(--f-mono); font-size:var(--t-label); letter-spacing:.1em; text-transform:uppercase; padding:var(--s-2) var(--s-4); cursor:pointer; border:var(--bw) solid var(--line-strong); border-radius:var(--radius); background:transparent; color:var(--muted); }
.nz-lbtabs button[aria-pressed="true"] { background:var(--lc,var(--accent)); border-color:var(--lc,var(--accent)); color:var(--accent-ink); }
.nz-rows { border:var(--bw) solid var(--line-strong); border-radius:var(--radius); background:var(--surface); }
.nz-row { display:flex; align-items:baseline; gap:var(--s-5); padding:var(--s-4) var(--s-5); border-bottom:1px solid var(--line); }
.nz-row:last-child { border-bottom:none; }
.nz-row .rank { font-family:var(--f-mono); font-size:var(--t-mono); color:var(--muted); min-width:2.4em; font-variant-numeric:tabular-nums; }
.nz-row .who { font-weight:800; }
.nz-row .sub { font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.1em; text-transform:uppercase; color:var(--muted); }
.nz-row .val { margin-left:auto; padding-left:var(--s-5); font-family:var(--f-mono); font-weight:800; font-size:var(--t-lg); font-variant-numeric:tabular-nums; }
.nz-row.me { background:var(--accent-soft); }
.nz-row.empty { color:var(--muted); }
/* The posted number is not accuracy: it blends accuracy with how close you
   got to the level's target pace. Unlabelled, a 99.4 beside a 74.0 reads as
   "got more words right", which is only two thirds of the truth. */
.nz-row.head { padding:var(--s-3) var(--s-5); border-bottom:1px solid var(--line-strong); font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.16em; text-transform:uppercase; color:var(--muted); }
.nz-row.head .val { font-size:var(--t-micro); font-weight:400; letter-spacing:.16em; }

.nz-acts { display:flex; gap:var(--s-6); flex-wrap:wrap; margin-top:var(--s-6); }

/* A CLASS THAT SETS display BEATS THE BROWSER'S [hidden] RULE, so anything
   hidden by attribute on this page has to say so. This exact bug shipped twice
   during the wheel port: two sets of controls on screen at once, from markup
   that looked entirely correct. */
.nz-page [hidden] { display: none !important; }

/* ============================================================
   BEYOND THE MOCKUP

   Three things the mockup had no reason to draw, because it demonstrated the
   grading with a scripted read rather than a microphone: the mic check, the
   stuck-word prompt, and the results screen. Built from the same tokens and
   the same shapes so they do not read as a different page.
   ============================================================ */

/* THE MIC CHECK EXISTS TO PREVENT THE WORST FAILURE THIS PAGE HAS: a reader
   who starts, says every word correctly, watches nothing happen, and concludes
   the game is broken. Cheaper to spend one phrase up front. */
.nz-mic { max-width:60ch; margin:var(--s-9) 0 var(--s-11); }
.nz-mic-h { margin:var(--s-3) 0 0; font-size:var(--t-h2); font-weight:800; font-style:italic; text-transform:uppercase; letter-spacing:-.02em; }
.nz-mic-p { margin:var(--s-4) 0 0; color:var(--muted); font-size:var(--t-md); }
/* THE PAGE FONT, NOT THE PASSAGE FONT. This was set in the serif on the theory
   that it is a line you read aloud, like a passage. On screen it just looked
   like the wrong font had loaded: it is the only serif anywhere near it, and
   the passage it was echoing is three screens away. The serif is the reading
   surface's signature and starts meaning nothing if other things borrow it. */
.nz-mic-phrase {
  margin:var(--s-7) 0 var(--s-6); padding:var(--s-7) var(--s-8);
  border:var(--bw) solid var(--line-strong); border-radius:var(--radius); background:var(--surface);
  font-family:var(--f-display); font-size:clamp(20px,3vw,28px); font-weight:800;
  letter-spacing:-.01em; text-align:center;
}
.nz-mic-phrase.on { border-color:var(--hit); color:var(--hit); }
.nz-mic-meter { height:10px; border:var(--bw) solid var(--line-strong); border-radius:var(--radius); background:var(--surface); overflow:hidden; }
.nz-mic-meter i { display:block; height:100%; width:0; background:var(--accent); transition:width .08s linear; }
.nz-mic-status { margin:var(--s-4) 0 0; font-family:var(--f-mono); font-size:var(--t-sm); color:var(--muted); }
.nz-mic-status.ok { color:var(--hit); }
.nz-mic-status.bad { color:var(--miss); }
.nz-warn { margin-top:var(--s-6); padding:var(--s-4) var(--s-5); border:var(--bw) solid var(--miss); border-radius:var(--radius); background:rgba(239,106,106,.10); font-family:var(--f-mono); font-size:var(--t-sm); }

/* Is it listening right now? v1 answered this and the mockup did not need to,
   because a page that is silently not listening looks exactly like a page
   waiting for you to start. */
.nz-listen { display:flex; align-items:center; gap:var(--s-3); }
.nz-dot { width:9px; height:9px; border-radius:2px; background:var(--muted); }
.nz-dot.live { background:var(--hit); animation:nz-pulse 1.6s ease-in-out infinite; }
.nz-dot.paused { background:var(--muted); }
.nz-dot.bad { background:var(--miss); }
@keyframes nz-pulse { 0%,100% { opacity:1 } 50% { opacity:.35 } }
@media (prefers-reduced-motion: reduce) { .nz-dot.live { animation:none } }

/* A WORD THE ENGINE CANNOT HEAR DOES NOT SILENTLY SKIP. Auto-advancing past it
   is how one hard word desynchronises the entire rest of the passage, and the
   reader never finds out why everything after it went red. */
.nz-passage .w.stuck { background:var(--miss); color:#fff; }
.nz-stuck { display:flex; align-items:center; gap:var(--s-4); flex-wrap:wrap; margin-top:var(--s-5); padding:var(--s-4) var(--s-5); border:var(--bw) solid var(--miss); border-radius:var(--radius); background:rgba(239,106,106,.10); font-family:var(--f-mono); font-size:var(--t-sm); }
.nz-stuck b { color:var(--miss); }
.nz-stuck span { color:var(--muted); }
.nz-skip { margin-left:auto; cursor:pointer; font-family:var(--f-mono); font-size:var(--t-label); letter-spacing:.1em; text-transform:uppercase; padding:var(--s-2) var(--s-4); border:var(--bw) solid var(--miss); border-radius:var(--radius); background:transparent; color:var(--miss); }
.nz-skip:hover { background:var(--miss); color:#fff; }

/* ---------- results ---------- */
.nz-done { margin:var(--s-8) 0 var(--s-11); }
.nz-grade { display:flex; align-items:center; gap:var(--s-8); flex-wrap:wrap; padding:var(--s-7); border:var(--bw) solid var(--line-strong); border-radius:var(--radius); background:var(--surface); border-left:6px solid var(--gc,var(--accent)); }
.nz-gletter { font-family:var(--f-mono); font-size:clamp(54px,10vw,86px); font-weight:800; line-height:.9; color:var(--gc,var(--accent)); }
.nz-gscore { display:flex; align-items:baseline; gap:var(--s-3); }
.nz-gscore b { font-family:var(--f-mono); font-size:clamp(24px,4vw,34px); font-weight:800; font-variant-numeric:tabular-nums; letter-spacing:-.03em; }
.nz-gscore span { font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.16em; text-transform:uppercase; color:var(--muted); }
.nz-glabel { margin:var(--s-3) 0 0; font-size:var(--t-md); }
.nz-gpass { margin:var(--s-2) 0 0; font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.1em; text-transform:uppercase; color:var(--muted); }
.nz-newbest { margin-left:auto; font-family:var(--f-mono); font-size:var(--t-label); letter-spacing:.14em; text-transform:uppercase; padding:var(--s-3) var(--s-5); border:var(--bw) solid var(--accent); border-radius:var(--radius); background:var(--accent-soft); color:var(--accent); }
.nz-dstats { display:flex; flex-wrap:wrap; gap:var(--s-4); margin-top:var(--s-4); }
.nz-dstat { flex:1 1 130px; padding:var(--s-5) var(--s-6); border:var(--bw) solid var(--line-strong); border-radius:var(--radius); background:var(--surface); }
.nz-dstat b { display:block; font-family:var(--f-mono); font-size:var(--t-h2); font-weight:800; font-variant-numeric:tabular-nums; letter-spacing:-.02em; }
.nz-dstat span { display:block; margin-top:var(--s-2); font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.14em; text-transform:uppercase; color:var(--muted); }


/* ============================================================
   THE STEP WIPE

   Three screens in a row are really one act: you pick a level, you check the
   microphone, you read. Swapping them instantly makes each one feel like a
   different page that happened to load, and the level you chose, the single
   most important thing about the next two minutes, vanishes the moment you
   click it.

   THE PANEL IS THE LEVEL'S COLOUR AND ITS NAME IS CUT OUT OF IT. Not printed
   on it: a genuine hole, so the page carries on showing through the letters
   while the colour sweeps past. That is why it is an SVG rather than styled
   text, and why the fill is a masked rect: CSS has no portable way to punch
   element-shaped holes in a background.

   IT IS A CLIP, NOT A SCALE. Scaling would squash the panel's contents;
   clip-path reveals it left to right and then keeps going the same way to hide
   it, so the whole thing reads as one object crossing the screen rather than
   growing and shrinking back.

   z-index sits UNDER the site's own .fx layer at 80: a real navigation away
   from this page must still cover a step wipe that is mid-flight. */
.nz-fx {
  position: fixed; inset: 0; z-index: 70;
  display: flex; flex-direction: column; justify-content: center;
  gap: var(--s-3); padding: 0 var(--s-9);
  background: var(--fc, var(--accent)); color: #0c0c0e;
  pointer-events: none;
  clip-path: inset(0 100% 0 0);
}
/* .nz-fx sits OUTSIDE .nz-page as a sibling of main, so the scoped [hidden]
   override at the foot of this file does not reach it, and display:flex beats
   the browser rule anyway. Without this line the panel covers the entire site,
   permanently, on load. */
.nz-fx[hidden] { display: none; }
.nz-fx-n { font-family: var(--f-mono); font-size: clamp(60px,16vw,180px); font-weight: 800; line-height: .8; letter-spacing: -.04em; }
.nz-fx-t { font-size: clamp(22px,5vw,44px); font-weight: 800; font-style: italic; text-transform: uppercase; letter-spacing: -.02em; }
.nz-fx-s { font-family: var(--f-mono); font-size: var(--t-label); letter-spacing: .22em; text-transform: uppercase; opacity: .72; }
