/* Speed drill — periodic-table fluency against a clock.
 *
 * Loaded alongside quiz.css (shares .quiz-btn, .quiz-wrap, .quiz-guestsave)
 * and inherits the .hdwk design tokens from movie.css. Tokens only, no raw
 * brand hex — scripts/check_css_tokens.py enforces this; the tints below are
 * color-mix() over tokens for the same reason.
 *
 * Right is TEAL and wrong is TANGERINE, never green-and-red: the science
 * app's rules bar that pairing outright, and both states also carry a ✓/✗
 * glyph so the verdict never rests on colour alone.
 *
 * The whole drill lives on one deep-teal BOARD. The old layout floated a
 * white tile on a near-white page — a third of the screen was dead and the
 * thing you were meant to look at had no ground to sit on. A filled board
 * gives the tile contrast, owns the empty space, and reads as a game, which
 * is the point: this page competes with Blooket, not with a worksheet.
 */

/* ------------------------------- board ------------------------------- */
.hdwk .quiz-wrap--drill { max-width: 980px; }

.hdwk .drill-board {
  position: relative;
  border-radius: 18px;
  background: var(--teal);
  background:
    radial-gradient(90% 70% at 50% 42%, rgba(255, 255, 255, .09), rgba(255, 255, 255, 0) 70%),
    linear-gradient(165deg,
      color-mix(in srgb, var(--teal) 84%, #000),
      var(--teal) 55%,
      color-mix(in srgb, var(--teal) 80%, var(--cyan)));
  box-shadow: 0 10px 30px rgba(0, 0, 0, .18);
  overflow: hidden; /* the clockbar + confetti stay inside the radius */
}
/* Faint dot lattice — texture so the empty parts of the board read as
   deliberate surface, not as unused page. */
.hdwk .drill-board::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: radial-gradient(rgba(255, 255, 255, .07) 1px, transparent 1.5px);
  background-size: 22px 22px;
  pointer-events: none;
}
/* The last ten seconds: the whole board leans in, not just the clock. */
.hdwk .drill-board.is-endgame {
  box-shadow: 0 0 0 3px var(--gold), 0 10px 34px rgba(0, 0, 0, .25);
  animation: drill-edge .9s ease-in-out infinite;
}
@keyframes drill-edge {
  50% { box-shadow: 0 0 0 6px var(--gold), 0 10px 34px rgba(0, 0, 0, .25); }
}
/* A miss jolts the board sideways — after the answer is committed, so the
   input the student aims at next question has already stopped moving. */
.hdwk .drill-board.is-jolt { animation: drill-jolt .28s ease-in-out; }
@keyframes drill-jolt {
  25% { transform: translateX(-4px); }
  75% { transform: translateX(4px); }
}

/* -------------------------------- HUD -------------------------------- */
.hdwk .drill-hud {
  position: relative;
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  padding: .8rem 1.1rem;
  border-bottom: 1px solid rgba(255, 255, 255, .16);
}
.hdwk .drill-hud__score {
  font-family: var(--font-display);
  font-size: 2.6rem;
  line-height: 1;
  color: #fff;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}
.hdwk .drill-hud__score .is-pop { display: inline-block; animation: drill-pop .3s cubic-bezier(.2, 2.2, .4, 1); }
@keyframes drill-pop {
  0% { transform: scale(1); }
  40% { transform: scale(1.45); }
  100% { transform: scale(1); }
}
.hdwk .drill-hud__label {
  font-size: .75rem;
  color: #fff;
  opacity: .85;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  margin-left: .35rem;
}
/* Standing personal best — visible DURING play so there is a number to
   chase, not just one to discover at the end. */
.hdwk .drill-hud__best {
  font-size: .78rem;
  font-weight: 800;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: #fff;
  opacity: .75;
  padding: .25rem .6rem;
  border: 1.5px solid rgba(255, 255, 255, .35);
  border-radius: 999px;
  white-space: nowrap;
}
.hdwk .drill-hud__best.is-beaten {
  opacity: 1;
  color: var(--gold-ink);
  background: var(--gold);
  border-color: var(--gold);
  animation: drill-pop .3s cubic-bezier(.2, 2.2, .4, 1);
}
/* Streak tiers. A run of 3 and a run of 20 must not look the same — the
   flame count, size and colour all escalate, and each tier ALSO carries a
   text label so none of it rests on colour. */
.hdwk .drill-hud__streak {
  font-size: 1rem;
  font-weight: 800;
  color: var(--gold);
  min-height: 1.2em;
}
.hdwk .drill-hud__streak .drill-streakpop { display: inline-block; animation: drill-pop .3s cubic-bezier(.2, 2.2, .4, 1); }
.hdwk .drill-hud__streak--t2 { font-size: 1.15rem; }
.hdwk .drill-hud__streak--t3,
.hdwk .drill-hud__streak--t4 {
  font-size: 1.25rem;
  padding: .15rem .7rem;
  border-radius: 999px;
  background: var(--gold);
  color: var(--gold-ink);
}
.hdwk .drill-hud__streak--t4 { animation: drill-blaze .8s ease-in-out infinite; }
@keyframes drill-blaze { 50% { transform: scale(1.08); } }
/* "+1" floats up off the score on every hit — the number changing was
   correct but silent; this is the felt version. */
.hdwk .drill-plusone {
  position: absolute;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.3rem;
  color: var(--gold);
  pointer-events: none;
  animation: drill-rise .7s ease-out forwards;
}
@keyframes drill-rise {
  from { opacity: 1; transform: translateY(0); }
  to { opacity: 0; transform: translateY(-2.2rem); }
}
.hdwk .drill-hud__clock {
  margin-left: auto;
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: 1.9rem;
  font-weight: 700;
  color: #fff;
  font-variant-numeric: tabular-nums;
}
/* The last ten seconds go gold, and pulse — the one moment the drill should
   shout. Amber-on-teal was legible but said nothing. */
.hdwk .drill-hud__clock.is-urgent {
  color: var(--gold);
  animation: drill-pulse .5s steps(2, end) infinite;
}
@keyframes drill-pulse { 50% { opacity: .45; } }
.hdwk .drill-hud__stop {
  padding: .35rem .9rem;
  font-size: .85rem;
  background: rgba(255, 255, 255, .16);
  color: #fff;
  border: 1.5px solid rgba(255, 255, 255, .5);
}
.hdwk .drill-hud__stop:hover { background: rgba(255, 255, 255, .28); }

.hdwk .drill-clockbar {
  height: 8px;
  background: rgba(255, 255, 255, .18);
  overflow: hidden;
}
.hdwk .drill-clockbar__fill {
  height: 100%;
  width: 100%;
  background: linear-gradient(90deg, var(--gold), var(--accent));
  transition: width .1s linear;
}
.hdwk .drill-hint {
  margin: .8rem 0 0;
  font-size: .85rem;
  color: #fff;
  opacity: .8;
  text-align: center;
}

/* ------------------------------- stage ------------------------------- */
.hdwk .drill-stage {
  position: relative;
  padding: 0 1.25rem 1.75rem;
  min-height: 24rem;
}
.hdwk .drill-card { position: relative; }

/* The verdict gets a RESERVED row of its own. It used to be absolutely
   positioned over the stage, which put "✓ Yes" on top of the tile the student
   was still reading. A fixed-height row means it never overlaps and never
   shifts the answer box — a moving target is the one thing a speed drill
   cannot have. */
.hdwk .drill-verdictrow {
  height: 3.2rem;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: .35rem;
}
.hdwk .drill-verdict {
  font-family: var(--font-display);
  font-size: 1.3rem;
  font-weight: 800;
  line-height: 1;
  padding: .55rem 1.4rem;
  border-radius: 999px;
  pointer-events: none;
  opacity: 0;
}
.hdwk .drill-verdict.is-right,
.hdwk .drill-verdict.is-wrong {
  opacity: 1;
  animation: drill-verdict-in .22s cubic-bezier(.2, 1.8, .4, 1);
}
@keyframes drill-verdict-in {
  from { transform: scale(.6); }
  to { transform: scale(1); }
}
/* Right = white-on-board with teal ink (white on --teal2 fails the token
   contrast rule); wrong = tangerine. Both carry their glyph. */
.hdwk .drill-verdict.is-right { background: #fff; color: var(--teal); }
.hdwk .drill-verdict.is-wrong { background: var(--accent); color: #fff; }

/* -------------------------------- tile ------------------------------- */
.hdwk .drill-tile {
  width: 11.5rem;
  margin: .5rem auto 1.5rem;
  padding: 1rem .5rem;
  border: 3px solid var(--teal);
  border-radius: 14px;
  background: #fff;
  /* Gold underplate: on the dark board a teal drop-shadow vanishes. */
  box-shadow: 0 7px 0 var(--gold), 0 16px 30px rgba(0, 0, 0, .3);
  text-align: center;
  font-variant-numeric: tabular-nums;
  animation: drill-deal .22s cubic-bezier(.2, 1.6, .4, 1);
}
/* Each new tile lands with a snap — transform only, so the answer box
   underneath never moves. */
@keyframes drill-deal {
  from { transform: scale(.85); opacity: .4; }
  to { transform: scale(1); opacity: 1; }
}
.hdwk .drill-tile__mass { font-size: 1.25rem; font-weight: 700; color: var(--ink); }
.hdwk .drill-tile__symbol {
  font-family: var(--font-display);
  font-size: 4rem;
  line-height: 1.05;
  font-weight: 800;
  color: var(--teal);
}
.hdwk .drill-tile__z { font-size: 1.25rem; font-weight: 700; color: var(--ink); }
.hdwk .drill-tile__name {
  margin-top: .3rem;
  font-size: .9rem;
  color: var(--muted);
  font-weight: 600;
  white-space: nowrap;
  min-height: 1.2em;
}
.hdwk .drill-tile__blank { color: var(--accent); }

.hdwk .drill-question {
  text-align: center;
  font-family: var(--font-display);
  font-size: clamp(19px, 2.4vw, 24px);
  color: #fff;
  margin: 0 0 1rem;
  padding-top: .25rem;
  text-wrap: balance;
}
/* Reserve two lines in tile mode so a question that wraps cannot push the
   answer box down — the box must sit still between questions. */
.hdwk .drill-card--tile .drill-question {
  min-height: 3.4em;
  display: flex;
  align-items: center;
  justify-content: center;
}
.hdwk .drill-answer {
  display: flex;
  gap: .5rem;
  justify-content: center;
  max-width: 22rem;
  margin: 0 auto;
}
.hdwk .drill-input {
  flex: 1;
  min-width: 0;
  padding: .6rem .8rem;
  font-size: 1.2rem;
  font-weight: 600;
  border: 1.5px solid rgba(255, 255, 255, .4);
  border-radius: 10px;
  background: #fff;
  color: var(--ink);
}
.hdwk .drill-input:focus {
  outline: 3px solid var(--gold);
  outline-offset: 1px;
  border-color: var(--gold);
}
/* On the board the primary button goes gold — teal-on-teal disappears, and
   gold is the board's "go" colour. */
.hdwk .drill-board .quiz-btn--primary { background: var(--gold); color: var(--ink); font-weight: 800; }
.hdwk .drill-board .quiz-btn--primary:hover:not(:disabled) {
  background: var(--gold);
  filter: brightness(1.08);
}
.hdwk .drill-board .quiz-btn--ghost { background: rgba(255, 255, 255, .14); color: #fff; border-color: rgba(255, 255, 255, .45); }
.hdwk .drill-board .quiz-btn--ghost:hover:not(:disabled) { background: rgba(255, 255, 255, .26); border-color: rgba(255, 255, 255, .6); }

/* ------------------------- clickable table --------------------------- */
.hdwk .drill-table {
  display: grid;
  grid-template-columns: repeat(18, minmax(0, 1fr));
  gap: 3px;
  max-width: 52rem;
  margin: .5rem auto 0;
}
.hdwk .drill-cell {
  aspect-ratio: 1;
  min-width: 0;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, .25);
  border-radius: 4px;
  background: var(--paper);
  color: var(--ink);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  transition: transform .08s ease-out;
}
.hdwk .drill-cell:hover {
  transform: translateY(-2px) scale(1.08);
  box-shadow: 0 3px 0 var(--gold);
  z-index: 1;
}
.hdwk .drill-cell:active { transform: scale(.94); }
.hdwk .drill-cell:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }
.hdwk .drill-cell__mass { font-size: clamp(6px, 1vw, 9px); color: var(--muted); opacity: .85; }
.hdwk .drill-cell__z { font-size: clamp(6px, 1vw, 9px); color: var(--muted); }
.hdwk .drill-cell__sym { font-size: clamp(9px, 1.6vw, 15px); font-weight: 700; }

/* Category colour is a reading aid AND the thing that makes the table look
   like a periodic table rather than a spreadsheet. Light chips on the dark
   board — the symbol stays the loudest thing in the cell. */
.hdwk .drill-cell--metal { background: color-mix(in srgb, var(--teal2) 24%, #fff); }
.hdwk .drill-cell--non-metal { background: color-mix(in srgb, var(--cyan) 22%, #fff); }
.hdwk .drill-cell--noble-gas { background: var(--gold-soft); }
.hdwk .drill-cell--metalloid { background: color-mix(in srgb, var(--accent) 20%, #fff); }

/* Board-shape placeholders (heavy option only) — not every square is a
   target, so these are deliberately UNlike every other cell state: no fill
   colour, a dotted (not solid/dashed) border, no symbol/number, no hover, no
   pointer. They differ from an active cell by content + border style + a
   flat non-well surface, not by hue alone. */
.hdwk .drill-cell--skeleton {
  background: rgba(255, 255, 255, .04);
  border: 1px dotted rgba(255, 255, 255, .22);
  pointer-events: none;
  cursor: default;
}
.hdwk .drill-cell--skeleton:hover { transform: none; box-shadow: none; }

.hdwk .drill-table.is-locked .drill-cell { pointer-events: none; }
/* State rules carry a blank-socket variant: the socket base rule below has
   the same specificity and later source order, and would swallow these. */
.hdwk .drill-table--blank .drill-cell--blank.is-right,
.hdwk .drill-cell.is-right {
  background: var(--teal);
  border-color: #fff;
  border-style: solid;
  color: #fff;
  box-shadow: none;
  animation: drill-pop .3s cubic-bezier(.2, 2.2, .4, 1);
  z-index: 1;
}
.hdwk .drill-cell.is-right .drill-cell__z { color: #fff; }
.hdwk .drill-table--blank .drill-cell--blank.is-wrong,
.hdwk .drill-cell.is-wrong {
  background: var(--accent);
  border-color: var(--accent);
  border-style: solid;
  color: #fff;
  box-shadow: none;
  animation: drill-pop .3s cubic-bezier(.2, 2.2, .4, 1);
  z-index: 1;
}
.hdwk .drill-cell.is-wrong .drill-cell__z { color: #fff; }
/* Where it SHOULD have gone, shown after a miss. */
.hdwk .drill-table--blank .drill-cell--blank.is-home,
.hdwk .drill-cell.is-home {
  background: var(--gold);
  border-color: var(--gold-ink);
  border-style: solid;
  box-shadow: 0 0 0 3px var(--gold-line);
  animation: drill-pop .3s cubic-bezier(.2, 2.2, .4, 1);
  z-index: 1;
}

/* --------------------------- final scorecard ------------------------- */
/* Rendered ON the board — the finale happens in the arena, not on a white
   card beside it. */
.hdwk .drill-done {
  max-width: 30rem;
  margin: 1.5rem auto .5rem;
  text-align: center;
  animation: drill-deal .25s cubic-bezier(.2, 1.6, .4, 1);
}
.hdwk .drill-done__newbest {
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.05rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--gold-ink);
  background: var(--gold);
  border-radius: 999px;
  padding: .4rem 1.1rem;
  margin-bottom: .6rem;
  animation: drill-blaze .8s ease-in-out infinite;
}
.hdwk .drill-done__score {
  font-family: var(--font-display);
  font-size: 6rem;
  line-height: 1;
  font-weight: 800;
  color: #fff;
  text-shadow: 0 6px 24px rgba(0, 0, 0, .35);
}
.hdwk .drill-done__label { margin: .35rem 0 0; color: #fff; font-weight: 600; }
/* The rank is the thing to level up — score thresholds name it, so a better
   run earns a better title, not just a bigger number. */
.hdwk .drill-done__rank {
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 800;
  color: var(--gold);
  margin: .6rem 0 0;
}
.hdwk .drill-done__stats {
  display: flex;
  justify-content: center;
  gap: 2rem;
  margin: 1.1rem 0 1.25rem;
}
.hdwk .drill-done__stat {
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 800;
  color: #fff;
}
.hdwk .drill-done__statlabel {
  display: block;
  font-family: var(--font-body);
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: #fff;
  opacity: .75;
  margin-top: .15rem;
}
.hdwk .drill-done__saving { margin-bottom: 1.25rem; font-size: .9rem; color: #fff; opacity: .9; }
.hdwk .drill-done__saving a { color: var(--gold); font-weight: 700; }
.hdwk .drill-done__actions { display: flex; gap: .6rem; justify-content: center; flex-wrap: wrap; }

/* Confetti on a new personal best — a dozen JS-spawned chips that fall from
   the top of the board. Skipped entirely under reduced motion (see JS). */
.hdwk .drill-confetti {
  position: absolute;
  top: -1rem;
  width: 10px;
  height: 14px;
  border-radius: 2px;
  pointer-events: none;
  animation: drill-confetti-fall 1.6s ease-in forwards;
}
@keyframes drill-confetti-fall {
  from { opacity: 1; transform: translateY(0) rotate(0deg); }
  to { opacity: 0; transform: translateY(28rem) rotate(540deg); }
}

@media (max-width: 640px) {
  .hdwk .drill-hud__clock { font-size: 1.3rem; }
  .hdwk .drill-hud { gap: .6rem; }
  .hdwk .drill-stage { padding: 0 .6rem 1.1rem; }
}

/* ------------------------- skill picker chips ------------------------ */
.hdwk .drill-filters {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  margin: 0 0 .9rem;
  justify-content: center;
}
.hdwk .drill-chip {
  padding: .3rem .75rem;
  border: 1.5px solid var(--line);
  border-radius: 999px;
  background: #fff;
  color: var(--teal);
  font-size: .82rem;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}
.hdwk .drill-chip:hover { border-color: var(--teal2); background: var(--soft); }
/* Chips sit on the light page, not the board — teal focus, matching quiz.css. */
.hdwk .drill-chip:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
.hdwk .drill-chip.is-active {
  background: var(--teal);
  border-color: var(--teal);
  color: #fff;
}

/* --------------------- place drill (drag a tile) --------------------- */
/* Tile beside the grid, not above it. Stacked, the tile pushed the table off
   the bottom of the screen — and in a drag you need to see both ends of the
   gesture at once. */
.hdwk .drill-placewrap {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 1.5rem;
}
/* The blank grid has no text in it, so in a centred flex row it collapses to
   nothing without an explicit basis — the cells take their height from
   aspect-ratio, which needs a width to work from. */
.hdwk .drill-placewrap .drill-table {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
}
.hdwk .drill-placewrap .drill-tile--drag { flex: 0 0 auto; }

@media (max-width: 780px) {
  .hdwk .drill-placewrap { flex-direction: column; }
  .hdwk .drill-placewrap .drill-table { width: 100%; }
  .hdwk .drill-tile--drag { width: 7rem; padding: .5rem .4rem; }
  .hdwk .drill-tile--drag .drill-tile__symbol { font-size: 2.4rem; }
}
.hdwk .drill-tile--drag {
  margin: 0;
  cursor: grab;
  touch-action: none;   /* or the browser scrolls instead of dragging */
  user-select: none;
}
.hdwk .drill-tile--drag:active { cursor: grabbing; }
.hdwk .drill-tile--drag.is-lifted { opacity: .35; }
.hdwk .drill-tile--drag:focus-visible { outline: 3px solid var(--gold); outline-offset: 3px; }

/* The dragged copy follows the pointer — as a MINI tile floating just above
   it, so the square under the cursor stays visible. Full-size, the ghost
   completely covered the drop target, which was exactly the owner's "can't
   tell where it lands" complaint. Never a hit target itself or it would
   swallow every elementFromPoint. */
.hdwk .drill-tile--ghost,
.drill-tile--ghost {
  position: fixed;
  z-index: 999;
  width: 4.2rem;
  padding: .45rem .3rem;
  margin: 0;
  transform: translate(-50%, -130%) rotate(-4deg);
  pointer-events: none;
  /* :root tokens, not .hdwk ones — the ghost is appended to <body>. */
  border: 2px solid var(--color-primary-hover, currentColor);
  border-radius: 10px;
  background: #fff;
  box-shadow: 0 3px 0 var(--gold), 0 14px 26px rgba(0, 0, 0, .4);
  text-align: center;
  font-variant-numeric: tabular-nums;
  animation: none; /* the deal-in snap belongs to the source tile, not the copy */
}
/* Only the symbol travels — the numbers stay on the source tile, which is
   still on screen for reference. Unscoped selectors on purpose: the ghost is
   appended to <body>, outside .hdwk. */
.drill-tile--ghost .drill-tile__mass,
.drill-tile--ghost .drill-tile__z,
.drill-tile--ghost .drill-tile__name { display: none; }
.drill-tile--ghost .drill-tile__symbol {
  font-family: var(--jm-font-display, "Bricolage Grotesque", Georgia, serif);
  font-size: 2rem;
  line-height: 1.05;
  font-weight: 800;
  color: var(--color-primary-hover, currentColor);
}

/* A blank grid is the question in place mode — no symbols to copy from.
   The squares are WELLS sunk into the board: filled, ringed and shadowed,
   because a 1px dashed outline on the page background was genuinely
   invisible while dragging (owner-reported). */
.hdwk .drill-table--blank .drill-cell--blank {
  background: rgba(255, 255, 255, .14);
  border: 2px dashed rgba(255, 255, 255, .5);
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, .3);
}
.hdwk .drill-table--blank .drill-cell--blank:hover {
  background: rgba(255, 255, 255, .24);
  transform: none;
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, .3);
}
/* The square under the dragged tile must be unmistakable at a glance:
   solid gold, white ring, lifted — nothing else on the board looks like it. */
.hdwk .drill-cell.is-over,
.hdwk .drill-table--blank .drill-cell--blank.is-over {
  background: var(--gold);
  border: 2px solid #fff;
  box-shadow: 0 0 0 4px var(--gold-line), 0 6px 16px rgba(0, 0, 0, .35);
  transform: scale(1.25);
  z-index: 2;
}
/* Period / group labels on the grid edges (place mode): a way to navigate to
   a square that does not give the answer away — the student still has to
   know WHICH group and period. Transition-block columns carry no group
   number, matching the booklet's convention. */
.hdwk .drill-table--labeled {
  grid-template-columns: 1.3rem repeat(18, minmax(0, 1fr));
}
.hdwk .drill-glabel {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: "Space Mono", ui-monospace, monospace;
  font-size: clamp(8px, 1.1vw, 12px);
  font-weight: 700;
  color: #fff;
  opacity: .7;
  pointer-events: none;
}

.hdwk .drill-filters--range { margin-bottom: 1rem; align-items: center; }
.hdwk .drill-filters__label {
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--muted);
  margin-right: .2rem;
}
.hdwk .drill-chip--range.is-active { background: var(--teal2); border-color: var(--teal2); }

.hdwk .drill-filters--heavy { margin-bottom: 1rem; }
/* A distinct fill from both the skill (teal) and range (teal2) chips, since
   this is a different kind of control — an on/off extra, not a mutually
   exclusive pick from a set. */
.hdwk .drill-chip--heavy.is-active {
  background: var(--gold);
  border-color: var(--gold);
  color: var(--ink);
}

/* ---------------------- phone-sized tap targets ---------------------- */
/* This block must sit BELOW every .drill-table / --labeled / --blank rule:
   it wins on source order, not specificity. 18 squeezed columns gave ~17px
   tap targets at 390px — in a TIMED drill a mis-tap records a wrong answer
   into the mastery record, so targets must be thumb-sized. Fixed 44px
   tracks and the grid scrolls sideways instead of shrinking: the table
   keeps its real shape (the shape IS the content), and a question is
   answered by scrolling to a region, not by sniping a 17px square. */
@media (max-width: 640px) {
  .hdwk .drill-table {
    grid-template-columns: repeat(18, 44px);
    gap: 2px;
    max-width: 100%;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    /* Vertical padding gives the is-over/is-right pop room to scale without
       spawning a jittery vertical scrollbar inside the scroll container. */
    padding: 6px 0 .35rem;
    scrollbar-width: thin;
  }
  /* Period labels stay in the first track; group labels travel with their
     own columns, so column navigation survives the scroll. */
  .hdwk .drill-table--labeled { grid-template-columns: 1.4rem repeat(18, 44px); }
  /* Fixed cells no longer need viewport-relative type. */
  .hdwk .drill-cell__mass { font-size: 9px; }
  .hdwk .drill-cell__z { font-size: 10px; }
  .hdwk .drill-cell__sym { font-size: 16px; }
  .hdwk .drill-glabel { font-size: 11px; }
}

/* ---------------------- reduced motion, all of it -------------------- */
@media (prefers-reduced-motion: reduce) {
  .hdwk .drill-clockbar__fill { transition: none; }
  .hdwk .drill-cell { transition: none; }
  .hdwk .drill-cell:hover { transform: none; }
  .hdwk .drill-cell:active { transform: none; }
  .hdwk .drill-cell.is-over,
  .hdwk .drill-table--blank .drill-cell--blank.is-over { transform: none; }
  .hdwk .drill-tile,
  .hdwk .drill-done,
  .hdwk .drill-verdict.is-right,
  .hdwk .drill-verdict.is-wrong,
  .hdwk .drill-cell.is-right,
  .hdwk .drill-cell.is-wrong,
  .hdwk .drill-cell.is-home,
  .hdwk .drill-hud__score .is-pop,
  .hdwk .drill-hud__streak .drill-streakpop,
  .hdwk .drill-hud__streak--t4,
  .hdwk .drill-hud__best.is-beaten,
  .hdwk .drill-done__newbest,
  .hdwk .drill-board.is-endgame,
  .hdwk .drill-board.is-jolt,
  .hdwk .drill-plusone,
  .hdwk .drill-confetti { animation: none; }
  /* Endgame keeps its static gold ring — the signal survives, the pulse goes. */
  .hdwk .drill-board.is-endgame { box-shadow: 0 0 0 4px var(--gold), 0 10px 34px rgba(0, 0, 0, .25); }
  .hdwk .drill-plusone { display: none; }
}

/* ===================== element progress =============================== */
/* The mastery grid gets the same board as the drills — it is the trophy
   room the drills fill in, and it should look like part of the same game. */
.hdwk .ep-board {
  position: relative;
  border-radius: 18px;
  padding: 1.5rem 1.25rem 1.25rem;
  background: var(--teal);
  background:
    radial-gradient(90% 70% at 50% 42%, rgba(255, 255, 255, .09), rgba(255, 255, 255, 0) 70%),
    linear-gradient(165deg,
      color-mix(in srgb, var(--teal) 84%, #000),
      var(--teal) 55%,
      color-mix(in srgb, var(--teal) 80%, var(--cyan)));
  box-shadow: 0 10px 30px rgba(0, 0, 0, .18);
  margin-bottom: 1.5rem;
}
.hdwk .ep-summary { text-align: center; color: var(--ink); margin: .5rem 0 1rem; }
.hdwk .ep-h {
  font-family: var(--font-display);
  font-size: 1.1rem;
  color: var(--teal);
  margin: 1.75rem 0 .25rem;
}
.hdwk .ep-note { font-size: .85rem; color: var(--muted); margin: 0 0 .6rem; }

.hdwk .ep-grid {
  display: grid;
  grid-template-columns: repeat(18, minmax(0, 1fr));
  gap: 3px;
  max-width: 52rem;
  margin: 0 auto;
}
.hdwk .ep-cell {
  aspect-ratio: 1;
  min-width: 0;
  position: relative;
  border-radius: 4px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  border: 1px solid rgba(255, 255, 255, .25);
}
.hdwk .ep-cell__z { font-size: clamp(6px, 1vw, 9px); opacity: .7; }
.hdwk .ep-cell__sym { font-size: clamp(9px, 1.6vw, 15px); font-weight: 700; }

/* A band must never rest on hue alone (colour-blind rule): each tested band
   carries a corner glyph readable on the tile itself — ✓ solid, ~ getting
   there, ! shaky, ? barely tried. "Not tried" already reads without colour
   as the dashed empty socket. The legend swatches carry the same glyphs so
   the mapping teaches itself. */
.hdwk .ep-cell::after {
  position: absolute;
  top: 1px;
  right: 2px;
  font-size: clamp(7px, 1.1vw, 10px);
  font-weight: 700;
  line-height: 1;
  opacity: .9;
}
.hdwk .ep-cell.ep-cell--solid::after,
.hdwk .ep-swatch.ep-cell--solid::after { content: "✓"; }
.hdwk .ep-cell.ep-cell--getting-there::after,
.hdwk .ep-swatch.ep-cell--getting-there::after { content: "~"; }
.hdwk .ep-cell.ep-cell--shaky::after,
.hdwk .ep-swatch.ep-cell--shaky::after { content: "!"; }
.hdwk .ep-cell.ep-cell--thin::after,
.hdwk .ep-swatch.ep-cell--thin::after { content: "?"; }

/* One scale, four steps. Teal = solid through to tangerine = shaky, with
   untested left as an empty socket — a colour for "no data" would imply a
   judgement nobody has earned. Never red/green: colour-blind rule, and every
   band is also stated in the key and the tooltip. */
.hdwk .ep-cell--solid { background: color-mix(in srgb, var(--teal2) 30%, #fff); border-color: var(--teal2); color: var(--teal); }
.hdwk .ep-cell--getting-there { background: color-mix(in srgb, var(--cyan) 24%, #fff); border-color: var(--cyan); color: var(--teal); }
.hdwk .ep-cell--shaky { background: var(--accent); border-color: var(--accent); color: #fff; }
.hdwk .ep-cell--thin { background: var(--gold-soft); border-color: var(--gold-line); color: var(--gold-ink); }
.hdwk .ep-cell--untested { background: rgba(255, 255, 255, .1); border-style: dashed; border-color: rgba(255, 255, 255, .35); color: #fff; }

.hdwk .ep-key {
  display: flex;
  flex-wrap: wrap;
  gap: .9rem;
  justify-content: center;
  list-style: none;
  padding: 0;
  margin: .9rem 0 0;
  font-size: .8rem;
  color: #fff;
}
.hdwk .ep-key li { display: flex; align-items: center; gap: .35rem; }
.hdwk .ep-swatch {
  width: 17px;
  height: 17px;
  border-radius: 3px;
  display: inline-grid;
  place-items: center;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  border: 1px solid rgba(255, 255, 255, .4);
}

.hdwk .ep-list { list-style: none; padding: 0; margin: 0; }
.hdwk .ep-list li {
  padding: .4rem .6rem;
  border-bottom: 1px solid var(--line);
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  font-size: .92rem;
}
.hdwk .ep-list__n { color: var(--muted); font-size: .85rem; white-space: nowrap; }

.hdwk .ep-pill {
  display: inline-block;
  padding: .12rem .5rem;
  border-radius: 999px;
  font-weight: 700;
  font-size: .8rem;
}
/* The by-skill pills sit on the white page, not the board — the pale board
   tints would wash out there, so the pills keep solid readable fills. */
.hdwk .ep-pill.ep-cell--solid { background: var(--teal); border-color: var(--teal); color: #fff; }
.hdwk .ep-pill.ep-cell--getting-there { background: var(--teal2); border-color: var(--teal2); color: #fff; }
.hdwk .ep-table { margin-top: .4rem; }
.hdwk .ep-empty { text-align: center; margin: 2rem 0; color: var(--muted); }
.hdwk .ep-empty p { margin-bottom: .9rem; }

@media (max-width: 640px) {
  /* Same call as the drill grids: 18 squeezed columns shrink the tiles (and
     their band glyphs) below legibility at phone width. Fixed tracks and a
     sideways scroll keep the table shaped like the real thing. */
  .hdwk .ep-grid {
    grid-template-columns: repeat(18, 44px);
    max-width: 100%;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    padding-bottom: .35rem;
    scrollbar-width: thin;
  }
  .hdwk .ep-cell__z { font-size: 9px; }
  .hdwk .ep-cell__sym { font-size: 15px; }
  .hdwk .ep-cell::after { font-size: 10px; }
  .hdwk .ep-board { padding: 1rem .75rem .9rem; }
}
