/* ============================================================
   CutSite  -  styles
   Palette (a fluorescence-imaging look):
     --bg      deep imaging-screen navy
     --panel   raised panel
     --glow     bioluminescent cyan  (the fluorescing target)
     --cas9     amber                (the scissors / Cas9)
   DNA bases use a genomics-style coloring:
     A green, T red, G amber, C blue
   ============================================================ */

:root {
  --bg: #070d1a;
  --bg-2: #0b1424;
  --panel: #0f1a30;
  --panel-line: #1c2c4a;
  --ink: #e8eef7;
  --muted: #8598b6;
  --glow: #2ee6c8;
  --glow-soft: rgba(46, 230, 200, 0.28);
  --cas9: #ffb24d;
  --danger: #ff6b6b;
  --base-a: #46d17f;
  --base-t: #ff6f6f;
  --base-g: #f5b73d;
  --base-c: #4aa3ff;
  --radius: 16px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  min-height: 100%;
}

body {
  background:
    radial-gradient(900px 500px at 50% -10%, #12213d 0%, transparent 60%),
    radial-gradient(700px 500px at 90% 110%, #0d1830 0%, transparent 55%),
    var(--bg);
  color: var(--ink);
  font-family: "Space Grotesk", system-ui, sans-serif;
  display: flex;
  justify-content: center;
  padding: 28px 18px 40px;
}

/* ---------- custom scissors cursor ---------- */
.scissors {
  position: fixed;
  color: var(--cas9);
  pointer-events: none;
  transform: translate(-50%, -50%);
  z-index: 60;
  filter: drop-shadow(0 2px 6px rgba(0,0,0,0.6));
  opacity: 0;
  transition: opacity 0.2s ease;
}
.scissors.active { opacity: 1; }
/* Closing fully swings the handles a hair outside the 32x32 viewBox.
   Nothing here is interactive, so let the artwork spill rather than clip. */
.scissors svg { overflow: visible; }
/* Both arms pivot about the screw at (16,16) in the SVG's own coordinates.
   This used to be 6px 16px - out by the handles - so the blades swung around
   the wrong point and the snip flailed instead of closing. */
.scissors .blade {
  transform-box: view-box;
  transform-origin: 16px 16px;
  transition: transform 90ms ease;
}
/* SVG y runs downwards, so a positive rotation is clockwise on screen.
   Closing the tips means the lower arm goes up and the upper arm goes down.
   39.8deg is exactly the rest half-angle (atan2(10,12)), so the blades meet
   flush instead of stopping short. Shutting is quicker than reopening, which
   is what makes it read as a snip rather than a wobble. */
.scissors.snip .blade        { transition-duration: 55ms; }
.scissors.snip .blade-top    { transform: rotate(-39.8deg); }
.scissors.snip .blade-bottom { transform: rotate(39.8deg); }
/* blades jammed after an off-target cut: stuck open and red until they free up */
.scissors.jammed { color: var(--danger); animation: jamShudder 0.45s ease; }
.scissors.jammed .blade-top    { transform: rotate(14deg); }
.scissors.jammed .blade-bottom { transform: rotate(-14deg); }
@keyframes jamShudder {
  0%, 100% { margin-left: 0; }
  20% { margin-left: -3px; }
  50% { margin-left: 3px; }
  80% { margin-left: -2px; }
}

/* ---------- layout ---------- */
.app {
  width: 100%;
  max-width: 860px;
}

.topbar { margin-bottom: 20px; }
.brand { display: flex; align-items: center; gap: 12px; }
.brand-mark {
  font-family: "JetBrains Mono", monospace;
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  color: var(--cas9);
  border: 1px solid var(--cas9);
  padding: 4px 8px;
  border-radius: 999px;
  text-transform: uppercase;
}
.brand h1 {
  margin: 0;
  font-size: 1.9rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}
.tagline { margin: 8px 2px 0; color: var(--muted); font-size: 0.98rem; }

/* ---------- HUD ---------- */
.hud {
  display: grid;
  /* auto-fit lets the row rebalance when the Time stat is hidden in zen mode */
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px;
  margin-bottom: 18px;
}
.stat {
  background: linear-gradient(180deg, var(--panel), var(--bg-2));
  border: 1px solid var(--panel-line);
  border-radius: 12px;
  padding: 12px 14px;
}
.stat-label {
  display: block;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--muted);
}
.stat-value {
  display: block;
  margin-top: 4px;
  font-family: "JetBrains Mono", monospace;
  font-weight: 700;
  font-size: 1.5rem;
  font-variant-numeric: tabular-nums;
}
#time.low { color: var(--danger); }
#difficulty {
  transition: color 0.3s ease;
}
#difficulty[data-level="high"] { color: var(--danger); }
#difficulty[data-level="med"] { color: var(--cas9); }
.stat-value.bump { animation: bump 0.28s ease; }
@keyframes bump {
  0% { transform: scale(1); }
  40% { transform: scale(1.22); color: var(--glow); }
  100% { transform: scale(1); }
}

/* ---------- stage ---------- */
.stage {
  position: relative;
  background:
    linear-gradient(180deg, rgba(20,34,60,0.6), rgba(9,16,30,0.6));
  border: 1px solid var(--panel-line);
  border-radius: var(--radius);
  padding: 30px 22px 34px;
  min-height: 448px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  overflow: hidden;
  transition: transform 0.08s ease;
  /* rapid tapping must never trigger double-tap zoom, long-press must not
     start selecting base letters, and taps should not flash a highlight */
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}
.stage.shake {
  animation: shake 0.2s ease;
}
@keyframes shake {
  0%, 100% { transform: translate(0, 0); }
  25% { transform: translate(-2px, -1px); }
  50% { transform: translate(2px, 1px); }
  75% { transform: translate(-1px, 2px); }
}
.stage::before {
  /* faint imaging-grid texture */
  content: "";
  position: absolute; inset: 0;
  background-image:
    linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
  background-size: 26px 26px;
  pointer-events: none;
}
.status {
  position: relative;
  text-align: center;
  font-family: "JetBrains Mono", monospace;
  font-size: 0.82rem;
  letter-spacing: 0.05em;
  color: var(--muted);
  margin-bottom: 26px;
  min-height: 1.2em;
}
.status.hot { color: var(--glow); }
.accuracy-display {
  position: relative;
  text-align: center;
  font-family: "JetBrains Mono", monospace;
  font-size: 0.75rem;
  letter-spacing: 0.05em;
  color: var(--muted);
  margin-bottom: 18px;
  min-height: 1em;
  transition: color 0.2s ease;
}

/* ---------- DNA helix ---------- */
.helix { position: relative; padding: 20px 22px; }

/* DNA is antiparallel, and it matters here: the PAM and the protospacer sit on
   the top strand, while the guide RNA pairs with the bottom one. */
.polarity {
  position: absolute;
  font-family: "JetBrains Mono", monospace;
  font-size: 0.58rem;
  letter-spacing: 0.06em;
  color: var(--muted);
  opacity: 0.8;
}
.polarity-tl { left: 0;  top: 24px; }
.polarity-tr { right: 0; top: 24px; }
.polarity-bl { left: 0;  bottom: 24px; }
.polarity-br { right: 0; bottom: 24px; }
.backbone {
  position: absolute;
  left: 22px; right: 22px;
  height: 3px;
  border-radius: 3px;
  background: linear-gradient(90deg, rgba(51,80,127,0), #33507f, rgba(51,80,127,0));
}
.backbone-top { top: 12px; }
.backbone-bottom { bottom: 12px; }

.strand {
  position: relative;
  display: flex;
  gap: 2px;
  padding: 0 4px;
  cursor: none;
}
.col {
  position: relative;
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 6px 0;
  border-radius: 8px;
  transition: background 0.18s ease, transform 0.12s ease;
}
.base {
  width: 100%;
  max-width: 26px;
  aspect-ratio: 1 / 1;
  display: grid;
  place-items: center;
  border-radius: 6px;
  font-family: "JetBrains Mono", monospace;
  font-weight: 700;
  font-size: 0.8rem;
  color: #06101f;
  opacity: 0.9;
}
.rung {
  position: absolute;
  top: 50%;
  width: 2px;
  height: 22px;
  transform: translateY(-50%);
  background: rgba(120,150,190,0.35);
}
.base-A { background: var(--base-a); }
.base-T { background: var(--base-t); }
.base-G { background: var(--base-g); }
.base-C { background: var(--base-c); }
.base-U { background: var(--base-t); } /* RNA: uracil takes thymine's place */

/* ---------- guide RNA readout ---------- */
.guide {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 6px;
  margin-bottom: 18px;
  font-family: "JetBrains Mono", monospace;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  color: var(--muted);
  min-height: 20px;
}
.guide-label { text-transform: uppercase; letter-spacing: 0.16em; font-size: 0.62rem; }
.guide-end { opacity: 0.7; }
.guide-strand { font-size: 0.62rem; letter-spacing: 0.06em; color: var(--muted); margin-left: 4px; }
.guide-seq { display: inline-flex; gap: 2px; min-width: 1.5em; justify-content: center; }
.guide-seq .base {
  width: 18px;
  max-width: 18px;
  font-size: 0.68rem;
  border-radius: 4px;
  opacity: 1;
}

/* active target: the fluorescing window */
/* Every candidate site glows the same way. In Guide RNA mode the decoys are
   deliberately indistinguishable from the real target by look alone: the
   only way to tell is to read the sequence and the PAM. */
.col.candidate {
  background: var(--glow-soft);
  animation: fluoresce 0.9s ease-in-out infinite;
}
.col.candidate .base { opacity: 1; box-shadow: 0 0 12px 2px var(--glow-soft); }
/* Which strand carries the site: its bases glow in full and the other
   strand's sit dimmer. Together with the PAM's side that is the whole
   reading cue - a PAM to the left means a bottom-strand site, read right to
   left. Screen readers get the same fact in the column label. */
.col.candidate.on-top .base:last-child,
.col.candidate.on-bottom .base:first-child { opacity: 0.45; box-shadow: none; }
@keyframes fluoresce {
  0%, 100% { box-shadow: 0 0 0 0 rgba(46,230,200,0); }
  50% { box-shadow: 0 0 18px 3px var(--glow-soft); }
}

/* The PAM motif: all three bases tinted, but only the middle one carries the
   label, centred so it reads as one tag across NGG rather than once per base. */
.col.pam { background: rgba(255, 178, 77, 0.12); }
.col.pam-label::after {
  content: "PAM";
  position: absolute;
  bottom: -16px;
  left: 50%;
  transform: translateX(-50%);
  font-family: "JetBrains Mono", monospace;
  font-size: 0.52rem;
  letter-spacing: 0.12em;
  color: var(--cas9);
  white-space: nowrap;
}

/* The scissile position: a blunt break 3 bp upstream of the PAM. The line sits
   on the leading edge of the base immediately 3' of where the duplex parts. */
.col.cut-site::before {
  content: "";
  position: absolute;
  left: -1px;
  top: -2px;
  bottom: -2px;
  width: 2px;
  border-radius: 2px;
  background: repeating-linear-gradient(
    180deg, var(--cas9) 0 4px, transparent 4px 7px);
  opacity: 0.75;
}
/* the moment the duplex parts: the line snaps bright and square */
.col.breaking::before {
  animation: dsb 0.4s ease forwards;
}
@keyframes dsb {
  0%   { background: #ffffff; opacity: 1; width: 3px; left: -1.5px; }
  100% { background: var(--cas9); opacity: 0; width: 3px; left: -1.5px; }
}

/* an off-target cut: the column recoils red instead of flashing white */
.col.off-target {
  animation: offtargetflash 0.45s ease forwards;
}
@keyframes offtargetflash {
  0%   { background: var(--danger); transform: translateY(3px); }
  40%  { background: rgba(255,107,107,0.35); transform: translateY(0); }
  100% { background: transparent; transform: translateY(0); }
}

/* the moment of the cut */
.col.cut {
  animation: cutflash 0.4s ease forwards;
}
@keyframes cutflash {
  0% { background: #ffffff; transform: scale(1.15); }
  100% { background: transparent; transform: scale(1); }
}

/* floating +points text */
.pop {
  position: absolute;
  transform: translate(-50%, 0);
  font-family: "JetBrains Mono", monospace;
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--glow);
  pointer-events: none;
  animation: rise 0.7s ease forwards;
  z-index: 40;
}
/* a tolerated off-target cut: same rise, but nothing to celebrate */
.pop.bad { color: var(--danger); font-size: 0.72rem; letter-spacing: 0.12em; text-transform: uppercase; }
@keyframes rise {
  0% { opacity: 0; transform: translate(-50%, 6px); }
  20% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -30px); }
}

/* particle burst on cut */
.particle {
  position: absolute;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--glow);
  pointer-events: none;
  animation: burst 0.6s ease forwards;
  box-shadow: 0 0 8px var(--glow);
}
@keyframes burst {
  0% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  100% { opacity: 0; transform: translate(calc(-50% + var(--vx, 0)), calc(-50% + var(--vy, 0))) scale(0); }
}

/* ---------- overlay ---------- */
.overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* "safe" keeps a card taller than the stage from overflowing past the top
     edge, where it would be clipped and unreachable; it top-aligns instead
     and the overlay scrolls. Browsers without it fall back to flex-start,
     which is also never clipped. */
  justify-content: safe center;
  gap: 10px;
  padding: 14px;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: rgba(6, 12, 24, 0.72);
  backdrop-filter: blur(3px);
  z-index: 50;
}
.overlay > * { flex-shrink: 0; }
.overlay.hidden { display: none; }
.card {
  width: min(460px, 95%);
  text-align: center;
  background: linear-gradient(180deg, var(--panel), var(--bg-2));
  border: 1px solid var(--panel-line);
  border-radius: var(--radius);
  padding: 14px 12px;
}
.card.hidden { display: none; }

/* The end card is wider than the start card: its achievements and the top
   ten sit side by side, which is what keeps it inside the stage with no
   scrolling once both are on show. Narrow screens stack them and scroll. */
#card-end { width: min(700px, 95%); }
.end-body {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  align-items: start;
  margin: 4px 0 6px;
}
/* Grid children default to min-width: auto, which lets a block wider than
   its column push the whole body past the card edge instead of shrinking. */
.end-body > * { min-width: 0; }
.end-body .achievements-earned,
.end-body .leaderboard { margin: 0; }
@media (max-width: 720px) {
  #card-end { width: min(460px, 95%); }
  .end-body { grid-template-columns: 1fr; }
}
.card h2 { margin: 0 0 8px; font-size: 1.3rem; }
.card p { color: var(--muted); font-size: 0.92rem; line-height: 1.45; margin: 0 0 12px; }
.card b { color: var(--glow); font-weight: 700; }

.btn {
  display: inline-block;
  font-family: inherit;
  font-weight: 700;
  font-size: 1rem;
  color: #06101f;
  background: var(--glow);
  border: none;
  border-radius: 10px;
  padding: 12px 22px;
  cursor: pointer;
  transition: transform 0.1s ease, box-shadow 0.2s ease, filter 0.2s ease;
}
.btn:hover { filter: brightness(1.08); box-shadow: 0 0 22px var(--glow-soft); }
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline: 3px solid var(--cas9); outline-offset: 2px; }
.btn-ghost {
  margin-top: 12px;
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--panel-line);
  font-size: 0.82rem;
  padding: 8px 16px;
  display: block;
  margin-left: auto;
  margin-right: auto;
}
.btn-ghost:hover { box-shadow: none; color: var(--ink); }

.scoreline {
  display: flex;
  justify-content: center;
  gap: 20px;
  margin: 6px 0 12px;
}
.scoreline-num {
  display: block;
  font-family: "JetBrains Mono", monospace;
  font-weight: 700;
  font-size: 1.45rem;
  color: var(--glow);
}
.scoreline-num.scoreline-warn { color: var(--danger); }
.scoreline-lbl {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--muted);
}
.end-note { min-height: 1.2em; }
.card-warn {
  border-left: 2px solid var(--danger);
  padding-left: 10px;
  text-align: left;
  color: var(--muted);
  font-size: 0.82rem;
  line-height: 1.4;
}
.card-warn b { color: var(--danger); }

/* ---------- daily share row ---------- */
/* Sized to the end-note it replaces, so the daily's end card is exactly as
   tall as everyone else's and stays inside the stage with no scrolling. */
.share {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0 0 12px;
  min-height: 1.2em;
}
.share code {
  font-family: "JetBrains Mono", monospace;
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  color: var(--ink);
}
.share .btn-ghost { margin: 0; display: inline-block; padding: 3px 10px; font-size: 0.74rem; }

/* ---------- responsive ---------- */
@media (max-width: 560px) {
  .hud { grid-template-columns: repeat(2, 1fr); }
  .brand h1 { font-size: 1.55rem; }
  /* The strand is also shorter here (script.js), so each column is wider.
     Extra vertical padding grows the tap target without moving the bases,
     and slimmer side padding hands every spare pixel to the columns. */
  .col { gap: 10px; padding: 12px 0; }
  .base { font-size: 0.68rem; }
  .stage { padding-left: 12px; padding-right: 12px; }
  .helix { padding-left: 14px; padding-right: 14px; }
  .backbone { left: 14px; right: 14px; }
}

/* Touch-only devices: there is no roaming pointer for the scissors to
   follow, so a cursor stand-in would just sit wherever the last tap was. */
@media (hover: none) and (pointer: coarse) {
  .scissors { display: none; }
  .strand { cursor: auto; }
}

@media (prefers-reduced-motion: reduce) {
  .col.candidate { animation: none; }
  .col.candidate .base { box-shadow: 0 0 14px 3px var(--glow-soft); }
  .pop { animation-duration: 0.01s; }
  /* keep the red colour cue for a bad cut, drop the movement */
  .scissors.jammed { animation: none; }
  .col.off-target { animation: none; background: rgba(255,107,107,0.35); }
}

/* ---------- mode selector ---------- */
.mode-selector {
  display: flex;
  flex-direction: column;
  gap: 6px;
  justify-content: center;
  margin-bottom: 12px;
}
.mode-btn {
  width: 100%;
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--panel-line);
  border-radius: 8px;
  padding: 7px 10px;
  font-family: inherit;
  font-weight: 700;
  font-size: 0.85rem;
  cursor: pointer;
  transition: all 0.2s ease;
}
.mode-btn:hover {
  color: var(--ink);
  border-color: var(--glow-soft);
}
.mode-btn.selected {
  background: var(--glow-soft);
  color: var(--ink);
  border-color: var(--glow);
}

/* ---------- leaderboard ---------- */
.leaderboard {
  margin: 10px 0 12px;
  padding: 0;
}
.leaderboard-title {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--muted);
  margin-bottom: 8px;
}
.leaderboard ol {
  list-style: none;
  counter-reset: rank;
  margin: 0;
  padding: 0;
  /* Five rows in two columns: a full top ten in half the height, so the card
     fits without the list needing a scrollbar of its own. */
  display: grid;
  grid-template-rows: repeat(5, auto);
  grid-auto-flow: column;
  column-gap: 14px;
  font-family: "JetBrains Mono", monospace;
  font-size: 0.78rem;
  color: var(--glow);
}
/* rank.  score  ........  accuracy · date */
.leaderboard li {
  counter-increment: rank;
  display: grid;
  grid-template-columns: 1.4em auto 1fr;
  align-items: baseline;
  gap: 6px;
  padding: 1px 4px;
  border-radius: 6px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.leaderboard li::before { content: counter(rank) "."; color: var(--muted); text-align: right; }
.lb-score { font-weight: 700; }
.lb-meta { color: var(--muted); font-size: 0.66rem; text-align: right; }
/* the round just played, so you can see where it landed */
.leaderboard li.you { background: rgba(255, 178, 77, 0.14); }
.leaderboard li.you::before,
.leaderboard li.you .lb-score { color: var(--cas9); }
/* Two columns of one-line board rows do not fit a phone-width card, so the
   top ten runs as a single column there; the overlay scrolls anyway. This
   sits after the base rule on purpose: same specificity, so order decides. */
@media (max-width: 560px) {
  .leaderboard ol { grid-template-columns: 1fr; grid-template-rows: none; grid-auto-flow: row; }
}

/* ---------- achievements ---------- */
.achievements-earned {
  margin: 10px 0;
  padding: 8px 10px;
  background: rgba(46, 230, 200, 0.1);
  border: 1px solid var(--glow-soft);
  border-radius: 10px;
}

.achievement-progress {
  text-align: center;
  font-family: "JetBrains Mono", monospace;
  font-weight: 700;
  font-size: 0.82rem;
  color: var(--glow);
  padding-bottom: 4px;
}

/* All six, as tiles: two across, three down, beside the top ten. Locked
   tiles keep their description on show, because "how do I get that one" is
   the point of listing them. */
.achievement-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 5px;
  margin-top: 6px;
}
.achievement {
  padding: 5px 4px;
  text-align: center;
  border-radius: 8px;
  border: 1px solid transparent;
  background: rgba(46, 230, 200, 0.07);
}
.achievement.locked {
  opacity: 0.6;
  background: transparent;
  border-color: var(--panel-line);
  border-style: dashed;
}
.achievement.locked .achievement-name { color: var(--muted); }
.achievement.new {
  background: rgba(255, 178, 77, 0.12);
  border-color: var(--cas9);
}

.achievement-new {
  display: inline-block;
  font-family: "JetBrains Mono", monospace;
  font-size: 0.55rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: #06101f;
  background: var(--cas9);
  border-radius: 999px;
  padding: 1px 6px;
  margin-left: 6px;
  vertical-align: middle;
}

.achievement-name {
  font-weight: 700;
  color: var(--glow);
  font-size: 0.74rem;
  margin-bottom: 1px;
}

.achievement-desc {
  font-size: 0.62rem;
  line-height: 1.3;
  color: var(--muted);
}

/* ---------- keyboard play ---------- */
/* The strand is the single Tab stop. Its own ring is suppressed and drawn on
   the cursor column instead - and only while the keyboard is the active input
   (script.js toggles .kb), so a mouse player never sees a ring they did not
   ask for, and a keyboard player loses it the moment they reach for the mouse. */
.strand:focus { outline: none; }
.strand.kb:focus .col.cursor {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}
.card-hint { font-size: 0.8rem; opacity: 0.85; }

/* ---------- utility classes ---------- */
/* visually hidden, still read by screen readers */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.hidden {
  display: none !important;
}

/* ---------- stop button ---------- */
/* Pinned to the stage's corner, above the strand but below nothing that
   matters: the overlay only shows once the round is over and this is hidden. */
#stop-btn {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 40;
  padding: 8px 14px;
  font-size: 0.85rem;
}
@media (max-width: 560px) {
  #stop-btn { top: 8px; right: 8px; padding: 6px 10px; font-size: 0.78rem; }
}
