/* ══════════ TEST YOUR MIGHT — the game's own page ══════════
   A fixed 960x540 stage on a dark page, like an arcade cabinet.
   Everything is px, deliberately: the old overlay sized its HUD in vw
   because it WAS the viewport; a fixed box on a wide monitor turns 4vw
   into a 100px glyph in a 540px frame. The box is a constant, so its
   type is too. */

:root{
  --ink:#EDF2F4; --ink-dim:#8D99AE; --hair:#2B2D42;
  --crimson:#FF4D6D; --base:#0B0D10;
  --serif:"Fraunces",serif; --mono:"JetBrains Mono",monospace;
  --fit:1;
}
*{margin:0;padding:0;box-sizing:border-box}
/* THE hidden ATTRIBUTE MUST ALWAYS WIN.

   `hidden` works through the UA stylesheet's `display:none` — which any
   author `display:flex` on the same element silently overrides. That is
   exactly what happened here: `.ring__panel{display:flex}` kept the
   EMPTY leaderboard panel rendered over the whole stage from the first
   frame — its dark tint and backdrop-filter blur made the game look
   frozen and out of focus, and it swallowed every click so the guard
   could never be raised. JS said `panel.hidden === true` the whole time,
   and canvas-only captures never showed the DOM overlay: only the
   user's real screenshot caught it. */
[hidden]{display:none!important}
html,body{height:100%}
body{background:#07090C;color:var(--ink);font-family:var(--mono);
  display:flex;align-items:center;justify-content:center;overflow:hidden}

.back{position:fixed;top:16px;left:18px;font-size:10px;letter-spacing:.18em;
  text-transform:uppercase;color:var(--ink-dim);text-decoration:none;z-index:10}
.back:hover{color:var(--ink)}
.pill{font-family:var(--mono);font-size:9px;letter-spacing:.18em;text-transform:uppercase;
  color:var(--ink-dim);background:rgba(10,5,7,.6);border:1px solid var(--hair);
  border-radius:100px;padding:8px 14px;cursor:pointer;transition:all .3s ease}
.pill:hover{color:var(--ink);border-color:var(--crimson)}
.pill[aria-pressed="true"]{color:var(--crimson);border-color:var(--crimson)}
.snd{position:fixed;top:12px;right:18px;z-index:10}

/* ── the stage ──
   Two nested transforms, two elements, on purpose: the outer .ring
   carries the fit-to-window scale, the inner .ring__shell carries
   sway/shake/kick. One transform per element — sharing one would mean
   the effects and the fit clobbering each other's matrix. */
.stagewrap{display:flex;flex-direction:column;align-items:center;gap:14px}
.ring{position:relative;width:960px;height:540px;background:#000;
  transform:scale(var(--fit));transform-origin:center center;
  border:1px solid #1a1215;border-radius:4px;overflow:hidden}
.ring__shell{position:absolute;inset:0;
  transform:translate(var(--shx,0px),var(--shy,0px)) scale(var(--kick,1))}
.ring__gl{display:block;width:960px;height:540px}
.hint{font-size:9px;letter-spacing:.2em;text-transform:uppercase;color:var(--ink-dim)}

/* ── film grain + vignette ──
   Above the canvas, BELOW the HUD: grain over 9px mono type costs
   legibility. The grain is a tiled noise image stepped at ~12Hz — at
   60Hz it reads as electronic shimmer, at film rates it reads as film.
   It also dithers the JPEG banding a dark backdrop is prone to. */
.ring__grain{position:absolute;inset:-64px;pointer-events:none;z-index:2;opacity:.05;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='128' height='128'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='128' height='128' filter='url(%23n)' opacity='0.9'/%3E%3C/svg%3E");
  animation:grain .8s steps(8) infinite}
@keyframes grain{
  0%{transform:translate(0,0)}25%{transform:translate(-32px,18px)}
  50%{transform:translate(24px,-28px)}75%{transform:translate(-18px,-12px)}
  100%{transform:translate(0,0)}}
/* THE VIGNETTE CLOSES IN. Both the inner stop and the outer darkness are
   variables so a draining clock can strangle the frame — 58%/.55 at rest
   is exactly v1's look, and the game walks them to 20%/.94 as the last
   seconds go. Cheaper than any post-process and it darkens the HUD's
   corners too, which a shader on the canvas could not reach. */
.ring__vig{position:absolute;inset:0;pointer-events:none;z-index:2;
  background:radial-gradient(115% 100% at 50% 48%,
    transparent var(--vigstop,58%),rgba(4,2,3,var(--vigdark,.55)) 100%)}

/* ── the gate ── */
.ring__gate{position:absolute;inset:0;z-index:5;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:10px;background:#07090C;
  transition:opacity .7s ease}
.ring__gate.gone{opacity:0;pointer-events:none}
.ring__pct{font-family:var(--serif);font-size:34px;line-height:1}
.ring__pct i{font-family:var(--mono);font-size:10px;color:var(--ink-dim);font-style:normal}
.ring__bar{width:220px;height:2px;background:var(--hair);overflow:hidden}
.ring__bar span{display:block;height:100%;width:0;background:var(--crimson);
  transition:width .25s linear}
.ring__cap{font-size:8px;letter-spacing:.2em;text-transform:uppercase;color:var(--ink-dim)}

/* ── HUD ── */
.ring__hud{position:absolute;inset:0;z-index:3;pointer-events:none}
.ring__strikes{position:absolute;top:14px;left:16px;display:flex;gap:6px}
.ring__strikes i{width:10px;height:10px;border-radius:50%;border:1px solid var(--hair);
  transition:all .3s ease}
.ring__strikes i.on{background:var(--crimson);border-color:var(--crimson);
  box-shadow:0 0 14px rgba(255,77,109,.6)}
/* same reasoning as the sequence chip — a 9px tracked label over brick is
   unreadable without its own substrate */
.ring__level{position:absolute;top:12px;left:50%;transform:translateX(-50%);
  font-size:9px;letter-spacing:.2em;text-transform:uppercase;color:var(--ink);
  padding:6px 14px;border-radius:999px;
  background:rgba(6,4,6,.6);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  border:1px solid rgba(237,242,244,.10);white-space:nowrap}
.ring__pause{pointer-events:auto;position:absolute;top:10px;right:12px}
/* ── the sequence ──
   THE PROBLEM: thin light glyphs sitting directly on a photograph of a
   brick gym. The backdrop has detail and contrast at exactly the scale
   of the type, so the code disappears into the wall behind it — and the
   code is the one thing the player MUST read.

   The fix is a backing plate, not a brighter colour: raising contrast
   against an unpredictable background is a losing game, because the
   background changes as the bag swings. A dark, blurred, shrink-wrapped
   chip gives every glyph the SAME substrate no matter what is behind it,
   which is why every game HUD that has to survive arbitrary scenery ends
   up here. :empty keeps it from showing as a bare lozenge between
   sequences. */
/* 92px, not the old 64: the chip is OPAQUE now, and the clock arc's box
   reaches 84px up from the bottom. At 64 the plate sat on top of the
   arc's apex and hid the very part of it that shows time running out. */
.ring__code{position:absolute;left:50%;transform:translateX(-50%);bottom:92px;
  display:flex;align-items:center;gap:14px;
  padding:9px 20px;border-radius:999px;
  background:rgba(6,4,6,.66);
  -webkit-backdrop-filter:blur(12px) saturate(.75);
  backdrop-filter:blur(12px) saturate(.75);
  border:1px solid rgba(237,242,244,.12);
  box-shadow:0 10px 34px rgba(0,0,0,.6), inset 0 1px 0 rgba(255,255,255,.05)}
.ring__code:empty{display:none}
.ring__code b{font-size:30px;line-height:1;color:var(--ink-dim);opacity:.4;
  transition:all .22s ease;text-shadow:0 2px 6px rgba(0,0,0,.8)}
/* the punch you are ON: full white, larger, and lit from behind so it
   separates from its own neighbours as well as from the room */
.ring__code b.now{opacity:1;color:#fff;transform:scale(1.18);
  text-shadow:0 0 16px rgba(255,255,255,.55),0 2px 6px rgba(0,0,0,.9)}
.ring__code b.done{opacity:1;color:var(--crimson);
  text-shadow:0 0 18px rgba(255,77,109,.6),0 2px 6px rgba(0,0,0,.8)}
/* ── the clock, as a draining arc ──
   A straight rule reads as a progress bar; a curve emptying toward its
   end reads as time. Same footprint as v1's bar so nothing else moved. */
.ring__arc{position:absolute;left:50%;transform:translateX(-50%);bottom:38px;
  width:320px;height:46px;overflow:visible}
.ring__arctrack{fill:none;stroke:var(--hair);stroke-width:2;stroke-linecap:round}
.ring__arcfill{fill:none;stroke:var(--crimson);stroke-width:2.5;stroke-linecap:round;
  transition:stroke .3s ease;
  filter:drop-shadow(0 0 5px rgba(255,77,109,.45))}
/* the last three seconds: hotter, thicker, and pulsing on the beat of
   the corner bell */
.ring__arc.low .ring__arcfill{stroke:#FF2740;stroke-width:3.4;
  filter:drop-shadow(0 0 12px rgba(255,39,64,.9));
  animation:arcpulse .5s ease-in-out infinite}
@keyframes arcpulse{0%,100%{opacity:1}50%{opacity:.45}}
/* the caption carries the countdown and the round announcements, so it
   gets a hard shadow rather than a plate — it has to stay legible while
   sitting over the bag itself, and a chip there would box in the frame */
.ring__say{position:absolute;left:0;right:0;bottom:22px;text-align:center;
  font-size:10px;letter-spacing:.22em;text-transform:uppercase;color:#fff;
  text-shadow:0 0 10px rgba(0,0,0,.95),0 2px 4px rgba(0,0,0,1),0 0 26px rgba(0,0,0,.8)}

/* ── the finisher's grade and matte ──
   THE GRADE is two stacked layers in one element: a screen-blended hot
   spot that blooms out of the impact (--gx,--gy follow the fist), and an
   overlay-blended warm wash that lifts contrast across the frame. Screen
   alone just fogs the picture; overlay alone just tints it. Together
   they read as a lamp flaring into the lens, which is what a heavy
   impact does to a real camera.

   --gtint carries the colour so a flawless round can go gold without a
   second element, and --gamt is the master amount. */
.ring__grade{position:absolute;inset:0;z-index:2;pointer-events:none;opacity:var(--gamt,0)}
.ring__grade::before,.ring__grade::after{content:"";position:absolute;inset:0}
.ring__grade::before{mix-blend-mode:screen;
  background:radial-gradient(42% 62% at var(--gx,50%) var(--gy,50%),
    var(--gtint,rgba(255,186,92,.92)) 0%,rgba(255,140,40,.34) 34%,transparent 72%)}
.ring__grade::after{mix-blend-mode:overlay;
  background:linear-gradient(180deg,rgba(255,176,86,.42),rgba(120,40,10,.5))}

/* THE MATTE. Two bars driven by one variable, and `will-change` on the
   transform so a software rasteriser composites them instead of
   repainting the frame under them. */
.ring__bars{position:absolute;inset:0;z-index:3;pointer-events:none}
.ring__bars i,.ring__bars b{position:absolute;left:0;right:0;height:12%;background:#000;
  transform:scaleY(var(--bar,0));will-change:transform}
.ring__bars i{top:0;transform-origin:top}
.ring__bars b{bottom:0;transform-origin:bottom}

/* ── error flash ── */
.ring__flash{position:absolute;inset:0;z-index:4;pointer-events:none;opacity:0;
  background:radial-gradient(120% 100% at 50% 50%,transparent 45%,rgba(255,77,109,.6) 100%)}
.ring__flash.on{animation:ringflash .5s ease}
@keyframes ringflash{0%{opacity:0}18%{opacity:1}100%{opacity:0}}

/* ── the board panel ── */
.ring__panel{position:absolute;inset:0;z-index:6;display:flex;overflow-y:auto;
  background:rgba(6,4,5,.88);backdrop-filter:blur(12px);padding:24px}
.ring__panelwrap{width:520px;max-width:100%;margin:auto}
.ring__medal{width:54px;height:54px;margin-bottom:10px}
.ring__board h3{font-family:var(--serif);font-weight:400;font-size:26px;margin-bottom:12px}
.ring__rows{list-style:none;display:flex;flex-direction:column;gap:4px;margin-bottom:10px}
.ring__rows li{display:grid;grid-template-columns:20px 44px 1fr auto auto;gap:10px;
  align-items:baseline;padding:8px 10px;border:1px solid var(--hair);border-radius:8px;
  font-size:9px;letter-spacing:.1em;color:var(--ink-dim)}
.ring__rows li.won{border-color:rgba(255,77,109,.5)}
.ring__who{color:var(--ink);letter-spacing:.16em}
.ring__got{color:var(--crimson);text-transform:uppercase}
.ring__when{opacity:.6}
.ring__boardnote{font-size:8px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-dim)}
.ring__panelbtns{display:flex;gap:10px;margin-top:16px}

/* ── touch controls ──
   Sized in absolute px, not vw: a thumb is the same size on every phone,
   and 62px is about the smallest target that can be hit reliably while
   the other hand is holding the device. `touch-action:manipulation` and
   the preventDefault in the handler between them stop the double-tap
   zoom that would otherwise fire on a fast combination. */
/* A D-PAD, not a spread-out row. Four buttons in a plus, the way a
   controller has always laid them out — the thumb learns one shape and
   stops hunting. 3x3 grid with the corners empty; the cross IS the
   negative space. */
/* One variable sizes the whole thing: --padb is a button's edge, and the
   radius and glyph scale off it, so retuning is a single number. */
.pad{display:grid;grid-template-columns:repeat(3,var(--padb,44px));
  grid-template-rows:repeat(3,var(--padb,44px));gap:var(--padgap,4px);
  touch-action:manipulation;margin-top:4px}
.pad__b--up{grid-area:1/2}
.pad__b--left{grid-area:2/1}
.pad__b--right{grid-area:2/3}
.pad__b--down{grid-area:3/2}
.pad__b{width:100%;height:100%;border-radius:12px;
  font-family:var(--mono);font-size:15px;line-height:1;
  color:var(--ink);background:rgba(18,10,13,.82);border:1px solid var(--hair);
  display:flex;align-items:center;justify-content:center;
  touch-action:manipulation;-webkit-tap-highlight-color:transparent;
  user-select:none;cursor:pointer;transition:transform .08s ease,background .12s ease}
.pad__b:active{transform:scale(.9);background:rgba(255,77,109,.34);
  border-color:var(--crimson)}
/* the two SPECIAL punches read hotter than the hooks, so a glance at the
   pad tells you which two are the ones that cost you */
.pad__b--up,.pad__b--down{border-color:rgba(255,77,109,.5)}

/* ── phones ──
   A touch device gets a different LAYOUT, not a smaller one. Stacking the
   pad under the stage costs ~90px of a 390px-tall landscape screen, which
   is a quarter of the picture; the pad floats over the frame's dead
   corners instead and the stage takes the whole viewport.

   Keyed on pointer:coarse rather than width — a small window on a laptop
   still wants the desktop layout, and a big tablet still wants the pad. */
@media (pointer:coarse){
  body{overflow:hidden}
  .stagewrap{gap:0}
  .hint{display:none}
  .back,.snd{font-size:8px;padding:6px 10px;opacity:.5}

  /* bottom-LEFT, where a controller's d-pad lives and where the left
     thumb already is when the phone is held in landscape */
  /* 44px is Apple's and Google's minimum recommended touch target, and
     it is the floor here for a reason: a mis-tap in round 10 costs a
     strike, not just a moment. Sized down from 66 at the user's request
     and held at the floor rather than going under it. */
  .pad{position:fixed;left:calc(12px + env(safe-area-inset-left));
    bottom:calc(10px + env(safe-area-inset-bottom));right:auto;top:auto;
    margin:0;z-index:20;--padb:44px;--padgap:4px}
  .pad__b{font-size:16px;border-radius:12px;
    background:rgba(12,7,9,.5);backdrop-filter:blur(8px);
    border-color:rgba(237,242,244,.3)}

  /* rotate prompt — the game is a 16:9 frame and a portrait phone can
     only ever show it as a stripe */
  .rotate{position:fixed;inset:0;z-index:60;display:none;
    flex-direction:column;align-items:center;justify-content:center;gap:14px;
    background:#07090C;text-align:center;padding:24px}
  .rotate p{font-size:11px;letter-spacing:.2em;text-transform:uppercase;color:var(--ink-dim)}
  .rotate svg{width:54px;height:54px;stroke:var(--crimson);fill:none;stroke-width:1.5}
}
@media (pointer:coarse) and (orientation:portrait){
  .rotate{display:flex}
}

@media (prefers-reduced-motion: reduce){
  .ring__grain{animation:none}
  .ring__shell{transform:none !important}
}
