/* Bottle Battle — a pitch at night under stadium floodlights, with a flat, quiet HUD in the
   colours of the ground: warm night-black cards, chalk-white type like the pitch lines,
   scoreboard amber for what you act on and the score, pitch green for a land, gold for
   a double, red-card red for a wipeout. Grotesk type, scoreboard numerals.
   The HUD is made of what's in the bottle: every liquid in it (the streak tank, the push
   buttons, the cards) is the drink picked in settings (--liq), and things move the
   way the bottle does: a land flips and sticks, a double flips higher, a no-flip hops, a
   wipeout topples. Motion is transform and opacity only, so it never costs a layout.
   Layout: full-bleed 3D stage; HUD cards pinned to the four corners. */
@property --liq { syntax: '<color>'; inherits: true; initial-value: #6fbff0; }
@property --liq-deep { syntax: '<color>'; inherits: true; initial-value: #1d5f8f; }
:root {
  --bg: #0c0d0a;            /* the pitch at night */
  --ink: #f2efe4;           /* chalk, like the lines */
  --muted: #9a978b;
  --faint: #5d5b52;
  --accent: #ffb627;        /* scoreboard amber */
  --accent-deep: #c98700;
  --accent-ink: #1d1400;    /* text on amber */
  --gold: #ffd75e;          /* doubles, cap stands, the multiplier */
  --good: #8fd65e;          /* pitch green: landed */
  --bad: #ff4b3e;           /* red card: wasted */
  --air: #9fd0ff;           /* floodlight blue: in the air */

  /* the drink (Hud.setLiquid sets data-liquid); it pours from one to the next */
  --liq: #6fbff0;
  --liq-deep: #1d5f8f;
  --liq-ink: #04192a;
  transition: --liq .7s ease, --liq-deep .7s ease;

  --glass: rgba(17, 18, 14, 0.88);
  --glass-strong: #14150f;
  --glass-line: rgba(242, 239, 228, 0.08);
  --tile: rgba(242, 239, 228, 0.05);
  --accent-tint: rgba(255, 182, 39, 0.12);
  --liq-tint: color-mix(in srgb, var(--liq) 14%, transparent);

  --font-display: "Space Grotesk", "Inter Tight", "Segoe UI", system-ui, sans-serif;
  --font-score: "Chakra Petch", "Space Grotesk", "Segoe UI", system-ui, sans-serif;   /* the bottle label's */
  --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --font-ui: "Inter Tight", "Segoe UI", system-ui, sans-serif;

  /* one period of a water surface, and the bottle's outline, as masks */
  --wave: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 8' preserveAspectRatio='none'%3E%3Cpath d='M0 4C8 0 16 0 24 4s16 4 24 0v4H0z'/%3E%3C/svg%3E");
  --bottle: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12.2 34'%3E%3Cpath d='M2.55 33.0L9.6 33.0L10.2 32.8 L10.6 32.3 L11.0 31.6 L11.1 30.7 L11.1 14.0 L11.0 12.6 L10.7 11.1 L10.1 9.4 L8.8 7.2 L8.4 6.2 L8.2 5.4 L8.2 5.1 L8.7 4.9 L8.7 4.7 L8.2 4.6 L8.2 4.3L3.95 4.3 L3.95 4.6 L3.45 4.7 L3.45 4.9 L3.95 5.1 L3.95 5.4 L3.75 6.2 L3.35 7.2 L2.05 9.4 L1.45 11.1 L1.15 12.6 L1.05 14.0 L1.05 30.7 L1.15 31.6 L1.55 32.3 L1.95 32.8Z'/%3E%3Cpath d='M3.61 4.4h4.93V1.0h-4.93Z'/%3E%3C/svg%3E");
  /* the bottle itself, rendered in Blender (tools/blender_bottle_icons.py): a third full,
     empty and full, in the drink picked in settings */
  --bottle-img: url(../assets/bottle/water.png);
  --bottle-empty: url(../assets/bottle/water-empty.png);
  --bottle-full: url(../assets/bottle/water-full.png);

  /* One spacing scale in 4 px steps and one type scale. A card's own padding is never
     more than the space around it; nothing is set under 11 px; radii nest (a tile inside a
     card is rounded by the card's radius less its padding). */
  --s-1: 4px; --s-2: 8px; --s-3: 12px; --s-4: 16px; --s-5: 24px;
  --t-1: 11px;   /* labels, units, tags */
  --t-2: 13px;   /* body, rows, buttons */
  --t-3: 15px;   /* small values, lead text */
  --t-4: 18px;   /* values, the wordmark */
  --t-5: 28px;   /* the score on a phone */
  --t-6: 36px;   /* the score */
  --r-panel: 16px;   /* cards */
  --r-item: 8px;     /* tiles, chips, rows and fields inside a card */
  --r-tag: 6px;      /* tags and keys */
  --tag-pad: 2px 6px;
  --gutter: max(16px, env(safe-area-inset-left, 0px));
  --gutter-r: max(16px, env(safe-area-inset-right, 0px));
  color-scheme: dark;
}
:root[data-liquid="lime"] { --liq: #b6e35a; --liq-deep: #4a8419; --liq-ink: #142405;
  --bottle-img: url(../assets/bottle/lime.png); --bottle-empty: url(../assets/bottle/lime-empty.png); --bottle-full: url(../assets/bottle/lime-full.png); }
:root[data-liquid="berry"] { --liq: #ec5a95; --liq-deep: #8f1747; --liq-ink: #2a0716;
  --bottle-img: url(../assets/bottle/berry.png); --bottle-empty: url(../assets/bottle/berry-empty.png); --bottle-full: url(../assets/bottle/berry-full.png); }
:root[data-liquid="cola"] { --liq: #cf9158; --liq-deep: #4a1c0a; --liq-ink: #1f0a03;
  --bottle-img: url(../assets/bottle/cola.png); --bottle-empty: url(../assets/bottle/cola-empty.png); --bottle-full: url(../assets/bottle/cola-full.png); }
:root[data-liquid="citrus"] { --liq: #ff9a38; --liq-deep: #b25000; --liq-ink: #2a1300;
  --bottle-img: url(../assets/bottle/citrus.png); --bottle-empty: url(../assets/bottle/citrus-empty.png); --bottle-full: url(../assets/bottle/citrus-full.png); }

* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; background: var(--bg); color: var(--ink); overflow: hidden; }
body { font-family: var(--font-ui); font-size: var(--t-2); -webkit-font-smoothing: antialiased; touch-action: none; }
[hidden] { display: none !important; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

#app { position: fixed; inset: 0; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
#stage { position: absolute; inset: 0; cursor: grab; }
#stage.pulling { cursor: grabbing; }
#stage canvas { display: block; width: 100%; height: 100%; }

/* ---------- liquid ---------- */
/* A body of the drink: an element moved up and down by its level, with .swell riding on
   top; --lc is the colour at its surface. The swells are drawn by a gradient, not a mask
   (a mask is an extra render pass on every frame it moves), and scroll by one period so
   they loop. */
.swell { --wp: 48px; position: absolute; left: 0; top: -6px; width: calc(100% + var(--wp)); height: 7px; pointer-events: none;
  background: radial-gradient(60% 100% at 50% 100%, var(--lc, var(--liq)) calc(100% - 1.2px), transparent) 0 0 / var(--wp) 100% repeat-x;
  animation: waveX 2.6s linear infinite; }
@keyframes waveX { to { transform: translateX(calc(-1 * var(--wp))); } }
/* While a throw is on, the HUD's idle motion stops so the frame is the bottle's; only the
   bottle moves. Stopped, not paused: a paused animation still keeps
   its compositor layer, and the layers of whatever it overlaps */
.in-play .swell, .in-play .live-tag[data-tone] .drop::after, .in-play .kv .mult.hot::before, .in-play .kv .mult.hot::after { animation: none; }
.boot.done .swell { animation: none; }
.droplet { position: absolute; border-radius: 50%; pointer-events: none; z-index: 3; }
.ripple { position: absolute; border-radius: 50%; border: 2px solid currentColor; pointer-events: none; }
.fx { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
/* a small bottle drawn in currentColor, the drink inside */
.bot { display: block; width: 100%; height: 100%; overflow: visible; }
.bot-shell { fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linejoin: round; }
.bot-liq { fill: var(--liq); }
.bot-cap { fill: currentColor; }
.bot-label { fill: currentColor; opacity: 0.35; }

/* ---------- cards ---------- */
/* flat and nearly opaque: no blur (also spares phone GPUs), no gloss, no glow */
.glass, .glass-strong {
  position: relative;
  background: var(--glass);
  border: 1px solid var(--glass-line);
  border-radius: var(--r-panel);
  box-shadow: 0 12px 32px -16px rgba(0,0,0,0.7);
}
.glass-strong { background: var(--glass-strong); }

.panel-title {
  display: flex; align-items: center; gap: var(--s-2);
  font-size: var(--t-2); font-weight: 600; color: var(--ink);
}
.label { font-size: var(--t-1); color: var(--muted); }

/* numbers that flip like the bottle (ui/Motion.js FlipNumber; 3D only while flipping) */
.flipnum .digits { display: inline-block; white-space: nowrap; }
.flipnum .d { display: inline-block; transform-origin: 50% 58%; }

/* ---------- top bar ---------- */
.hud { position: absolute; left: 0; right: 0; display: flex; justify-content: space-between; align-items: flex-start; gap: var(--s-3); pointer-events: none; padding-left: var(--gutter); padding-right: var(--gutter-r); }
.hud > * { pointer-events: auto; }
/* the top row: your card on the left, everyone's on the right, each its own height;
   stacked (everyone's first) once they don't fit side by side */
.hud-top { top: 0; align-items: flex-start; padding-top: max(16px, env(safe-area-inset-top, 0px)); }
/* the hint centred on screen, the controls at the right; when the two would meet, the
   empty left column gives way first and the hint moves left */
.hud-bottom { bottom: 0; display: grid; grid-template-columns: 1fr auto 1fr; align-items: end; padding-bottom: max(16px, env(safe-area-inset-bottom, 0px)); }

/* one card: the brand, then your score after a rule */
.player { display: flex; align-items: stretch; }
.brand { display: flex; align-items: center; gap: var(--s-3); padding: var(--s-3) var(--s-4) var(--s-3) var(--s-3); }
/* the mark is the bottle itself, its water rocking; it flips when yours lands */
.brand-mark { width: 32px; height: 32px; border-radius: var(--r-item); display: grid; place-items: center; background: var(--liq-tint); flex: none; }
/* the mark is the game's bottle in the drink you picked; it flips when yours lands */
.brand-mark .bm { width: 12px; height: 30px; background: var(--bottle-img) center / contain no-repeat; transform-origin: 50% 60%; }
.wordmark { font-family: var(--font-display); font-weight: 700; font-size: var(--t-4); letter-spacing: -0.02em; line-height: 1; }
.wordmark .accent { color: var(--accent); margin-left: 0.22em; }
.tagline { margin-top: var(--s-1); font-size: var(--t-1); color: var(--muted); }

.score { position: relative; display: flex; gap: var(--s-4); padding: var(--s-3) var(--s-4); align-items: stretch; border-left: 1px solid var(--glass-line); border-radius: 0 calc(var(--r-panel) - 1px) calc(var(--r-panel) - 1px) 0; }
/* the streak tank: the card fills as the streak grows (×5 at the brim), sloshes on every
   land and drains on a wipeout */
.tank { position: absolute; inset: 0; border-radius: inherit; overflow: hidden; pointer-events: none; }
.tank-liq { position: absolute; left: 0; right: 0; top: 0; height: 100%; transform: translateY(calc((1 - var(--lvl, 0)) * 100% + 8px)); transition: transform 1.2s cubic-bezier(.3,1.45,.5,1); }
.tank-liq.drain { transition: transform .75s cubic-bezier(.6,0,.85,.5); }
.tank-body { --lc: color-mix(in srgb, var(--liq) 24%, transparent); position: absolute; left: -30%; right: -30%; top: 0; height: 190%; transform-origin: 50% 0;
  background: linear-gradient(var(--lc), color-mix(in srgb, var(--liq-deep) 16%, transparent) 45%); }
.tank-body .swell { --wp: 40px; height: 6px; top: -5px; animation-duration: 3.4s; }
.score-main, .score-side { position: relative; }
.score-main { display: flex; flex-direction: column; justify-content: center; min-width: 96px; }
.score-main .value { font-family: var(--font-score); font-weight: 700; font-size: var(--t-6); line-height: 1; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; color: var(--accent); }
.score-side { display: grid; gap: var(--s-1); align-content: center; border-left: 1px solid var(--glass-line); padding-left: var(--s-4); }
.kv { display: grid; grid-template-columns: 56px auto auto; align-items: baseline; gap: var(--s-2); font-variant-numeric: tabular-nums; }
.kv .v { font-family: var(--font-score); font-weight: 700; font-size: var(--t-3); }
.kv .mult { position: relative; font-size: var(--t-1); color: var(--gold); font-weight: 700; }
.kv .mult.hot { color: var(--accent-ink); background: var(--gold); border-radius: var(--r-tag); padding: 0 var(--s-1); }
/* a hot multiplier fizzes */
.kv .mult.hot::before, .kv .mult.hot::after { content: ""; position: absolute; bottom: 60%; width: 3px; height: 3px; border-radius: 50%; background: var(--gold); animation: fizz 1.5s ease-in infinite; pointer-events: none; }
.kv .mult.hot::before { left: 22%; }
.kv .mult.hot::after { left: 64%; animation-delay: .7s; }
@keyframes fizz { from { transform: translateY(0) scale(.5); opacity: 0.9; } to { transform: translateY(-13px) scale(1); opacity: 0; } }

/* ---------- global stats ---------- */
/* the top right corner: everyone's numbers first; your own score sits by the brand */
.world { padding: var(--s-3); }
.world .state-tag:not([data-tone]), .world .state-tag[data-tone=""] { color: var(--faint); }
.world .panel-title { color: var(--ink); }
.globe { width: 14px; height: 14px; flex: none; fill: none; stroke: var(--accent); stroke-width: 1.6; }
/* live: a drop with rings spreading from it; offline: a dry dot */
.live-tag { display: inline-flex; align-items: center; gap: var(--s-1); }
.live-tag .drop { position: relative; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }
.world .live-tag[data-tone="good"] { color: var(--accent); background: var(--accent-tint); }
.live-tag[data-tone="good"] .drop::after { content: ""; position: absolute; inset: -1px; border-radius: 50%; border: 1.5px solid currentColor; animation: ping 2.2s cubic-bezier(.1,.6,.3,1) infinite; }
@keyframes ping { from { transform: scale(1); opacity: 0.9; } 70%, to { transform: scale(3.2); opacity: 0; } }
.world-grid { display: grid; grid-template-columns: repeat(5, 84px); gap: var(--s-1); margin-top: var(--s-2); }
.wstat { position: relative; isolation: isolate; display: grid; gap: 2px; padding: var(--s-1) var(--s-2); border-radius: var(--r-item); background: var(--tile); min-width: 0; }
.wstat .v { font-family: var(--font-score); font-weight: 700; font-size: var(--t-3); line-height: 1.1; font-variant-numeric: tabular-nums; white-space: nowrap; }
.wstat .k { font-size: var(--t-1); color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wstat .short { display: none; }
/* the lands tile is filled to everyone's landing rate */
.tile-liq { position: absolute; inset: 0; border-radius: inherit; overflow: hidden; z-index: -1; }
.tile-liq .tl { --lc: color-mix(in srgb, var(--liq) 22%, transparent); position: absolute; left: 0; right: 0; top: 0; height: 100%;
  background: linear-gradient(var(--lc), color-mix(in srgb, var(--liq) 6%, transparent)); transform: translateY(calc((1 - var(--lvl, 0)) * 100% + 6px)); transition: transform 1.4s cubic-bezier(.3,1.4,.5,1); }
.tile-liq .swell { --wp: 24px; height: 4px; top: -3.5px; animation-duration: 3s; }
@media (max-width: 1180px) {
  .world { padding: var(--s-2); }
  .world-grid { grid-template-columns: repeat(5, 80px); gap: var(--s-1); margin-top: var(--s-2); }
  .wstat { padding: var(--s-1) var(--s-2); }
  .wstat .v { font-size: var(--t-3); }
  .wstat .long, #w-rate { display: none; }
  .wstat .short { display: inline; }
}
@media (max-width: 960px) {
  /* the brand keeps its mark and gives the width to the global stats */
  .brand .brand-text { display: none; }
  .brand { padding: var(--s-3); }
  /* with your score hidden, the mark alone would be an empty card: the card goes too */
  .player:has(> .score[hidden]) { display: none; }
}
@media (max-width: 860px) {
  /* the card goes under the global stats once they reach across */
  .hud-top { flex-direction: column; align-items: flex-end; }
  .world { order: -1; }
  .player { align-self: flex-start; }
}

/* ---------- telemetry ---------- */
/* bottom left, above the bottom row (the toolbar's 50 px) with the cards' 16 px between */
.telemetry { position: absolute; left: var(--gutter); bottom: calc(max(16px, env(safe-area-inset-bottom, 0px)) + 66px); width: 264px; padding: var(--s-3); pointer-events: auto; }
.state-tag { margin-left: auto; padding: var(--tag-pad); border-radius: var(--r-tag); background: var(--tile); color: var(--muted); font-size: var(--t-1); font-weight: 600; }
.state-tag[data-tone="air"] { color: var(--air); background: rgba(159,208,255,0.12); }
.state-tag[data-tone="good"] { color: var(--bg); background: var(--good); }
.state-tag[data-tone="bad"] { color: var(--bad); background: rgba(255,75,62,0.12); }
.tele-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-2); margin: var(--s-3) 0 var(--s-2); }
.tele { position: relative; display: grid; grid-template-columns: auto 1fr; align-items: baseline; column-gap: var(--s-1); row-gap: 2px; padding: var(--s-2); border-radius: var(--r-item); background: var(--tile); contain: layout; }
.tele .k { grid-column: 1 / -1; font-size: var(--t-1); color: var(--muted); }
.tele .v { font-family: var(--font-display); font-weight: 700; font-size: var(--t-4); font-variant-numeric: tabular-nums; }
.tele .u { font-size: var(--t-1); color: var(--muted); }
/* a bottle turning with yours, and a water line tipping with the slosh */
.tele-bot { position: absolute; right: 10px; top: 9px; width: 9px; height: 18px; color: var(--muted); transform-origin: 50% 55%; }
.slosh-line { position: absolute; right: 8px; top: 16px; width: 20px; height: 2px; border-radius: 2px; background: var(--liq); }
.slosh-line::before, .slosh-line::after { content: ""; position: absolute; top: -5px; width: 1px; height: 12px; background: var(--faint); }
.slosh-line::before { left: -2px; } .slosh-line::after { right: -2px; }
#scope { width: 100%; height: 92px; display: block; border-radius: var(--r-item); background: var(--tile); }
.scope-legend { display: flex; gap: var(--s-3); margin-top: var(--s-2); font-size: var(--t-1); color: var(--muted); }
.lg::before { content: ""; display: inline-block; width: 12px; height: 2px; margin-right: 6px; vertical-align: middle; border-radius: 2px; }
.lg-omega::before { background: var(--accent); }
.lg-spread::before { background: rgba(242,239,228,0.8); }

/* ---------- camera ---------- */
/* on the right edge at mid-height, clear of the top cards and the toolbar: a joystick to
   look around (ui/Hud.js _wireCamera), the zoom, and back to your eyes once it moved */
.campad { position: absolute; right: var(--gutter-r); top: 50%; transform: translateY(-50%); display: grid; justify-items: center; gap: var(--s-1); padding: var(--s-1); pointer-events: auto; }
.joy { position: relative; width: 48px; height: 48px; border-radius: 50%; background: var(--tile); box-shadow: inset 0 0 0 1px var(--glass-line); touch-action: none; cursor: grab; }
.joy.held { cursor: grabbing; }
.joy:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.joy-arrows { position: absolute; inset: 0; width: 100%; height: 100%; fill: var(--muted); transition: fill .2s; }
.joy:hover .joy-arrows, .joy.held .joy-arrows { fill: var(--ink); }
/* the knob holds the drink, and springs back to the middle when let go */
.joy-knob { position: absolute; left: 50%; top: 50%; width: 24px; height: 24px; margin: -12px 0 0 -12px; display: grid; place-items: center; border-radius: 50%; background: var(--liq); color: var(--liq-ink); box-shadow: 0 2px 8px rgba(0,0,0,0.45); transition: transform .35s cubic-bezier(.3,1.6,.5,1), background .3s; }
.joy.held .joy-knob { transition: background .3s; }
.joy-knob svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linejoin: round; }
/* the first time on a device: where the camera is, in the words of that device */
.camtip { position: absolute; right: calc(var(--gutter-r) + 58px + var(--s-2)); top: 50%; transform: translateY(-50%); max-width: 190px; padding: var(--s-2) var(--s-3); border-radius: var(--r-item); background: var(--liq); color: var(--liq-ink); font-size: var(--t-2); font-weight: 600; line-height: 1.3; pointer-events: none; box-shadow: 0 10px 28px -10px rgba(0,0,0,0.7); animation: tipIn .5s cubic-bezier(.3,1.4,.5,1); }
.camtip::after { content: ""; position: absolute; right: -5px; top: 50%; width: 10px; height: 10px; margin-top: -5px; background: inherit; transform: rotate(45deg); border-radius: 2px; }
@keyframes tipIn { from { opacity: 0; translate: 8px 0; } }
.camtip .for-mouse { display: none; }
@media (hover: hover) and (pointer: fine) { .camtip .for-touch { display: none; } .camtip .for-mouse { display: inline; } }

/* ---------- bottom ---------- */
.hint { grid-column: 2; padding: var(--s-2) var(--s-3); display: flex; gap: var(--s-3); align-items: center; color: var(--muted); font-size: var(--t-1); transition: opacity .4s, transform .4s; }
.hint.dim { opacity: 0; transform: translateY(8px); pointer-events: none; }
.hint-main { color: var(--ink); }
/* the arrows act the throw out: up, then back */
.gesture b { display: inline-block; font-weight: inherit; }
.hint:not(.dim) .gesture .up { animation: gUp 2.4s cubic-bezier(.3,.7,.4,1) infinite; }
.hint:not(.dim) .gesture .down { animation: gDown 2.4s cubic-bezier(.3,.7,.4,1) infinite; }
@keyframes gUp { 0%, 30%, 100% { transform: none; } 12% { transform: translateY(-3px); } }
@keyframes gDown { 0%, 22%, 52%, 100% { transform: none; } 34% { transform: translateY(3px); } }
@media (max-width: 1100px) { .hint-alt { display: none; } }
@media (max-width: 860px) { .hint .long { display: none; } .hud-bottom { grid-template-columns: minmax(0, 1fr) auto; } .hint { grid-column: 1; justify-self: start; } }
kbd { font-family: var(--font-ui); font-size: var(--t-1); font-weight: 600; padding: var(--tag-pad); border-radius: var(--r-tag); background: var(--accent-tint); color: var(--accent); }
.controls { grid-column: -2 / -1; justify-self: end; display: flex; gap: var(--s-1); padding: var(--s-1); }
/* any element can hold a cup of the drink (ui/Hud.js _liquify) */
/* (position only where nothing else places the element: :where() weighs nothing) */
:where(.sloshy) { position: relative; }
.sloshy { isolation: isolate; }
.glass.sloshy { --lvl-hover: 84%; }
#board-tabs .chip { --lvl-on: var(--lvl-rest, 105%); }   /* the tabs' blob shows which is on */
/* a control's label shakes and squeezes as the toolbar's icons do; the button stays put */
.sloshy > .in { display: inline-flex; align-items: center; gap: inherit; transition: transform .25s cubic-bezier(.3,1.6,.5,1); }
.chip.pour { --cup: var(--c); --lvl-on: 8%; }
/* each button is a cup of the drink: hovering raises the level and (ui/Hud.js _jiggle)
   shakes the cup and sloshes it, pressing fills it, and a button that's on keeps it half
   full; at rest the drink is still */
.icon-btn { position: relative; isolation: isolate; overflow: hidden; width: 40px; height: 40px; display: grid; place-items: center; border-radius: calc(var(--r-panel) - var(--s-1)); border: 1px solid transparent; background: transparent; color: var(--ink); cursor: pointer; transition: color .2s; }
.cup { position: absolute; inset: 0; z-index: -1; overflow: hidden; border-radius: inherit; pointer-events: none; }
.cup-lvl { position: absolute; inset: 0; transform: translateY(var(--lvl-rest, 105%)); transition: transform .5s cubic-bezier(.3,1.5,.5,1); }
/* the surface is two waves, each cup's own length (a share of its width, so wide cups swell
   rather than ripple), height and phase (ui/Hud.js _liquify);
   the back one paler and a little lower, so where they cross the drink looks deeper */
.cup-wave { position: absolute; left: -30%; right: -30%; top: 0; height: 160%; transform-origin: 50% 8%; background: color-mix(in srgb, var(--cup, var(--liq)) 22%, transparent);
  -webkit-mask: var(--wave) var(--wp, 0px) 0 / var(--wl, 22px) var(--wh, 6px) repeat-x, linear-gradient(#000 0 0) 0 calc(var(--wh, 6px) - 1px) / 100% 100% no-repeat;
  mask: var(--wave) var(--wp, 0px) 0 / var(--wl, 22px) var(--wh, 6px) repeat-x, linear-gradient(#000 0 0) 0 calc(var(--wh, 6px) - 1px) / 100% 100% no-repeat; }
.cup-wave.b { --wl: var(--wl2, 34px); --wh: var(--wh2, 5px); --wp: var(--wp2, 0px); top: 2px; opacity: 0.55; }
.icon-btn svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; transition: transform .25s cubic-bezier(.3,1.6,.5,1); }
@media (hover: hover) {
  .sloshy:hover:not(:disabled) > .cup .cup-lvl { transform: translateY(var(--lvl-hover, 70%)); }
}
:is(.icon-btn, [data-slosh="control"]):active:not(:disabled) > .cup .cup-lvl { transform: translateY(22%); transition-duration: .14s; }
[data-slosh="control"]:active:not(:disabled) > .in { transform: scale(.94); }
.icon-btn:active:not(:disabled) svg { transform: scale(.88); }
.icon-btn:focus-visible, .text-btn:focus-visible, .chip:focus-visible, input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.icon-btn[aria-pressed="true"], .icon-btn[aria-expanded="true"] { color: var(--liq); }
.sloshy[aria-pressed="true"] > .cup .cup-lvl, .sloshy[aria-expanded="true"] > .cup .cup-lvl, .sloshy[aria-checked="true"] > .cup .cup-lvl { transform: translateY(var(--lvl-on, 52%)); }
/* saving the replay: a red dot while it records */
.icon-btn#btn-save.rec { color: var(--bad); }
.icon-btn#btn-save.rec::after { content: ""; position: absolute; top: 7px; right: 7px; width: 6px; height: 6px; border-radius: 50%; background: var(--bad); animation: rec 1s ease-in-out infinite; }
.icon-btn#btn-sound[aria-pressed="false"] .wave { opacity: 0.15; }
.icon-btn:disabled { color: var(--faint); cursor: not-allowed; }
.icon-btn#btn-replay:not(:disabled) { color: var(--accent); }
.icon-btn#btn-replay .rec { fill: currentColor; stroke: none; }
.icon-btn#btn-replay:not(:disabled) .rec { animation: rec 1.6s ease-in-out infinite; }
@keyframes rec { 50% { opacity: 0.25; } }

/* ---------- tether ---------- */
#tether { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; }
#tether-path { fill: none; stroke: var(--accent); stroke-width: 1.5; stroke-dasharray: 4 5; opacity: 0.8; filter: drop-shadow(0 0 4px var(--accent)); }
#tether-knob { fill: rgba(255,182,39,0.12); stroke: var(--accent); stroke-width: 1.5; filter: drop-shadow(0 0 6px var(--accent)); }
#tether:not(.on) { display: none; }

/* ---------- banner ---------- */
.banner { position: absolute; left: 50%; top: 21%; transform: translate(-50%, 0); text-align: center; pointer-events: none; opacity: 0; width: min(92vw, 720px); }
.banner.show { animation: bannerIn 1.9s cubic-bezier(.2,.8,.2,1) forwards; }
@keyframes bannerIn {
  0% { opacity: 0; transform: translate(-50%, 10px) scale(0.96); }
  8% { opacity: 1; transform: translate(-50%, 0) scale(1); }
  80% { opacity: 1; }
  100% { opacity: 0; transform: translate(-50%, -8px); }
}
.banner-title { position: relative; font-family: var(--font-display); font-weight: 700; font-size: clamp(40px, 9vw, 92px); letter-spacing: -0.04em; text-transform: uppercase; line-height: 0.95; color: var(--ink); text-shadow: 0 2px 24px rgba(0,0,0,0.45); text-wrap: balance; }
.banner-title .word { display: inline-block; white-space: nowrap; }
.banner-title .ch { display: inline-block; transform-origin: 50% 62%; }
/* a rendered letter (ui/Hud.js glyph) in liquid glass, as tall as the cap height of the
   text it stands for. From the bottom: its soft shadow; the glass body, which frosts,
   lifts and (in Chromium) bends the stadium behind it at its rounded rims; the drink,
   which the game pours inside the letter's inner outline and keeps level as the letter
   moves (Motion.js GlyphDrink), clear too, its surface catching the light and bubbles
   rising to it; and the glass's highlights. Nothing filters the letter as a whole: that
   would keep the glass from seeing what's behind it. */
.banner-title .glyph { position: relative; height: 0.72em; vertical-align: baseline; }
.glyph .g-shell { position: relative; display: block; height: 100%; width: auto; -webkit-user-drag: none; mix-blend-mode: screen; }   /* its highlights light the glass, never cloud it */
.glyph .g-shade { position: absolute; inset: 0; width: 100%; height: 100%; filter: brightness(0) blur(6px); opacity: 0.45; transform: translateY(3px); }
.glyph .g-glass, .glyph .g-drink { position: absolute; inset: 0; -webkit-mask-size: 100% 100%; mask-size: 100% 100%; -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; }
.glyph .g-glass { background: linear-gradient(170deg, rgba(255,255,255,0.12), rgba(255,255,255,0.02) 45%, rgba(255,255,255,0.06));
  -webkit-backdrop-filter: blur(2px) saturate(1.7) brightness(1.15); backdrop-filter: blur(2px) saturate(1.7) brightness(1.15); }
.glyph .g-drink { overflow: hidden; }
.glyph .g-surf { position: absolute; left: 50%; top: 50%; width: 400%; height: 400%; transform-origin: 0 0; overflow: hidden;
  background: linear-gradient(rgba(255,255,255,0.9), rgba(255,255,255,0) 2.5px),
    linear-gradient(color-mix(in srgb, var(--drink-top) 80%, transparent), color-mix(in srgb, var(--drink) 50%, transparent) 6px, color-mix(in srgb, var(--drink-deep) 72%, transparent) 24%); }
/* the letter spans the middle quarter of its drink's box, the top quarter under the
   surface; a few bubbles of different sizes rise through it, on a tile that repeats every
   72 px, each letter's out of step with the next (ui/Hud.js glyph: --bx, --bd) */
.glyph .g-surf::before { content: ""; position: absolute; left: 37.5%; width: 25%; top: 0; height: calc(25% + 72px);
  background: radial-gradient(circle at 18% 22%, rgba(255,255,255,0.8) 0 1.3px, transparent 2px),
    radial-gradient(circle at 63% 58%, rgba(255,255,255,0.6) 0 1px, transparent 1.6px),
    radial-gradient(circle at 84% 9%, rgba(255,255,255,0.55) 0 0.8px, transparent 1.3px);
  background-size: 44px 72px; background-position: var(--bx, 0) 0;
  animation: rise 2.2s linear var(--bd, 0s) infinite; }
@keyframes rise { to { transform: translateY(-72px); } }
.banner { --drink: #4aa3f0; --drink-deep: #1d5f9f; --drink-top: #d4ecff; }
.banner[data-tone="good"] { --drink: #5bc23a; --drink-deep: #2c7a14; --drink-top: #d2f8b8; }
.banner[data-tone="gold"] { --drink: #ffc21f; --drink-deep: #c7820a; --drink-top: #fff1b8; }
.banner[data-tone="bad"] { --drink: #e8322a; --drink-deep: #8f1410; --drink-top: #ffbcb4; }
.banner-splash { position: absolute; left: 0; right: 0; top: clamp(20px, 4.4vw, 44px); height: 0; }
.banner[data-tone="good"] .banner-title { color: var(--good); }
.banner[data-tone="gold"] .banner-title { color: var(--gold); }
.banner[data-tone="bad"] .banner-title { color: var(--bad); }

/* ---------- sheets: settings, contact, top 10, your name ---------- */
/* a sheet lands like a water balloon from its button, and drains away when closed */
.settings, .sheet { transform-origin: 100% 100%; animation: sheetIn .44s cubic-bezier(.2,1.2,.35,1); }
.settings.closing, .sheet.closing { animation: sheetOut .17s ease-in forwards; pointer-events: none; }
@keyframes sheetIn { 0% { opacity: 0; transform: translateY(14px) scale(.9, .72); } 50% { opacity: 1; transform: translateY(-3px) scale(1.012, 1.03); } 75% { transform: scale(.996, .99); } 100% { transform: none; } }
@keyframes sheetOut { to { opacity: 0; transform: translateY(10px) scale(.97, .86); } }
.settings { position: absolute; right: var(--gutter-r); bottom: calc(max(16px, env(safe-area-inset-bottom, 0px)) + 66px); width: min(330px, calc(100vw - 32px)); max-height: calc(100dvh - max(16px, env(safe-area-inset-top, 0px)) - max(16px, env(safe-area-inset-bottom, 0px)) - 82px); overflow-y: auto; overscroll-behavior: contain; padding: var(--s-4); display: grid; gap: var(--s-4); z-index: 5; }
.settings .group-title { margin: var(--s-1) 0 calc(-1 * var(--s-1)); padding-top: var(--s-3); border-top: 1px solid var(--glass-line); font: inherit; font-size: var(--t-2); font-weight: 700; color: var(--ink); }
.settings .panel-title + .group-title { border-top: 0; padding-top: 0; }
.settings .close, .sheet .close { width: 32px; height: 32px; margin: calc(-1 * var(--s-2)) calc(-1 * var(--s-2)) calc(-1 * var(--s-2)) auto; display: grid; place-items: center; border: 0; border-radius: var(--r-item); background: none; color: var(--muted); font-size: var(--t-4); line-height: 1; cursor: pointer; }
.settings .close:hover, .sheet .close:hover { color: var(--ink); background: var(--tile); }
.field { display: grid; gap: var(--s-2); }
.field-head { display: flex; justify-content: space-between; font-size: var(--t-2); color: var(--muted); }
.field-val { color: var(--ink); font-variant-numeric: tabular-nums; }
.field-note { font-size: var(--t-1); color: var(--muted); line-height: 1.45; }
/* the fill level is a bottle you fill: drag fast and the water lags behind, then sloshes */
.fill-row { display: flex; align-items: center; gap: var(--s-3); }
.fill-row input { flex: 1; min-width: 0; }
.fill-bottle { position: relative; flex: none; width: 16px; height: 50px; }
/* the drink you pour sits inside the empty bottle's render, masked by it, under it */
.fb-glass { position: absolute; inset: 0; overflow: hidden; -webkit-mask: var(--bottle-empty) center / contain no-repeat; mask: var(--bottle-empty) center / contain no-repeat; }
.fb-shell { position: absolute; inset: 0; background: var(--bottle-empty) center / contain no-repeat; pointer-events: none; }
.fb-liq { position: absolute; inset: 0; transform: translateY(73.6%); transition: transform .25s cubic-bezier(.3,1.3,.5,1); }
.fb-surf { --lc: var(--liq); position: absolute; left: -90%; right: -90%; top: 0; height: 120%; transform-origin: 50% 0; background: linear-gradient(var(--liq), var(--liq-deep)); }
.fb-surf .swell { --wp: 10px; height: 3px; top: -2.5px; animation-duration: 1.8s; }
input[type="range"] { -webkit-appearance: none; appearance: none; width: 100%; height: 24px; background: transparent; cursor: pointer; }
input[type="range"]::-webkit-slider-runnable-track { height: 4px; border-radius: 4px; background: linear-gradient(90deg, var(--liq-deep), var(--liq) var(--p, 45%), rgba(255,255,255,0.12) var(--p, 45%)); }
input[type="range"]::-moz-range-track { height: 4px; border-radius: 4px; background: rgba(255,255,255,0.12); }
input[type="range"]::-moz-range-progress { height: 4px; border-radius: 4px; background: var(--liq); }
input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 16px; height: 16px; margin-top: -6px; border-radius: 50%; background: var(--ink); border: 3px solid var(--liq); transition: transform .2s cubic-bezier(.3,1.6,.5,1); }
input[type="range"]:active::-webkit-slider-thumb { transform: scale(1.25); }
input[type="range"]::-moz-range-thumb { width: 12px; height: 12px; border-radius: 50%; background: var(--ink); border: 3px solid var(--liq); }
.chips { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.chip { display: inline-flex; align-items: center; gap: var(--s-2); padding: var(--s-2) var(--s-3); border-radius: var(--r-item); border: 1px solid transparent; background: var(--tile); color: var(--ink); font: inherit; font-size: var(--t-2); font-weight: 500; cursor: pointer; transition: border-color .2s, background .2s, color .2s; }
.chip[aria-checked="true"], .chip[aria-pressed="true"] { background: var(--accent-tint); color: var(--accent); }
.chip:hover { border-color: rgba(255,255,255,0.14); }
/* liquids: a drop of each; the picked one is poured full of it */
.chip.pour[aria-checked="true"] { color: var(--ink); background: var(--tile); border-color: color-mix(in srgb, var(--c) 55%, transparent); }
.chip.pour .sw { width: 10px; height: 10px; border-radius: 50% 0 50% 50%; transform: rotate(-45deg); background: var(--c); box-shadow: inset 0 0 0 1px rgba(255,255,255,0.3); }
/* panels on screen: a row of switches, two columns */
.switches { display: grid; grid-template-columns: 1fr 1fr; gap: 0 var(--s-4); }
.switch { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2); min-height: 36px; margin: 0 calc(-1 * var(--s-2)); padding: 0 var(--s-2); border: 0; border-radius: var(--r-item); background: none; color: var(--ink); font: inherit; font-size: var(--t-2); text-align: left; cursor: pointer; }
.switch[aria-checked="false"] { color: var(--muted); }
.switch .knob { position: relative; flex: none; width: 32px; height: 20px; border-radius: 10px; background: var(--tile); box-shadow: inset 0 0 0 1px var(--glass-line); transition: background .2s; }
.switch .knob::after { content: ""; position: absolute; top: 3px; left: 3px; width: 14px; height: 14px; border-radius: 50%; background: var(--muted); transition: transform .25s cubic-bezier(.3,1.4,.5,1), background .2s; }
.switch[aria-checked="true"] .knob { background: color-mix(in srgb, var(--liq) 70%, transparent); }
.switch[aria-checked="true"] .knob::after { transform: translateX(12px); background: var(--ink); }
.switch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-tag); }
/* graphics: one control, three parts */
.segmented { gap: 0; padding: 2px; border-radius: var(--r-item); background: var(--tile); flex-wrap: nowrap; }
.segmented .chip { flex: 1; justify-content: center; background: none; border-radius: calc(var(--r-item) - 2px); }
.segmented .chip[aria-checked="true"] { background: none; color: var(--liq); }   /* the drink in it, as a toolbar button that's on */
.text-btn { justify-self: start; background: var(--tile); border: 1px solid transparent; border-radius: var(--r-item); padding: var(--s-2) var(--s-3); color: var(--muted); font: inherit; font-size: var(--t-2); cursor: pointer; }
.text-btn.danger:hover { color: var(--bad); border-color: rgba(255,75,62,0.4); }
a.text-btn { text-decoration: none; }
.settings-links { display: flex; flex-wrap: wrap; gap: var(--s-2); justify-content: space-between; }
.text-btn.armed { color: var(--bad); border-color: rgba(255,75,62,0.6); animation: wobble .4s ease-out; }
@keyframes wobble { 25% { transform: rotate(-2deg); } 50% { transform: rotate(1.6deg); } 75% { transform: rotate(-.8deg); } }

.sheet { position: absolute; right: var(--gutter-r); bottom: calc(max(16px, env(safe-area-inset-bottom, 0px)) + 66px); width: min(380px, calc(100vw - 32px)); max-height: calc(100dvh - max(16px, env(safe-area-inset-top, 0px)) - max(16px, env(safe-area-inset-bottom, 0px)) - 82px); overflow-y: auto; overscroll-behavior: contain; touch-action: pan-y; padding: var(--s-4); display: grid; gap: var(--s-3); z-index: 6; }
.sheet form { display: grid; gap: var(--s-3); }
.sheet .field-val { color: var(--muted); font-size: var(--t-1); text-align: right; }
/* top 10: the tabs share one blob of the drink that flows to the picked one */
.chips.tabs { position: relative; isolation: isolate; }
.tab-blob { position: absolute; left: 0; top: 0; width: 0; height: 0; border-radius: var(--r-item); z-index: -1; pointer-events: none; background: var(--liq-tint); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--liq) 42%, transparent);
  transition: transform .5s cubic-bezier(.3,1.3,.5,1), width .5s cubic-bezier(.3,1.3,.5,1); }
#board-tabs .chip { background: transparent; }
#board-tabs .chip[aria-selected="true"] { color: var(--liq); }
/* rows flip in one after another, and each pours a bar of its score against #1 */
.board-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; counter-reset: rank; }
.board-list li { position: relative; isolation: isolate; overflow: hidden; display: grid; grid-template-columns: 40px 1fr auto; align-items: center; gap: var(--s-3); padding: var(--s-2) var(--s-3); border-radius: var(--r-item); background: var(--tile); font-size: var(--t-3);
  animation: rowIn .5s cubic-bezier(.2,1.3,.4,1) backwards; animation-delay: calc(var(--i, 0) * 45ms); }
.board-list li::before { content: ""; position: absolute; inset: 0; z-index: -1; transform-origin: 0 50%; transform: scaleX(var(--v, 0));
  background: linear-gradient(90deg, color-mix(in srgb, var(--liq) 6%, transparent), color-mix(in srgb, var(--liq) 22%, transparent));
  animation: pourX .9s cubic-bezier(.2,.9,.3,1) backwards; animation-delay: calc(var(--i, 0) * 45ms + 140ms); }
@keyframes rowIn { from { opacity: 0; transform: perspective(500px) rotateX(-80deg) translateY(-6px); } }
@keyframes pourX { from { transform: scaleX(0); } }
.board-list li .rank { font-family: var(--font-score); font-weight: 700; color: var(--muted); }
/* the first three: a gold, silver and bronze cup */
.board-list li .rank.medal { display: inline-flex; align-items: center; gap: var(--s-1); }
.board-list li .rank svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.board-list li .rank svg .bowl { fill: currentColor; fill-opacity: 0.4; }
.board-list li .rank.gold { color: #f2c94c; }
.board-list li .rank.silver { color: #c9d2db; }
.board-list li .rank.bronze { color: #d68c55; }
.board-list li .who { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.board-list li .who.anon { color: var(--muted); }
.board-list li .val { font-family: var(--font-score); font-weight: 700; font-variant-numeric: tabular-nums; }
.board-list li.you { color: var(--liq); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--liq) 40%, transparent); }
.board-list li.you::after { content: ""; position: absolute; inset: 0; z-index: -1; background: linear-gradient(100deg, transparent 35%, color-mix(in srgb, var(--liq) 22%, transparent) 50%, transparent 65%); transform: translateX(-100%); animation: caustic 3s ease-in-out 1s infinite; }
@keyframes caustic { 55%, 100% { transform: translateX(100%); } }
.board-list li.empty { display: block; color: var(--muted); background: none; padding: var(--s-1) 0; font-size: var(--t-2); animation: none; }
.board-link { display: inline-flex; align-items: center; gap: var(--s-1); min-height: 24px; margin-left: auto; padding: var(--tag-pad); border: 0; border-radius: var(--r-tag); background: var(--accent-tint); color: var(--accent); font: inherit; font-size: var(--t-1); font-weight: 600; cursor: pointer; white-space: nowrap; }
.board-link svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; transform-origin: 50% 90%; }
.board-link:hover { filter: brightness(1.15); }
@media (hover: hover) { .board-link:hover svg { animation: wiggle .5s ease-out; } }
@keyframes wiggle { 25% { transform: rotate(-14deg); } 55% { transform: rotate(10deg); } 80% { transform: rotate(-4deg); } }
.board-link + .state-tag { margin-left: 0; }
/* a notice falls as a drop and splats into a pill, then dries up */
.toast { position: absolute; left: 50%; bottom: calc(max(16px, env(safe-area-inset-bottom, 0px)) + 124px); transform: translateX(-50%); z-index: 7; isolation: isolate; max-width: calc(100vw - 32px); padding: var(--s-2) var(--s-3); border-radius: var(--r-item); color: var(--liq-ink); font-size: var(--t-3); font-weight: 600; pointer-events: none; animation: toastText 3.6s ease forwards; }
.toast::before { content: ""; position: absolute; inset: 0; z-index: -1; border-radius: var(--r-item); background: var(--liq); box-shadow: 0 12px 32px -12px rgba(0,0,0,0.7); animation: splat 3.6s forwards; }
@keyframes splat {
  0% { transform: translateY(-48px) scale(.05, .32); border-radius: 50%; opacity: 0; }
  3% { opacity: 1; }
  9% { transform: translateY(0) scale(.05, .32); border-radius: 50%; animation-timing-function: cubic-bezier(.2,1.4,.4,1); }
  20% { transform: scale(1.05, .86); border-radius: var(--r-item); }
  26%, 86% { transform: none; border-radius: var(--r-item); opacity: 1; }
  100% { transform: scale(.94, .3); opacity: 0; }
}
@keyframes toastText { 0%, 14% { color: transparent; } 22%, 86% { color: var(--liq-ink); } 100% { color: transparent; } }
.input { width: 100%; padding: var(--s-2) var(--s-3); border-radius: var(--r-item); border: 1px solid transparent; background: var(--tile); color: var(--ink); font: inherit; font-size: 16px;   /* phones zoom into smaller fields */ -webkit-user-select: text; user-select: text; }
.input:focus { outline: none; border-color: rgba(255,182,39,0.5); background: rgba(255,255,255,0.07); }
textarea.input { resize: vertical; min-height: 96px; line-height: 1.45; }
.trap { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.form-row { position: relative; display: flex; align-items: center; gap: var(--s-3); }
.btn-primary { min-height: 44px; padding: 0 var(--s-5); border: 0; border-radius: var(--r-item); background: var(--accent); color: var(--accent-ink); font: inherit; font-size: var(--t-3); font-weight: 600; cursor: pointer; transition: transform .2s cubic-bezier(.3,1.6,.5,1); }
.btn-primary:hover:not(:disabled) { filter: brightness(1.08); }
.btn-primary:active:not(:disabled) { transform: scale(.95, .9); }
.btn-primary:disabled { opacity: 0.55; cursor: progress; }
.form-status { font-size: var(--t-2); color: var(--muted); line-height: 1.35; }
.form-status[data-tone="good"] { color: var(--good); }
.form-status[data-tone="bad"] { color: var(--bad); }
.settings { touch-action: pan-y; }

/* ---------- boot ---------- */
/* a bottle fills as the game loads, and gets flipped once when it's full */
.boot { position: absolute; inset: 0; display: grid; place-items: center; background: var(--bg); z-index: 20; transition: opacity .8s ease, visibility .8s; }
.boot.done { opacity: 0; visibility: hidden; }
.boot-inner { width: min(420px, calc(100vw - 48px)); }
.boot-bottle { position: relative; width: 20px; height: 62px; margin-bottom: var(--s-4); transform-origin: 50% 60%; }
/* the empty bottle, and the full one over it, shown from the base up as the game loads */
.bb-img { position: absolute; inset: 0; background: var(--bottle-empty) center / contain no-repeat; }
.bb-full { background-image: var(--bottle-full); clip-path: inset(100% 0 0 0); transition: clip-path .35s cubic-bezier(.3,1.3,.5,1); }
.wordmark.big { font-size: var(--t-6); margin-bottom: var(--s-4); }
#boot-log { margin: 0 0 var(--s-4); min-height: 1.5em; font-size: var(--t-3); color: var(--muted); }
.boot-bar { height: 2px; background: rgba(255,255,255,0.08); border-radius: 2px; overflow: hidden; }
.boot-bar span { display: block; height: 100%; width: 0; background: var(--liq); transition: width .3s ease; }

/* ---------- responsive ---------- */
@media (max-width: 860px) {
  .telemetry { width: 200px; padding: var(--s-2); }
  .tele-grid { gap: var(--s-1); margin: var(--s-2) 0 0; }
  .tele { padding: var(--s-1) var(--s-2); }
  .tele .v { font-size: var(--t-3); }
  .tele-bot { top: 6px; right: 8px; width: 7px; height: 14px; }
  .slosh-line { top: 12px; width: 16px; }
  #scope, .scope-legend { display: none; }
  .hint-alt { display: none; }
}
@media (max-width: 560px) {
  /* the hint floats on the left above the telemetry strip, on the cards' edge */
  .hint { position: fixed; left: var(--gutter); right: var(--gutter-r); width: fit-content; max-width: calc(100vw - 32px); margin: 0 auto; bottom: calc(max(16px, env(safe-area-inset-bottom, 0px)) + 148px); }
  #telemetry[hidden] ~ .hud-bottom .hint { bottom: calc(max(16px, env(safe-area-inset-bottom, 0px)) + 66px); }
  .hud-bottom { column-gap: 0; }   /* the hint floats free: the controls get the whole row */
  /* notices go above the hint, which sits higher while the telemetry strip is shown */
  #telemetry:not([hidden]) ~ .toast { bottom: calc(max(16px, env(safe-area-inset-bottom, 0px)) + 202px); }
  .telemetry { left: var(--gutter); right: var(--gutter-r); width: auto; bottom: calc(max(16px, env(safe-area-inset-bottom, 0px)) + 64px); padding: var(--s-2); }
  .telemetry .panel-title { display: none; }
  .tele-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-1); margin: 0; }
  .tele { grid-template-columns: 1fr; padding: var(--s-1) var(--s-2); }
  .tele .k { grid-row: auto; width: auto; font-size: var(--t-1); }
  .tele .v { font-size: var(--t-2); text-align: left; }
  .tele .u { text-align: left; }
  .tele-bot { top: 7px; right: 7px; width: 6px; height: 12px; }
  .slosh-line { top: 12px; right: 6px; width: 12px; }
  .slosh-line::before, .slosh-line::after { display: none; }
  .brand { padding: var(--s-2); gap: var(--s-2); }
  .brand-mark { width: 24px; height: 24px; border-radius: var(--r-tag); }
  .brand-mark .bm { width: 11px; height: 22px; }
  .wordmark { font-size: var(--t-3); }
  .tagline { display: none; }
  .score { gap: var(--s-3); padding: var(--s-2) var(--s-3); }
  .score-main { min-width: 64px; }
  .score-main .value { font-size: var(--t-5); }
  .score-main .label { white-space: nowrap; }
  .score-side { padding-left: var(--s-3); }
  .kv { grid-template-columns: 44px auto auto; }
  .kv .v { font-size: var(--t-2); }
  .hint { max-width: calc(100vw - 200px); margin: 0; }
  /* the global stats fill the top row beside the brand mark, still headed as everyone's */
  .world { align-self: stretch; padding: var(--s-2); }
  /* and the card under them as wide, its stats on the same right edge */
  .player { align-self: stretch; }
  .score { flex: 1; }
  .score-main { flex: 1; }
  .world .panel-title { margin: 0 0 var(--s-2); font-size: var(--t-1); }
  .world .globe { width: 12px; height: 12px; }
  .world-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--s-1); margin: 0; }
  .wstat { padding: var(--s-1); }
  .wstat .v { font-size: var(--t-2); }
  .banner { top: 24%; }
}
@media (max-width: 560px) {
  /* on a phone the form hangs from the top, clear of the keyboard, and drops from there */
  .sheet { top: calc(max(16px, env(safe-area-inset-top, 0px)) + 8px); bottom: auto; left: var(--gutter); right: var(--gutter-r); width: auto; max-height: calc(100dvh - max(16px, env(safe-area-inset-top, 0px)) - 24px); transform-origin: 50% 0; }
}
@media (max-width: 420px) {
  /* narrow phones: eight buttons in the bar */
  .icon-btn { width: 36px; height: 36px; }
  .world .panel-title .long { display: none; }
}
.world .panel-title .short { display: none; }
@media (max-width: 420px) { .world .panel-title .short { display: inline; } }
@media (max-width: 360px) {
  .controls { gap: 2px; }
  .icon-btn { width: 32px; height: 32px; }
  /* five tiles are too narrow for their labels here: three, then two, and what sits
     under the global stats moves down by the extra row */
  .world-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* short windows: the telemetry drops its chart; where the
   world strip runs under the top bar, the telemetry goes under the strip */
@media (max-height: 700px) {
  #scope, .scope-legend { display: none; }
}
/* phones on their side (~375–430 px high): nothing but the bottle down the middle. The
   world panel becomes a small card in the top right corner, the brand and score card
   slims down in the top left and the telemetry runs along the bottom left, over the hint */
@media (max-height: 500px) and (orientation: landscape) {
  .hud-top { padding-top: max(12px, env(safe-area-inset-top, 0px)); }
  .hud-bottom { padding-bottom: max(12px, env(safe-area-inset-bottom, 0px)); }
  .brand .brand-text { display: none; }
  .brand { padding: var(--s-2); }
  .score { gap: var(--s-3); padding: var(--s-2) var(--s-3); }
  .score-main { min-width: 64px; }
  .score-main .value { font-size: var(--t-5); }
  .score-main .label { white-space: nowrap; }
  .score-side { gap: 2px; padding-left: var(--s-3); }
  .kv { grid-template-columns: 44px auto auto; }
  .kv .v { font-size: var(--t-2); }
  .hud-top { flex-direction: row; align-items: flex-start; }
  .world { order: 0; align-self: flex-start; width: 240px; padding: var(--s-2); }
  .world .panel-title { display: flex; flex-wrap: wrap; align-items: center; max-width: none; gap: var(--s-1); margin: 0 0 var(--s-2); font-size: var(--t-1); line-height: 1.2; }
  .world .panel-title .wt { display: none; }
  .world .panel-title .board-link, .world .panel-title .state-tag { grid-column: auto; margin-left: 0; }
  .world .panel-title .state-tag { margin-left: auto; }
  .world-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-1); margin: 0; }
  .wstat { padding: var(--s-1) var(--s-2); }
  .wstat .v { font-size: var(--t-2); }
  .wstat .long { display: none; }
  .wstat .short { display: inline; }
  .telemetry { width: 300px; bottom: calc(max(12px, env(safe-area-inset-bottom, 0px)) + 62px); padding: var(--s-2); }
  /* the camera in a row above the toolbar: mid-height on the right is the world card's */
  .campad { top: auto; bottom: calc(max(12px, env(safe-area-inset-bottom, 0px)) + 62px); transform: none; grid-auto-flow: column; align-items: center; }
  .camtip { top: auto; bottom: calc(max(12px, env(safe-area-inset-bottom, 0px)) + 66px); right: calc(var(--gutter-r) + 146px + var(--s-2)); transform: none; }
  .telemetry .panel-title { display: none; }
  .tele-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-1); margin: 0; }
  .tele { grid-template-columns: 1fr; padding: var(--s-1) var(--s-2); }
  .tele .v { font-size: var(--t-2); }
  .tele-bot { top: 6px; right: 6px; width: 6px; height: 12px; }
  .slosh-line { top: 11px; right: 6px; width: 12px; }
  .slosh-line::before, .slosh-line::after { display: none; }
  .banner { top: 14%; }
  .banner-title { font-size: clamp(34px, 7vw, 64px); }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
  :root { transition: none; }
}
