/* kubewrecked — white-on-black terminal, Kubernetes-blue accent, three-format daily drill */
:root{
  --bg:#0b0b0c; --bg-deep:#050506; --panel:#141416;
  --fg:#f4f4f5; --dim:#a1a1a6; --mut:#8a8a90;
  --amber:#ffb000; --green:#27c93f; --red:#ff5f56;
  --blue:#326ce5; --blue-lite:#6ea8ff; --blue-glow:rgba(50,108,229,.30);
  --line:#2a2a2e; --glow:rgba(255,255,255,.16);
  --mono:ui-monospace,"SF Mono","JetBrains Mono","Cascadia Mono",Menlo,Consolas,monospace;
}
*{box-sizing:border-box;}
html{height:100%;}
/* min-height (not height): body must be free to grow TALLER than the viewport. With height:100%
   body is locked to viewport height, and because .crt is a flex item with overflow:hidden its
   auto min-height resolves to 0 — so a tall question (long .why) shrinks+clips the card instead
   of letting the page scroll, stranding the explanation and "next" button off-screen on mobile. */
body{min-height:100%;}
body{margin:0;
  background:radial-gradient(120% 120% at 50% 0%,#141416 0%,var(--bg) 55%,var(--bg-deep) 100%);
  color:var(--fg);font-family:var(--mono);font-size:15px;line-height:1.5;
  display:flex;flex-direction:column;align-items:center;padding:24px 14px 48px;
  -webkit-font-smoothing:antialiased;}

.crt{width:100%;max-width:560px;background:var(--bg);border:1px solid var(--line);
  border-radius:8px;overflow:hidden;position:relative;
  box-shadow:0 24px 60px -22px rgba(0,0,0,.85),inset 0 0 120px rgba(255,255,255,.02);}
.crt::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:repeating-linear-gradient(to bottom,rgba(0,0,0,0) 0 2px,rgba(0,0,0,.16) 3px,rgba(0,0,0,0) 4px);
  mix-blend-mode:multiply;opacity:.5;}

.titlebar{display:flex;align-items:center;gap:8px;padding:9px 14px;
  background:linear-gradient(#161618,#101012);border-bottom:1px solid var(--line);}
.dot{width:11px;height:11px;border-radius:50%;}
.dot.r{background:#ff5f56;}.dot.y{background:#ffbd2e;}.dot.g{background:#27c93f;}
.title{margin-left:6px;font-size:12px;letter-spacing:.5px;color:var(--dim);}

.screen{padding:18px 16px 16px;}
.head{display:flex;justify-content:space-between;align-items:baseline;}
.task-no{font-size:12px;letter-spacing:.5px;color:var(--dim);text-transform:uppercase;}
.timer{font-size:13px;color:var(--amber);text-shadow:0 0 6px rgba(255,176,0,.3);font-variant-numeric:tabular-nums;}

.progress{display:flex;gap:6px;margin:10px 0 16px;}
.pip{flex:1;height:4px;border-radius:2px;background:var(--line);}
.pip.done{background:var(--green);box-shadow:0 0 6px rgba(39,201,63,.4);}
.pip.wrong{background:var(--red);box-shadow:0 0 6px rgba(255,95,86,.4);}
.pip.active{background:var(--fg);}

/* min-height keeps the loading card a sensible size while the drill is fetched; it is NOT the CLS
   fix on its own — a single day's first-question content runs anywhere from ~360px (short MCQ) to
   ~800px (tall multi-chip build), so no fixed reserve can both cover the tall days and avoid a big
   gap on the short ones. The actual CLS fix is the visibility:hidden-until-.ready rule below, which
   stops the late-loaded content from shoving the visible footer/sponsor/legal down. #quiz is
   [hidden] on the result screen, so this adds no gap there. */
#quiz{min-height:360px;}

/* CLS guard. The drill is fetched ~1s after first paint and its height varies wildly day to day,
   so any element rendered BELOW it would be shoved downward when the content lands — that single
   big downward jump was the CLS culprit (PageSpeed mobile, ~0.19 on a median day, ~0.35 on a
   build-heavy one). These trailing blocks keep their box (visibility:hidden still reserves layout),
   so they stay invisible until game.js renders the first screen and adds .ready to <body>. An
   invisible element's movement is not counted toward CLS, so the one big shift becomes ~zero — with
   no reserved-space gap. They cross-fade in (opacity) once revealed; the snap-to-visible happens at
   their final position, so the fade itself shifts nothing. game.js requires JS to run at all (it
   fetches the drill), so gating these on JS-driven .ready costs nothing a no-JS visitor still had. */
.sponsor,footer.stats,.legal{transition:opacity .25s ease;}
body:not(.ready) .sponsor,
body:not(.ready) footer.stats,
body:not(.ready) .legal{visibility:hidden;opacity:0;}
@media (prefers-reduced-motion:reduce){.sponsor,footer.stats,.legal{transition:none;}}
/* min-height reserves the single label line so the empty->"// recall it (1 of 3)" fill doesn't
   nudge the (visible) prompt down — that nudge was a small CLS contributor. The label is always
   one line, so this adds no gap. */
.qlabel{font-size:12px;color:var(--mut);margin:0 0 6px;min-height:1.5em;}
.prompt{font-size:16px;margin:0 0 16px;text-shadow:0 0 6px var(--glow);
  border-left:2px solid var(--blue);padding-left:12px;white-space:pre-wrap;}
.prompt code{color:var(--fg);background:var(--panel);padding:2px 6px;border-radius:3px;}

/* MCQ + reverse options */
.opts{display:flex;flex-direction:column;gap:8px;}
.opt{display:flex;align-items:center;gap:10px;text-align:left;width:100%;
  background:var(--panel);border:1px solid var(--line);border-radius:6px;
  padding:12px 14px;color:var(--fg);font-family:var(--mono);font-size:15px;cursor:pointer;
  transition:border-color .1s,background .1s;}
.opt:hover{border-color:var(--dim);}
.opt .key{color:var(--mut);min-width:18px;}
.opt:disabled{cursor:default;}
.opt.correct{border-color:var(--green);background:rgba(39,201,63,.10);}
.opt.correct .key{color:var(--green);}
.opt.wrong{border-color:var(--red);background:rgba(255,95,86,.10);}
.opt.wrong .key{color:var(--red);}
.opt.faded{opacity:.45;}

/* chunk assembly */
.build{min-height:44px;display:flex;align-items:center;gap:6px;flex-wrap:wrap;
  background:var(--panel);border:1px dashed var(--mut);border-radius:6px;padding:10px 12px;margin-bottom:12px;}
.build .pr{color:var(--blue-lite);} .build .ph{color:var(--mut);font-size:13px;}
.built{display:inline-flex;gap:6px;flex-wrap:wrap;}
.chiprow{display:flex;gap:8px;flex-wrap:wrap;}
.chip{background:var(--panel);border:1px solid var(--line);border-radius:5px;
  padding:8px 12px;color:var(--fg);font-family:var(--mono);font-size:15px;cursor:pointer;}
.chip:hover{border-color:var(--dim);}
.chip.used{opacity:.35;pointer-events:none;}
.chip.inbuild{background:#1c1c1f;border-color:var(--dim);}
.submitwrap{margin-top:12px;display:flex;gap:10px;align-items:center;}
.submitwrap .preview{flex:1;font-size:13px;color:var(--mut);min-width:0;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;}

/* #why stays empty during play — explanations live on the result screen (#recap), revealed by
   tapping the red/green squares. Kept as a collapsed, harmless element so game.js's el("why")
   references stay valid. */
.why{margin:14px 0 0;font-size:13px;color:var(--dim);border-left:2px solid var(--mut);
  padding-left:12px;min-height:1px;}
.why:empty{margin:0;border:0;padding:0;min-height:0;}

.next{margin-top:18px;display:flex;}
/* game.js toggles this block via the `hidden` attribute (shown only once a question is answered).
   The UA rule [hidden]{display:none} and the .next class rule above have EQUAL specificity, so the
   author rule wins by origin — which left the "next →" button visible during the question and made
   it shift down when the async drill loaded (a CLS source). This higher-specificity rule restores
   the intended hide. */
.next[hidden]{display:none;}
button.go{font-family:var(--mono);font-size:14px;cursor:pointer;background:transparent;color:var(--fg);
  border:1px solid var(--mut);border-radius:4px;padding:8px 16px;text-shadow:0 0 6px var(--glow);
  transition:background .12s;}
button.go:hover{background:rgba(255,255,255,.08);}
button.go:disabled{opacity:.4;cursor:default;}
.next button.go{flex:1;padding:13px;font-size:15px;border-color:var(--fg);background:rgba(255,255,255,.06);}
.next button.go:hover{background:rgba(255,255,255,.12);}

/* result */
.result{text-align:center;padding:8px 0 4px;}
.result h2{font-size:18px;margin:0 0 4px;text-shadow:0 0 6px var(--glow);}
.score{font-size:32px;margin:8px 0 2px;font-variant-numeric:tabular-nums;}
.time{color:var(--amber);font-size:15px;margin-bottom:14px;text-shadow:0 0 6px rgba(255,176,0,.3);
  font-variant-numeric:tabular-nums;}
.squares{display:flex;gap:8px;justify-content:center;margin:8px 0 6px;flex-wrap:wrap;}
.sq{display:inline-flex;align-items:center;justify-content:center;
  min-width:40px;min-height:40px;padding:4px;font-size:20px;font-weight:700;line-height:1;
  background:var(--panel);border:1px solid var(--line);border-radius:8px;color:var(--dim);
  cursor:pointer;transition:transform .1s,box-shadow .1s;}
.sq.ok{background:rgba(39,201,63,.16);border-color:var(--green);color:var(--green);}
.sq.miss{background:rgba(255,95,86,.16);border-color:var(--red);color:var(--red);}
.sq:hover{transform:translateY(-2px);}
.sq.sel{box-shadow:0 0 0 2px var(--fg);}
.recaphint{font-size:12px;color:var(--mut);margin:0 0 8px;}
.recap{text-align:left;max-width:380px;margin:0 auto 14px;background:var(--panel);
  border:1px solid var(--line);border-radius:6px;padding:12px 14px;font-size:13px;line-height:1.5;}
.recap .recap-h{font-weight:600;}
.recap code{background:#0d0d0f;padding:2px 6px;border-radius:3px;}
.actions{display:flex;gap:14px;justify-content:center;align-items:center;flex-wrap:wrap;}
.sharebox{margin:14px auto 0;max-width:330px;background:var(--panel);border:1px solid var(--line);
  border-radius:6px;padding:12px 14px;white-space:pre;font-size:14px;line-height:1.5;text-align:left;}
.countdown{font-size:13px;color:var(--mut);}

/* sponsor */
.sponsor{margin-top:22px;padding-top:14px;border-top:1px dashed var(--line);
  display:flex;align-items:center;gap:10px;}
.sponsor-tag{font-size:11px;color:var(--mut);letter-spacing:.5px;}
.sponsor-slot{display:inline-flex;align-items:center;gap:8px;min-height:34px;padding:6px 14px;
  border:1px dashed var(--mut);border-radius:4px;color:var(--dim);text-decoration:none;font-size:13px;}
.sponsor-slot img{max-height:22px;width:auto;display:block;}
.sponsor-slot:hover{border-color:var(--fg);color:var(--fg);}
/* Placeholder swap: "your logo here" -> the (deliberately spaced) contact email on hover /
   focus / press. The two labels are grid-stacked in ONE cell, so the box is sized to the wider
   email and there is no layout shift; opacity cross-fades between them. Pure CSS — the strict CSP
   forbids inline JS — and works in every engine. :focus covers a real click (a mailto keeps the
   page, so the link stays focused); :active covers a touch press. When a real sponsor loads,
   game.js clears this markup entirely, so it only affects the empty-slot placeholder. */
.sl-swap{display:inline-grid;}
.sl-swap>span{grid-area:1/1;text-align:center;transition:opacity .12s ease;}
.sl-email{opacity:0;}
.sponsor-slot:hover  .sl-default,
.sponsor-slot:focus  .sl-default,
.sponsor-slot:active .sl-default{opacity:0;}
.sponsor-slot:hover  .sl-email,
.sponsor-slot:focus  .sl-email,
.sponsor-slot:active .sl-email{opacity:1;}
@media (prefers-reduced-motion:reduce){.sl-swap>span{transition:none;}}

/* stat bar */
/* transparent (not var(--panel)): inherit the card's own var(--bg) so the bar is the SAME colour
   as the card body — a flat panel still read as a distinct lighter band ("two colours"). The
   border-top is the only divider. */
.stats{display:flex;gap:18px;flex-wrap:wrap;padding:11px 16px;border-top:1px solid var(--line);
  background:transparent;font-size:13px;}
/* one uniform text colour for the whole bar: the muted label (--mut) + bright glowing value
   (--fg) read as two colours. Both use --dim so the footer is a single flat colour. */
.stat .k,.stat .v{color:var(--dim);}

a{color:var(--blue-lite);}
kbd{font-family:var(--mono);font-size:.85em;background:var(--panel);border:1px solid var(--line);
  border-radius:3px;padding:1px 5px;}

@media (max-width:480px){
  body{font-size:14px;padding:12px 8px 32px;}
  .screen{padding:14px 12px;}
  .prompt{font-size:15px;}
  .stats{gap:12px;}
}
@media (prefers-reduced-motion:reduce){ .crt::after{display:none;} .sq:hover{transform:none;} }
:focus-visible{outline:2px solid var(--blue-lite);outline-offset:2px;}
.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;
  clip:rect(0 0 0 0);clip-path:inset(50%);overflow:hidden;white-space:nowrap;}
.legal{max-width:560px;margin:14px auto 0;padding:0 14px;font-family:var(--mono);
  font-size:9px;line-height:1.45;color:var(--dim);text-align:center;}

/* clean-run confetti (3/3). Rules live in this same-origin sheet => style-src 'self' covers them.
   pointer-events:none on BOTH layer and pieces so the results UI underneath stays clickable. */
.confetti-layer{position:fixed;inset:0;overflow:hidden;pointer-events:none;z-index:9999;}
.confetti{position:absolute;top:-14px;width:8px;height:14px;pointer-events:none;opacity:0;
  will-change:transform,opacity;border-radius:1px;
  animation:confetti-fall var(--dur,3s) linear var(--delay,0s) forwards;}
@keyframes confetti-fall{
  0%{transform:translate3d(0,-14px,0) rotate(0);opacity:1;}
  100%{transform:translate3d(var(--drift,0),102vh,0) rotate(720deg);opacity:0;}
}
@media (prefers-reduced-motion: reduce){ .confetti-layer{display:none;} }
