:root{
  --sky-1: #5ec6ff;
  --sky-2: #9cdcff;
  --sky-3: #d9f1ff;
  --ink:   #1a1330;
  --cream: #fff7e8;
  --danger:#e13f3f;

  /* cat */
  --fur:   #f6d6a8;
  --fur-sd:#d9ab73;
  --fur-hl:#ffe9c6;
  --patch: #e89360;
  --pink:  #ff7fa8;

  /* UI brick accent */
  --hud-1: #2e2a55;
  --hud-2: #ff6c4a;

  /* ===== DESIGN TOKENS (consolidated repeating patterns) ===== */

  /* heading fonts — two variants preserved for exact parity with prior inline values */
  --font-heading:    "Luckiest Guy", "Fredoka", sans-serif;  /* HUD surfaces (full fallback) */
  --font-heading-s:  "Luckiest Guy", sans-serif;             /* in-game FX text (short fallback) */

  /* 4-corner ink outline (fake text-stroke) at different offsets */
  --outline-2: 2px 2px 0 var(--ink), -2px 2px 0 var(--ink), 2px -2px 0 var(--ink), -2px -2px 0 var(--ink);
  --outline-3: 3px 3px 0 var(--ink), -3px 3px 0 var(--ink), 3px -3px 0 var(--ink), -3px -3px 0 var(--ink);
  --outline-4: 4px 4px 0 var(--ink), -4px 4px 0 var(--ink), 4px -4px 0 var(--ink), -4px -4px 0 var(--ink);

  /* 3D "brick" button — 3-layer (drop + top highlight + bottom inset) */
  --brick-shadow-rest:    0 5px 0 rgba(0,0,0,.35), inset 0 4px 0 rgba(255,255,255,.25), inset 0 -5px 0 rgba(0,0,0,.25);
  --brick-shadow-pressed: 0 2px 0 rgba(0,0,0,.35), inset 0 4px 0 rgba(255,255,255,.25), inset 0 -5px 0 rgba(0,0,0,.25);

  /* 3D "pill" button — 2-layer (drop + top highlight) */
  --pill-shadow-rest:    0 3px 0 rgba(0,0,0,.35), inset 0 2px 0 rgba(255,255,255,.2);
  --pill-shadow-pressed: 0 1px 0 rgba(0,0,0,.35), inset 0 2px 0 rgba(255,255,255,.2);
}
*{ box-sizing:border-box }
html,body{ margin:0; height:100%; background:#000; overflow:hidden;
  font-family:"Fredoka",system-ui,sans-serif; color:var(--ink);
  -webkit-user-select:none; user-select:none; touch-action:none;
}
/* ============================================================
   SCENE SYSTEM — all non-gameplay visual layers, in SSOT order.
   See SCENE.md §4.0 → §4.7. Bricks/cat/HUD/FX live elsewhere.
   ============================================================ */

/* ===== §4.0 Container — .stage + .frame ===== */
.stage{ position:fixed; inset:0; display:grid; place-items:center;
  background: radial-gradient(1200px 700px at 50% 120%, #1a1a2e, #000 70%);
}
.frame{
  position:relative;
  width: min(100vw, calc(100vh * 9/16));
  height: min(100vh, calc(100vw * 16/9));
  overflow:hidden;
  background: #000; /* safety fill; real bg is on ::before */
  box-shadow: 0 40px 120px rgba(0,0,0,.6);
  transition: background .6s ease;
}

/* ===== §4.1 Sky BG — .frame::before ===== */
/* Scenery background layer — only this + parallax/clouds/stars get the
   cycle hue/sat shift, so the cat/bricks/UI stay on their original colors. */
.frame::before{
  content:"";
  position:absolute; inset:0;
  z-index:0;
  background: linear-gradient(180deg, var(--sky-1) 0%, var(--sky-2) 60%, var(--sky-3) 100%);
  transition: background .6s ease, filter .6s ease;
  filter: hue-rotate(var(--cycle-hue, 0deg)) saturate(var(--cycle-sat, 1));
}
.parallax, .clouds, .stars, .baseplate, .trees, .sugars{
  filter: hue-rotate(var(--cycle-hue, 0deg)) saturate(var(--cycle-sat, 1));
  transition: filter .6s ease;
}
.frame.biome-candy::before { background: linear-gradient(180deg,#ffb4d8 0%, #c8a8ff 55%, #a8d8ff 100%); }
.frame.biome-jungle::before{ background: linear-gradient(180deg,#7fd5ff 0%, #b5ebc4 60%, #d9f7cf 100%); }
.frame.biome-sky::before   { background: linear-gradient(180deg,#5ec6ff 0%, #9cdcff 60%, #ffffff 100%); }
.frame.biome-space::before { background: linear-gradient(180deg,#0a1540 0%, #243a9e 60%, #4a5ed8 100%); }
.frame.biome-galaxy::before{ background: linear-gradient(180deg,#1a0933 0%, #3d1970 50%, #5a28a6 100%); }
.frame.biome-void::before  { background: linear-gradient(180deg,#020106 0%, #0a0518 55%, #120a28 100%); }

/* ===== §4.2 Parallax cubes — .parallax ===== */
/* faraway decor cubes behind gameplay */
.parallax{ position:absolute; inset:0; pointer-events:none; z-index:1; overflow:hidden; }
.pcube{
  position:absolute; opacity:.55;
  filter: blur(.3px);
}
.pcube .pc-top, .pcube .pc-left, .pcube .pc-right{ position:absolute; }

/* ===== §4.3 Clouds — .clouds ===== */
/* voxel clouds */
.clouds{ position:absolute; inset:0; pointer-events:none; z-index:2 }
.cloud{ position:absolute; display:grid; grid-template-columns: repeat(var(--cw,4), 16px); grid-template-rows: repeat(var(--ch,2), 16px); }
.cloud i{ background:#fff; box-shadow: inset 0 -4px 0 #d6e1ec; }
.cloud i.off{ background: transparent; box-shadow:none; }
@keyframes cloudDrift{ from{transform:translateX(0)} to{transform:translateX(-140vw)} }
/* biome-aware: clouds hide in dark biomes + candy (sugar decor takes their place) */
.frame.biome-candy .clouds,
.frame.biome-space .clouds,
.frame.biome-galaxy .clouds,
.frame.biome-void .clouds{ opacity:0; }

/* ===== §4.4 Stars — .stars ===== */
/* stars layer (shown on space/galaxy/void; see biome rule below) */
.stars{ position:absolute; inset:0; pointer-events:none; z-index:2; opacity:0; transition: opacity .6s; }
.stars .star{
  position:absolute; width:6px; height:6px; background:#fff;
  box-shadow:
    0 -3px 0 #fff, 0 3px 0 #fff,
    -3px 0 0 #fff, 3px 0 0 #fff;
  animation: twinkle 2s ease-in-out infinite alternate;
}
@keyframes twinkle{ from{opacity:.3} to{opacity:1} }
/* biome-aware: stars appear in dark biomes */
.frame.biome-space .stars,
.frame.biome-galaxy .stars{ opacity:1; }

/* ===== §4.4b Trees — JUNGLE decor (same voxel pattern as stars/clouds) ===== */
.trees{ position:absolute; inset:0; pointer-events:none; z-index:2; display:none; }
.frame.biome-jungle .trees{ display:block; }
.tree{ position:absolute; display:grid; }
.tree i{ display:block; }
.tree i.g{ background:#5fb668; box-shadow: inset 0 -3px 0 #3e8a4b; }
.tree i.d{ background:#3e8a4b; box-shadow: inset 0 -3px 0 #26602f; }
.tree i.off{ background: transparent; box-shadow: none; }

/* ===== §4.4c Sugar — CANDY decor (cubic candies, same toggle pattern as trees) ===== */
.sugars{ position:absolute; inset:0; pointer-events:none; z-index:2; display:none; }
.frame.biome-candy .sugars{ display:block; }
.sugar{ position:absolute; pointer-events:none; overflow:visible; }
/* stroke=fill + linejoin:round = mild corner radius without changing shape */
.sugar path{ stroke-linejoin:round; stroke-width:3; vector-effect:non-scaling-stroke; }
.sugar path.f1{ fill:var(--c1); stroke:var(--c1); }
.sugar path.f2{ fill:var(--c2); stroke:var(--c2); }
.sugar path.f3{ fill:var(--c3); stroke:var(--c3); }
/* 5 pastel palettes */
.sugar.white { --p1:#ffffff; --p2:#fbf4f7; --p3:#f0e6ec; --p4:#e6dae2; }
.sugar.pink  { --p1:#fff4f9; --p2:#ffdfeb; --p3:#fcc5da; --p4:#f5b8d0; }
.sugar.blue  { --p1:#f4faff; --p2:#dceefb; --p3:#c0dff4; --p4:#b4d6ee; }
.sugar.purple{ --p1:#f8f3fd; --p2:#e8dbf6; --p3:#d4c2ec; --p4:#cab6e4; }
.sugar.mint  { --p1:#f4fcf8; --p2:#dff5e9; --p3:#c4ead8; --p4:#b8e0cd; }
/* 6 face-color patterns */
.sugar.q1{ --c1:var(--p1); --c2:var(--p3); --c3:var(--p2); }
.sugar.q2{ --c1:var(--p2); --c2:var(--p4); --c3:var(--p1); }
.sugar.q3{ --c1:var(--p3); --c2:var(--p1); --c3:var(--p2); }
.sugar.q4{ --c1:var(--p1); --c2:var(--p2); --c3:var(--p3); }
.sugar.q5{ --c1:var(--p4); --c2:var(--p2); --c3:var(--p3); }
.sugar.q6{ --c1:var(--p2); --c2:var(--p1); --c3:var(--p4); }
/* sizes */
.sugar.xs{ width:18px; height:18px; }
.sugar.s { width:22px; height:22px; }
.sugar.m { width:28px; height:28px; }
.sugar.l { width:32px; height:32px; }
/* skews — break rigid grid feel */
.sugar.skew-a{ transform:rotate(-6deg) scaleY(.92); }
.sugar.skew-b{ transform:rotate(8deg) scaleX(.95); }
.sugar.skew-c{ transform:rotate(-12deg) scaleY(1.05); }
.sugar.skew-d{ transform:rotate(15deg) scaleX(1.08) scaleY(.94); }
.sugar.skew-e{ transform:rotate(-3deg); }
.sugar.skew-f{ transform:rotate(20deg) scaleY(.88); }

/* ===== §4.5 Baseplate — .baseplate ===== */
/* baseplate ground — color driven by biome via --g-surface/--g-edge/--g-shade */
.baseplate{
  position:absolute; left:0; right:0; bottom:0; height: 12%;
  z-index: 3;
  background-color: var(--g-surface, #9b86d3);
  background-image:
    radial-gradient(circle at 12px 12px, rgba(255,255,255,.25) 3px, transparent 4px),
    radial-gradient(circle at 36px 36px, rgba(0,0,0,.08) 3px, transparent 4px);
  background-size: 48px 48px, 48px 48px;
  background-position: 0 0, 24px 24px;
  border-top: 6px solid var(--g-edge, #3e2f6b);
  box-shadow: inset 0 -14px 0 var(--g-shade, #6e5aa8);
  transition: background-color .6s ease, border-color .6s ease, box-shadow .6s ease;
}

/* ===== §4.6 Glass bricks overlay — .frame.glass-bricks =====
   Only for biomes flagged glass:true (VOID).
   Keeps the biome's palette, but dims the faces into plastic-translucent
   panels and adds a crisp white outline + subtle outer glow. */
.frame.glass-bricks .brick .top,
.frame.glass-bricks .brick .front,
.frame.glass-bricks .brick .side{
  opacity: .55;
}
.frame.glass-bricks .brick{
  outline: 2px solid rgba(255,255,255,.8);
  outline-offset: -2px;
  box-shadow:
    0 0 14px rgba(180,140,255,.55),
    inset 0 0 18px rgba(255,255,255,.25);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
/* studs (dots on top) get a clear highlight so they still read */
.frame.glass-bricks .brick .studs span{
  background: rgba(255,255,255,.7);
  box-shadow: 0 0 6px rgba(255,255,255,.6);
}

/* ===== §4.7 Biome banner — .biome-banner ===== */
/* biome banner */
.biome-banner{
  position:absolute; left:0; right:0; top: calc(22% - 70px);
  text-align:center; z-index: 22;
  font-family: var(--font-heading-s); font-size: 48px; color: #fff;
  letter-spacing: 3px; opacity:0; pointer-events:none;
  text-shadow: var(--outline-4);
}
/* Long names (biome + cycle variant, e.g. "GALAXY — AUTUMN") shrink to fit */
.biome-banner[data-long]{
  font-size: min(32px, 8vw);
  letter-spacing: 2px;
  text-shadow: var(--outline-3);
  white-space: nowrap;
}
.biome-banner.on{ animation: banner 2s ease-out forwards }
@keyframes banner{
  0%{ opacity:0; transform: translateY(20px) scale(.7) }
  20%{ opacity:1; transform: translateY(0) scale(1) }
  80%{ opacity:1 }
  100%{ opacity:0; transform: translateY(-20px) scale(1) }
}

/* ===== END SCENE SYSTEM ===== */

/* ===== first-play story note ===== */
#firstPlayNote{
  margin: 10px auto 18px;
  max-width: 280px;
  font-family: Georgia, serif;
  font-style: italic;
  font-size: 12px;
  line-height: 1.6;
  color: #7a6f5d;
  opacity: .75;
  white-space: pre-line;
}
#firstPlayNote:empty{ display: none; }

.world{ position:absolute; inset:0; z-index:5; }

/* ===== BRICK (cube with 3 faces + studs) ===== */
.brick{
  position:absolute;
  --w: 86px;
  --h: 86px;
  --c: #ffb4d8;      /* main */
  --c-top: #ffd2e5;  /* top lighter */
  --c-side:#d88fae;  /* right darker */
  --c-dk:  #a56884;  /* bottom shade */
  width: var(--w); height: var(--h);
  filter: drop-shadow(0 6px 0 rgba(0,0,0,.22));
}
.brick .top{
  position:absolute; left:0; right:0; top:0; height: 22px;
  background: var(--c-top);
  clip-path: polygon(8% 0%, 92% 0%, 100% 100%, 0% 100%);
  box-shadow: inset 0 4px 0 rgba(255,255,255,.35);
}
.brick .front{
  position:absolute; left:0; right:0; top: 20px; bottom: 0;
  background: var(--c);
  box-shadow:
    inset 0 -10px 0 var(--c-dk),
    inset 0 3px 0 rgba(255,255,255,.15);
}
.brick .side{
  position:absolute; right:-10px; top: 8px; bottom: 8px;
  width: 16px;
  background: var(--c-side);
  clip-path: polygon(0 0, 100% 10%, 100% 90%, 0 100%);
}
/* studs on top — 2×2 grid of little bumps */
.brick .studs{
  position:absolute; top: 2px; left: 12%; right: 12%; height: 18px;
  display:grid; grid-template-columns: 1fr 1fr; gap: 8px;
  pointer-events:none;
}
.brick .studs span{
  background: var(--c-top);
  border-radius: 50%;
  box-shadow: inset 0 -3px 0 var(--c), 0 0 0 1.5px rgba(0,0,0,.18);
  height: 12px;
  transform: skewX(-2deg);
}

/* FRAGILE block — "Translucent Spirit" ghost brick.
   The brick itself is a semi-transparent cyan-white ghost with a soft blue
   glow halo and two floating eyes on the front. It reads distinctly from
   the biome bricks because nothing else in the game glows blue. Triggered
   = whole ghost blinks with a red distress glow. */
.brick.fragile{
  --c:      rgba(220,232,248,.55);
  --c-top:  rgba(240,248,255,.72);
  --c-side: rgba(150,175,210,.62);
  --c-dk:   rgba(90,120,160,.75);
  /* soft blue aura that gently pulses */
  filter: drop-shadow(0 0 8px rgba(150,200,255,.45));
  animation: ghostAura 1.6s ease-in-out infinite alternate;
}
/* glassy faces w/ a subtle blue inner stroke so edges read on any bg */
.brick.fragile .front{
  box-shadow:
    inset 0 -10px 0 var(--c-dk),
    inset 0 0 0 1.5px rgba(140,180,230,.9),
    inset 0 0 18px rgba(180,220,255,.55);
}
.brick.fragile .top{
  box-shadow: inset 0 0 0 1.5px rgba(140,180,230,.9);
}
.brick.fragile .side{
  box-shadow: inset 0 0 0 1.5px rgba(140,180,230,.8);
}
/* studs render translucent too */
.brick.fragile .studs span{
  background: rgba(200,220,245,.7);
  box-shadow: inset 0 -3px 0 rgba(100,130,170,.5);
}
/* floating ghost eyes — injected by makeBlock as <div class="eyes"> */
.brick.fragile .eyes{
  position:absolute;
  left:50%; top:50%;
  transform: translate(-50%, -50%);
  display:flex; gap: 16px;
  z-index: 4;
  pointer-events: none;
}
.brick.fragile .eyes span{
  width: 9px; height: 15px; border-radius: 50%;
  background: #1a2030;
  box-shadow: 0 0 6px rgba(140,200,255,.9);
}
@keyframes ghostAura{
  from{ filter: drop-shadow(0 0 6px rgba(150,200,255,.35)) }
  to  { filter: drop-shadow(0 0 14px rgba(180,220,255,.8))  }
}
/* triggered — panic mode: red glow + whole ghost blinks */
.brick.fragile.triggered{
  animation: ghostPanic 120ms steps(2,end) infinite;
}
@keyframes ghostPanic{
  0%, 100%{
    opacity: 1;
    filter: drop-shadow(0 0 10px rgba(255,120,120,.7));
  }
  50%{
    opacity: 0.35;
    filter: drop-shadow(0 0 22px rgba(255,80,80,1));
  }
}

/* SPIKE block — red-black with X pattern */
.brick.spike{
  --c:      #e13f3f;
  --c-top:  #ff6a5a;
  --c-side: #a82a2a;
  --c-dk:   #6b1b1b;
}
.brick.spike .front::after{
  content:""; position:absolute; inset: 8px 10px;
  background-image:
    repeating-linear-gradient(45deg,  rgba(0,0,0,.25) 0 6px, transparent 6px 14px),
    repeating-linear-gradient(-45deg, rgba(0,0,0,.25) 0 6px, transparent 6px 14px);
  opacity:.6;
}
/* warning pulse on top face just before spikes pop up */
.brick.spike.warning .top{
  animation: hazardPulse .4s ease-in-out infinite alternate;
}
@keyframes hazardPulse{
  from{ filter: brightness(1) }
  to  { filter: brightness(1.4) }
}

/* STAR LAUNCHER block — glossy midnight (navy-black) brick with a cool-gray blinking star.
   Whole brick pulses brightness in sync with the star icon's blink. */
.brick.star{
  --c:      #0f1a2e;
  --c-top:  #4a6a92;
  --c-side: #070d1a;
  --c-dk:   #000104;
  animation: starBrickPulse 0.35s steps(2,end) infinite;
}
@keyframes starBrickPulse{
  0%,100%{ filter: drop-shadow(0 6px 0 rgba(0,0,0,.22)) brightness(1); }
  50%    { filter: drop-shadow(0 6px 0 rgba(0,0,0,.22)) brightness(1.35); }
}
/* big star icon on the FRONT face — blinks in sync with brick pulse */
.brick.star .front::after{
  content:"★";
  position:absolute; left:50%; top:50%;
  transform: translate(-50%, -50%);
  font-size: 56px;
  line-height: 1;
  color: #9fb0c5;
  text-shadow:
    0 0 6px rgba(180,200,230,.6),
    0 0 14px rgba(120,160,210,.45);
  animation: starBlinkBig 0.35s steps(2,end) infinite;
  pointer-events:none;
  z-index: 5;
}
@keyframes starBlinkBig{
  0%, 100%{ opacity: 1; transform: translate(-50%,-50%) scale(1); }
  50%     { opacity: 0.15; transform: translate(-50%,-50%) scale(0.85); }
}

/* ========= STAR TRAP BLOCK =========
   Disguised as the old gold star launcher (v36 palette) to bait players.
   On land: locks tap (registry lockTapOnLand), rains 6 giant ★ from above,
   cat dies on impact (~700ms). Shares starBrickPulse + starBlinkBig keyframes
   with Black Star so the bait reads as identical at idle. */
.brick.star-trap{
  --c:      #ffcd3a;
  --c-top:  #ffe27a;
  --c-side: #c99517;
  --c-dk:   #7a5a0d;
  animation: starBrickPulse 0.35s steps(2,end) infinite;
}
.brick.star-trap .front::after{
  content:"★";
  position:absolute; left:50%; top:50%;
  transform: translate(-50%, -50%);
  font-size: 56px;
  line-height: 1;
  color: #6b4a00;
  text-shadow:
    0 0 6px rgba(255,240,160,.95),
    0 0 14px rgba(255,200,60,.8);
  animation: starBlinkBig 0.35s steps(2,end) infinite;
  pointer-events:none;
  z-index: 5;
}
/* armed state — applied on land via preTriggerClass invariant.
   Intensifies pulse (0.15s) + light shake to telegraph "oh no" moment. */
.brick.star-trap.trapping{
  animation: starBrickPulse 0.15s steps(2,end) infinite,
             brickShake 0.08s linear infinite;
}

/* Star rain container — injected by PRETRIGGER_FX.starRain on land.
   Extends well above and slightly outside the brick so stars can spawn
   off-screen top and fall onto the cat's head. z-index above brick/cat. */
.star-rain{
  position:absolute;
  left:-80px; right:-80px;
  top:-760px; bottom:0;
  pointer-events:none;
  z-index: 30;
  overflow: visible;
}
.star-rain span{
  position:absolute;
  top: 0;
  line-height: 1;
  color: #ffcd3a;
  text-shadow:
    0 0 10px rgba(255,205,58,.95),
    0 0 24px rgba(255,180,40,.7);
  filter: drop-shadow(0 4px 0 rgba(0,0,0,.28));
  opacity: 0;
  animation: starRainFall 620ms cubic-bezier(.5,0,.3,1) forwards;
}
/* 6 stars, staggered left/size/delay = "รัวๆ" feel */
.star-rain span:nth-child(1){ left: 10%; font-size: 96px; animation-delay:   0ms; }
.star-rain span:nth-child(2){ left: 42%; font-size: 74px; animation-delay:  85ms; }
.star-rain span:nth-child(3){ left: 68%; font-size: 88px; animation-delay: 170ms; }
.star-rain span:nth-child(4){ left: 24%; font-size: 70px; animation-delay: 250ms; }
.star-rain span:nth-child(5){ left: 78%; font-size: 82px; animation-delay: 330ms; }
.star-rain span:nth-child(6){ left: 50%; font-size:104px; animation-delay: 420ms; }
@keyframes starRainFall{
  0%   { transform: translateY(0)     rotate(-45deg); opacity: 0; }
  12%  { opacity: 1; }
  100% { transform: translateY(820px) rotate( 35deg); opacity: 1; }
}

/* impact flash — applied to cat via HAZARD_HANDLERS.starSmash right before die() */
@keyframes starImpactFlash{
  0%, 100%{ filter: none; }
  50%     { filter: brightness(2.3) drop-shadow(0 0 22px #fff); }
}

/* ========= FLIP BLOCK =========
   ดูเหมือนบล็อกสีม่วงปกติ แต่เอียงช้าๆแสดงว่าไม่มั่นคง.
   ลงแล้ว FLIP_DELAY ผ่าน → พลิก 180° → ตกกับบล็อก ถ้ายังอยู่. */
.brick.flip{
  --c:      #4d3d8a;
  --c-top:  #6f5bb0;
  --c-side: #332660;
  --c-dk:   #1e1546;
  transform-origin: center center;
  animation: flipWobble 2.2s ease-in-out infinite;
}
@keyframes flipWobble{
  0%, 100%{ transform: rotate(-12deg) }
  50%    { transform: rotate(12deg) }
}
.brick.flip.flipping{
  animation: flipFall .5s cubic-bezier(.4,.1,.7,1) forwards;
}
@keyframes flipFall{
  0%  { transform: rotate(0) translateY(0); opacity: 1 }
  60% { transform: rotate(180deg) translateY(40px); opacity: 1 }
  100%{ transform: rotate(360deg) translateY(260px); opacity: 0 }
}

/* ========= ELECTRIC BLOCK =========
   บล็อกสีเข้มดูไม่อันตราย แต่มีสายฟ้าผ่าแว่บเป็นจังหวะ.
   ถ้ายืน/ลงตอนฟ้าผ่า → ตาย */
.brick.electric{
  --c:      #1a1a3e;
  --c-top:  #2a2a5e;
  --c-side: #0a0a2e;
  --c-dk:   #050515;
}
.brick.electric .front{ overflow: hidden; }
.brick.electric .studs span{
  background: #3a3a7e;
  box-shadow: inset 0 -3px 0 #1a1a3e, 0 0 0 1.5px rgba(100,200,255,.3);
}
.brick.electric .bolt{
  position:absolute; left:50%; top:50%;
  transform: translate(-50%, -50%);
  width: 28px; height: 42px;
  pointer-events:none;
  z-index: 5;
  filter: drop-shadow(0 0 5px #5be8ff);
  animation: boltPulse 1.1s ease-in-out infinite;
}
.brick.electric .bolt svg{ width:100%; height:100%; display:block; }
@keyframes boltPulse{
  0%, 100%{ transform: translate(-50%,-50%) scale(1);    opacity: .85 }
  50%    { transform: translate(-50%,-50%) scale(1.22);  opacity: 1  }
}

/* ========= ELECTRIC DEATH STRIKE =========
   Big bolt from top of screen down onto the cat + screen flash. */
.electric-strike{
  position:absolute; top:0; z-index:17; pointer-events:none;
  width: 80px;
  transform: translateX(-50%);
  animation: strikeFlicker .45s steps(3,end);
}
.electric-strike svg{ width:100%; height:100%; display:block;
  filter: drop-shadow(0 0 8px #5be8ff) drop-shadow(0 0 16px rgba(255,255,255,.85));
}
@keyframes strikeFlicker{
  0%, 100%{ opacity: 1 }
  50%    { opacity: .25 }
}
.electric-flash{
  position:absolute; inset:0; z-index:16; pointer-events:none;
  background: rgba(200,240,255,.7);
  animation: flashFade .45s ease-out forwards;
}
@keyframes flashFade{
  0%{ opacity: 1 }
  100%{ opacity: 0 }
}
/* Mini upward bolt — fires when cat takes off from electric block.
   Positioned absolutely on the block, rising from top of block upward.
   Uses existing strikeFlicker keyframe for consistency. */
.electric-bolt-up{
  position:absolute; left:50%; bottom:100%;
  width:28px; height:80px;
  transform: translateX(-50%);
  animation: strikeFlicker .35s steps(3,end) forwards;
  pointer-events:none; z-index:18;
}
.electric-bolt-up svg{ width:100%; height:100%; display:block;
  filter: drop-shadow(0 0 6px #5be8ff) drop-shadow(0 0 12px rgba(255,255,255,.8));
}

/* RAINBOW block — rare safe haven (0.2% after GALAXY unlock).
   Standing on it is free: doesn't fall, doesn't break combo, no score.
   Prism gradient + soft aura. Charging FX on the cat is handled in the
   CAT section (.cat-wrap.rainbow-charging) via whileStandingClass field. */
.brick.rainbow{
  animation: rainbowAura 2.2s ease-in-out infinite alternate;
}
.brick.rainbow .front{
  background: linear-gradient(115deg,
    #ff5e8a 0%, #ffb347 20%, #ffe56b 40%,
    #7dd87d 60%, #6ec7ff 80%, #b084ff 100%);
  box-shadow:
    inset 0 -10px 0 rgba(0,0,0,.18),
    inset 0 3px 0 rgba(255,255,255,.3);
}
.brick.rainbow .top{
  background: linear-gradient(90deg,#ffcce0,#fff5cc,#d9f7cf,#d0e9ff,#e2d4ff);
}
.brick.rainbow .side{ background: #9a6ebf; }
.brick.rainbow .studs span{
  background: rgba(255,255,255,.85);
  box-shadow: inset 0 -3px 0 rgba(255,200,230,.5);
}
@keyframes rainbowAura{
  from{ filter: drop-shadow(0 6px 0 rgba(0,0,0,.22)) drop-shadow(0 0 8px rgba(255,255,255,.5)); }
  to  { filter: drop-shadow(0 6px 0 rgba(0,0,0,.22)) drop-shadow(0 0 20px rgba(255,255,255,1)); }
}

/* ================= SLIDE (Galaxy-only rhythm block) =================
   Rests at lane center (like normal block), slides outward toward own edge, returns.
   Driven by updateSlide() (JS): sets --slide-x CSS var + .paused class.
   Safe ONLY when paused at center (pink glow). */
.brick.slide{
  --c:      #e85cff;
  --c-top:  #f8b2ff;
  --c-side: #9c3fd0;
  --c-dk:   #601e87;
  --slide-x: 0px;
  transform: translateX(var(--slide-x));
  transition: filter 120ms ease;
  will-change: filter, transform;
}
.brick.slide .studs span{
  background: #ffdaff;
  box-shadow: inset 0 -3px 0 #e85cff, 0 0 0 1.5px rgba(120,30,160,.3);
}
/* .paused = resting at lane center — pink glow = tap window */
.brick.slide.paused{
  filter:
    drop-shadow(0 6px 0 rgba(0,0,0,.22))
    drop-shadow(0 0 18px rgba(255,140,240,.95));
}
/* .flipping = cat didn't tap — block flips upside down at edge.
   Preserves translateX(var(--slide-x)) so flip happens at edge position.
   Animation runs in parallel with die()'s cat-fall (both ~250ms).
   --flip-dir (= b.side) set at trigger time. */
.brick.slide.flipping{
  animation: slideFlip 0.25s ease-out forwards;
}
@keyframes slideFlip{
  0%   { transform: translateX(var(--slide-x)) rotate(0deg); }
  100% { transform: translateX(var(--slide-x)) rotate(calc(var(--flip-dir, 1) * 180deg)); }
}

/* Shaking block (stand too long) */
.brick.shaking{ animation: brickShake .12s linear infinite; }
@keyframes brickShake{
  0%  { transform: translate(0, 0) }
  25% { transform: translate(-2px, 1px) }
  50% { transform: translate(2px, -1px) }
  75% { transform: translate(-1px, 2px) }
  100%{ transform: translate(1px, -2px) }
}
.brick.falling{ animation: brickFall .6s ease-in forwards; }
@keyframes brickFall{
  0%  { transform: translateY(0) rotate(0); opacity:1 }
  100%{ transform: translateY(600px) rotate(25deg); opacity:0 }
}

/* Spike cap — appears on hazard when active */
.brick .spikes{
  position:absolute; left: 6px; right: 6px; top: -14px; height: 14px;
  transform: scaleY(0); opacity:0;
  transform-origin: 50% 100%;
  transition: transform .14s ease-out, opacity .14s;
  will-change: transform;
  background:
    linear-gradient(135deg, var(--danger) 50%, transparent 50%) 0 0/14px 14px repeat-x,
    linear-gradient(45deg,  var(--danger) 50%, transparent 50%) 0 0/14px 14px repeat-x;
  filter: drop-shadow(0 2px 0 #6b1b1b);
  display:none;                     /* only shown on spike bricks */
}
.brick.spike .spikes{ display:block; }
.brick.spikes-up .spikes{ transform: scaleY(1); opacity:1; }

/* ============================================================
   CAT — single-owner block
   Covers: visual parts · states (flipped/squash/dead/launching) ·
   skins (classic/miso/void) · animations · preview containers.
   See CAT.md for full spec. Do NOT add cat styles outside this block.
   ============================================================ */
.cat-wrap{
  --flip: 1;
  --accent-2: transparent;
  position:absolute; z-index:8;
  width: 86px; height: 110px;
  will-change: transform, left, top;
  animation: idleBump 1.55s steps(2,end) infinite;
}
.cat-wrap.flipped{ --flip: -1; }

/* cat glow trail during star launch — triggered by game via classList */
.cat-wrap.launching{ filter: drop-shadow(0 0 12px rgba(200,220,255,.9)) drop-shadow(0 0 22px rgba(120,160,220,.6)); }

/* rainbow charging — toggled by updateStandingFX() when cat stands on a rainbow
   block (via BLOCK_TYPES[type].whileStandingClass). Pastel sparks overlay on
   the cat, streaming upward through the body = "recharging" vibe. */
.cat-wrap.rainbow-charging{ position: relative; }
.cat-wrap.rainbow-charging::before{
  content: "";
  position: absolute;
  left: -14px; right: -14px;
  top: -22px; bottom: -6px;
  pointer-events: none;
  z-index: 20;
  background-image:
    radial-gradient(circle 2.8px at 12% 8%,  #ffb3e0 55%, transparent 75%),
    radial-gradient(circle 2.2px at 68% 18%, #b3d9ff 55%, transparent 75%),
    radial-gradient(circle 3px   at 38% 32%, #fff0b3 55%, transparent 75%),
    radial-gradient(circle 2.2px at 82% 44%, #d9b3ff 55%, transparent 75%),
    radial-gradient(circle 2.6px at 22% 58%, #b3ffcc 55%, transparent 75%),
    radial-gradient(circle 2px   at 56% 72%, #ffffff 55%, transparent 75%),
    radial-gradient(circle 2.4px at 88% 86%, #ffccb3 55%, transparent 75%),
    radial-gradient(circle 2.2px at 8%  94%, #ffffff 55%, transparent 75%),
    radial-gradient(circle 2px   at 48% 4%,  #cce5ff 55%, transparent 75%);
  background-size: 100% 62px;
  background-repeat: repeat-y;
  filter: drop-shadow(0 0 3px rgba(255,255,255,.85));
  animation: catChargeFlow 1.3s linear infinite;
  mix-blend-mode: screen;
  -webkit-mask-image: linear-gradient(to top, transparent 0%, #000 15%, #000 85%, transparent 100%);
          mask-image: linear-gradient(to top, transparent 0%, #000 15%, #000 85%, transparent 100%);
}
@keyframes catChargeFlow{
  from { background-position: 0 62px; }
  to   { background-position: 0 0; }
}

/* skin slots — default (no skin class) = classic; extra skins preserved for future use */
.cat-wrap.skin-miso{
  --ink:#221a38; --fur:#dfdee6; --fur-sd:#b7b5c2; --fur-hl:#f6f5fb;
  --patch:#8c90a0; --pink:#f39cb8; --accent-2:#787d90;
}
.cat-wrap.skin-void{
  --ink:#0f1017; --fur:#343642; --fur-sd:#1f2029; --fur-hl:#555867;
  --patch:#f0c36b; --pink:#f5a3bf; --accent-2:#d2d5e4;
}

.cat-body{ position:relative; width:100%; height:100%; transform: scaleX(var(--flip)); }

.cat-wrap .shadow{
  position:absolute; left:50%; bottom:-7px;
  transform: translateX(-50%);
  width: 76px; height: 14px;
  background: rgba(0,0,0,.22);
  border-radius: 999px;
  filter: blur(4px); opacity: .72; z-index:-1;
}
.cat-torso{
  position:absolute; left: 13px; top: 56px; width: 59px; height: 35px;
  background: var(--fur);
  border-radius: 9px 13px 11px 8px;
  transform: rotate(-2.5deg);
  animation: torsoStiff 2s steps(2,end) infinite;
  box-shadow:
    inset -6px 0 0 var(--fur-sd),
    inset 0 -6px 0 var(--fur-sd),
    inset 0 4px 0 var(--fur-hl);
}
.cat-torso::after{
  content:""; position:absolute; width:14px; height:8px; left:9px; top:8px;
  border-radius:999px;
  background: color-mix(in srgb, var(--patch) 78%, white 22%);
  opacity:.95;
}
.cat-wrap.skin-void .cat-torso::after{
  width:16px; height:10px; left:30px; top:16px;
  background: var(--accent-2); transform: rotate(-12deg);
}
.leg{
  position:absolute; top: 84px; width: 14px; height: 22px;
  background: var(--fur); border-radius: 4px 5px 6px 7px;
  box-shadow: inset -3px 0 0 var(--fur-sd), inset 0 -4px 0 var(--fur-sd);
}
.leg.fl{ left: 20px; transform: rotate(1deg); }
.leg.fr{ right: 19px; height: 21px; transform: rotate(-2deg); }
.leg.bl{ left: 17px; background: var(--fur-sd); height: 20px; }
.leg.br{ right: 18px; background: var(--fur-sd); }
.cat-head{
  position:absolute; left: 6px; top: 11px; width: 73px; height: 59px;
  background: var(--fur);
  border-radius: 13px 17px 12px 15px;
  transform: rotate(-1.8deg);
  animation: headBlank 2.1s steps(2,end) infinite;
  box-shadow:
    inset -8px 0 0 var(--fur-sd),
    inset 0 -6px 0 var(--fur-sd),
    inset 0 5px 0 var(--fur-hl);
}
.cat-head::after{
  content:""; position:absolute; top: 0; left: 27px; width: 23px; height: 13px;
  background: var(--patch); border-radius: 0 0 10px 12px;
  transform: rotate(3deg);
}
.cat-head::before{
  content:""; position:absolute; left:14px; top:11px; width:12px; height:7px;
  background: var(--accent-2); border-radius:999px; opacity:0;
}
.cat-wrap.skin-miso .cat-head::before{ opacity:.78; transform: rotate(-18deg); }
.cat-wrap.skin-void .cat-head::before{
  opacity:.92; left:48px; top:18px; width:10px; height:10px; border-radius:50%;
}
.ear{
  position:absolute; top: -8px; width: 22px; height: 22px;
  background: var(--fur);
  clip-path: polygon(0 100%, 100% 100%, 50% 0);
}
.ear.l{ left: 8px; transform: rotate(-11deg); }
.ear.r{ right: 11px; transform: rotate(10deg) scale(0.96); background: var(--patch); }
.ear::after{
  content:""; position:absolute; left: 5px; right: 5px; top: 8px; bottom: 2px;
  background: var(--pink);
  clip-path: polygon(0 100%, 100% 100%, 50% 0);
  opacity:.9;
}
.eye{
  position:absolute; top: 23px; width: 8px; height: 12px;
  background: var(--ink); border-radius: 48% 52% 46% 54%;
}
.eye.l{ left: 23px; transform: rotate(6deg); }
.eye.r{ right: 20px; top: 24px; height: 11px; transform: rotate(-3deg); }
.eye::after{
  content:""; position:absolute; top: 2px; left: 2px;
  width: 3px; height: 3px; background: rgba(255,255,255,.88); border-radius: 50%;
}
.cat-wrap.skin-void .eye::after{ opacity:.48; }
.nose{
  position:absolute; left: 49%; top: 41px; transform: translateX(-50%) rotate(-4deg);
  width: 6px; height: 5px; background: var(--pink); border-radius: 50%;
}
.mouth{
  position:absolute; left: 50%; top: 47px; transform: translateX(-50%);
  width: 16px; height: 2.5px;
  background: var(--ink); border-radius: 999px;
}
.mouth::before{
  content:""; position:absolute; left:-2px; top:-1px;
  width: 9px; height: 2.5px;
  background: var(--ink); border-radius: 999px; transform: rotate(2deg);
}
.mouth::after{
  content:""; position:absolute; right:-1px; top:1px;
  width: 8px; height: 2px;
  background: var(--ink); border-radius: 999px; transform: rotate(-5deg);
}

.tail{
  position:absolute; right: -5px; top: 57px;
  width: 14px; height: 58px;
  background: var(--fur);
  border-radius: 11px 8px 15px 13px;
  transform-origin: 50% 8px;
  animation: tailGlitch 1.25s linear infinite;
  z-index:-1;
  overflow:hidden;
  box-shadow: inset -4px 0 0 var(--fur-sd);
}
.tail::before, .tail::after{
  content:""; position:absolute; left:0; right:0;
  background: var(--fur-sd); height: 6px;
}
.tail::before{ top: 11px; }
.tail::after { top: 31px; }

/* idle (desync 1.55 / 2.0 / 2.1 + steps(2) = "confused student cat")
   NOTE: uses `translate` + `rotate` CSS properties (not `transform`) so
   game's inline `catEl.style.transform = rotate(...)` during death-fall
   still composes on top without being overridden by the active animation. */
@keyframes idleBump{
  0%, 100%{ translate: 0 0;    rotate: 0deg; }
  25%    { translate: 0 -1px;  rotate: -1deg; }
  50%    { translate: 0 0;     rotate: .5deg; }
  75%    { translate: 0 -1px;  rotate: -.4deg; }
}
@keyframes headBlank{
  0%, 100%{ transform: rotate(-1.8deg) translate(0,0); }
  40%    { transform: rotate(-3.2deg) translate(-1px, 1px); }
  55%    { transform: rotate(.2deg)   translate(0, 0); }
}
@keyframes torsoStiff{
  0%, 100%{ transform: rotate(-2.5deg) translateY(0); }
  50%    { transform: rotate(-.2deg)  translateY(1px); }
}
@keyframes tailGlitch{
  0%, 24%  { transform: rotate(15deg); }
  25%, 38% { transform: rotate(-110deg); }
  39%, 62% { transform: rotate(28deg); }
  63%, 78% { transform: rotate(210deg); }
  79%, 100%{ transform: rotate(-8deg); }
}
/* iOS WebKit bug: `animation:` shorthand with same @keyframes name in two
   comma-separated layers fails to run on iOS Safari (all iOS browsers use
   WebKit). Duplicate keyframe with a different name so the compound
   animation in `.cat-wrap.squash .tail` parses correctly across engines. */
@keyframes tailKick{
  0%, 24%  { transform: rotate(15deg); }
  25%, 38% { transform: rotate(-110deg); }
  39%, 62% { transform: rotate(28deg); }
  63%, 78% { transform: rotate(210deg); }
  79%, 100%{ transform: rotate(-8deg); }
}

/* squash on landing — body pop + tail fast-kick overlay.
   Tail uses 2 compound animations so when game keeps `.squash` class
   forever (no removal in game JS), base glitch keeps running and fast
   kick self-terminates after 1 cycle. */
.cat-wrap.squash .cat-body{ animation: squash .18s ease-out; }
.cat-wrap.squash .tail    {
  animation:
    tailGlitch 1.25s linear infinite,
    tailKick    .4s linear 1;
}
@keyframes squash{
  0%  { transform: scaleX(var(--flip)) scale(1, 1); }
  40% { transform: scaleX(var(--flip)) scale(1.14, .86); }
  100%{ transform: scaleX(var(--flip)) scale(1, 1); }
}

/* ===== DEAD CAT (game over panel) ===== */
/* Preview container dimensions (sized to fit cat 86×110) */
#catPreview{ width:86px; height:110px; margin:0 auto 18px; position:relative; }

/* Preview containers: the wrap is position:absolute by default for in-game
   placement; inside a preview/panel it should flow normally. */
#catPreview > .cat-wrap,
#catDead > .cat-wrap,
#rotateCat > .cat-wrap,
#htpCat > .cat-wrap{ position: relative; left: 0; top: 0; }

#catDead{
  width: 86px; height: 110px;
  margin: -10px auto -30px;
  position: relative;
}
#rotateCat{
  width: 86px; height: 72px;
  margin: 0 auto 2px;
  position: relative;
}
/* Game Over: cat tail tilted up 30° to bite into top whitespace of the
   layout box → reduces visual top gap toward panel padding standard.
   Rotate warning: cat upright + mirrored (tail on left), X eyes preserved. */
#catDead > .cat-wrap{
  transform: scale(0.62) rotate(60deg);
  transform-origin: center center;
}
#rotateCat > .cat-wrap{
  transform: scaleX(-0.62) scaleY(0.62);
  transform-origin: top center;
}
/* rotateCat keeps .dead's X eyes but body stays upright (no belly-up flip) */
#rotateCat .cat-wrap.dead .cat-body{
  transform: scaleX(var(--flip));
}
#rotateCat{ margin: 0 auto 14px; }
#catDead .shadow{ display: none; }
#catDead::after{
  content: ""; position: absolute;
  left: 50%; bottom: 25px;
  transform: translateX(-50%);
  width: 64px; height: 11px;
  background: rgba(0,0,0,.22);
  border-radius: 999px;
  filter: blur(4px); opacity: .72;
}
/* Upside-down body (belly up = "dead"), X eyes, faded shadow.
   scaleX(var(--flip)) preserved so dead+flipped combo doesn't break. */
.cat-wrap.dead .cat-body{ transform: scaleX(var(--flip)) rotate(180deg); }
.cat-wrap.dead .shadow{ opacity: .4; }
.cat-wrap.dead,
.cat-wrap.dead .cat-head,
.cat-wrap.dead .cat-torso,
.cat-wrap.dead .tail{ animation: none; }
.cat-wrap.dead .eye{
  background: transparent;
  border-radius: 0;
  width: 11px; height: 11px;
}
.cat-wrap.dead .eye::after{
  content: ""; position: absolute;
  top: 50%; left: 0;
  width: 100%; height: 2.5px;
  background: var(--ink); border-radius: 2px;
  transform: translateY(-50%) rotate(-45deg);
}
.cat-wrap.dead .eye::before{
  content: ""; position: absolute;
  top: 50%; left: 0;
  width: 100%; height: 2.5px;
  background: var(--ink); border-radius: 2px;
  transform: translateY(-50%) rotate(45deg);
}

/* ===== HUD — brick style ===== */
.hud{
  position:absolute; left:0; right:0; top:0; z-index:20;
  display:flex; justify-content:space-between; align-items:flex-start;
  padding: 14px 14px; pointer-events:none;
}
.hud-brick{
  position:relative; padding: 12px 18px 14px;
  font-family: var(--font-heading);
  color:#fff; font-weight:700; letter-spacing: 1px;
  box-shadow: var(--brick-shadow-rest);
  border-radius: 10px;
  line-height: 1;
  pointer-events: auto;  /* HUD bricks always capture clicks — never fall through to gameplay taps */
}
.hud-brick.score{
  background: var(--hud-1);
  font-size: 16px;
  letter-spacing: 1.5px;
  display:flex; align-items:baseline; gap:8px;
}
.hud-brick.score b{ font-size: 28px; color:#8ee8ff; letter-spacing: .5px; transition: font-size .15s ease; }
.hud-brick.score[data-len="5"] b{ font-size: 24px; }
.hud-brick.score[data-len="6"] b{ font-size: 20px; }
.hud-brick.score[data-len="7"] b{ font-size: 17px; }

/* RANK pill — tappable HUD entry to leaderboard */
.hud-brick.rank{
  background: var(--hud-2);
  color:#fff;
  display:flex; align-items:center; gap:6px;
  font-size: 15px;
  border: none;
  cursor: pointer;
  transition: transform .05s, box-shadow .05s;
  font-family: var(--font-heading);
  letter-spacing: 1px;
}
.hud-brick.rank:active{
  transform: translateY(3px);
  box-shadow: var(--brick-shadow-pressed);
}
.hud-brick.rank .trophy{ font-size: 16px; }
.hud-brick.rank b{ font-size: 20px; color:#fff; }

/* RANK pill has 2 views: rank (default) + best (during gameplay) */
/* Toggle via .showing-best class. Invariant: score===0 → rank, score>0 → best */
.hud-brick.rank .rank-view,
.hud-brick.rank .best-view{
  display:flex; align-items:center; gap:6px;
}
.hud-brick.rank .best-view{ display:none; }
.hud-brick.rank.showing-best .rank-view{ display:none; }
.hud-brick.rank.showing-best .best-view{ display:flex; }
.hud-brick.rank .best-label{
  font-size: 13px; letter-spacing: 1px; opacity: .85;
}

/* ===== LEADERBOARD overlay ===== */
#lbOverlay{ align-items: flex-start; padding-top: var(--lb-top, 96px); }
#lbOverlay .panel{
  min-width: 0; width: calc(100% - 28px); max-width: 420px;
  padding: 24px 28px 18px;
  max-height: 86%;
  display:flex; flex-direction:column;
}
.lb-close{
  position:absolute; top: 18px; right: 12px;
  width: 32px; height: 32px; border-radius: 50%;
  background: var(--hud-1); color: #fff;
  border: none; cursor: pointer;
  font-size: 16px;
  font-family: var(--font-heading-s);
  display:flex; align-items:center; justify-content:center;
  padding: 0; line-height: 1;
  box-shadow: var(--pill-shadow-rest);
  transition: transform .05s, box-shadow .05s;
}
.lb-close:active{
  transform: translateY(2px);
  box-shadow: var(--pill-shadow-pressed);
}
#lbOverlay .panel h1{
  font-size: 28px; margin: 2px 0 12px;
}
.lb-tabs{
  display:flex; gap: 6px; margin-bottom: 10px;
}
.lb-tab{
  flex: 1;
  font-family: var(--font-heading);
  font-size: 13px; letter-spacing: 1px;
  padding: 9px 6px;
  background: rgba(0,0,0,.06); color: #8a7f66;
  border: none; border-radius: 9px;
  cursor: pointer;
  transition: background .15s, color .15s;
}
.lb-tab.active{
  background: var(--hud-1); color: #fff;
  box-shadow: 0 3px 0 rgba(0,0,0,.25), inset 0 2px 0 rgba(255,255,255,.2);
}
.lb-list{
  flex: 1;
  overflow-y: auto;
  margin: 0 -4px;
  padding: 0 4px;
}
.lb-row{
  display:flex; align-items:center; gap: 8px;
  padding: 8px 10px;
  background: rgba(0,0,0,.04);
  border-radius: 8px;
  margin-bottom: 4px;
  font-size: 14px;
  font-family:"Fredoka", sans-serif; font-weight: 600;
  color: var(--hud-1);
  white-space: nowrap;
}
.lb-row .lb-rank{
  font-family: var(--font-heading-s); font-size: 15px;
  min-width: 30px; text-align:center;
  color: #8a7f66;
}
.lb-row .lb-flag{ font-size: 16px; }
.lb-row .lb-name{ flex: 1; overflow:hidden; text-overflow: ellipsis; }
.lb-row .lb-score{
  font-family: var(--font-heading-s); font-size: 14px;
  color: var(--hud-1); letter-spacing: .5px;
}
/* medal rows — medal icons replace rank number; no special background */
.lb-row .lb-medal{
  font-size: 18px;
  min-width: 30px; text-align:center;
  line-height: 1;
}
/* fixed "you/your country" row */
.lb-you-row{ margin-top: 10px; padding-top: 10px; border-top: 2px dashed #e5dac0; }
.lb-you-row .lb-row{
  background: var(--hud-2);
  color:#fff;
  margin-bottom: 0;
  box-shadow: 0 3px 0 #a04024, inset 0 2px 0 rgba(255,255,255,.25);
}
.lb-you-row .lb-row .lb-rank,
.lb-you-row .lb-row .lb-name,
.lb-you-row .lb-row .lb-score{ color:#fff; }

/* COMBO display = persistent state that grows with tier.
   1 element, reused. Pulses on every update, shakes at fire/god. */
.combo-display{
  position:absolute; left:50%;
  top: calc(40% - 85px);
  transform: translateX(-50%);
  font-family: var(--font-heading-s);
  color: var(--combo-ring, #ffdc4a);
  text-align:center; z-index:22; pointer-events:none;
  text-shadow: var(--outline-2), 0 5px 0 rgba(0,0,0,.3);
  letter-spacing: 1.5px;
  opacity: 0;
  font-size: 18px;
  transition: font-size .18s ease-out;
  white-space: nowrap;
}
.combo-display.on{ opacity: 1; }
.combo-display.tier-mid  { font-size: 22px; }
.combo-display.tier-high { font-size: 26px; }
.combo-display.tier-fire { font-size: 30px; color: #ff8c2e; }
.combo-display.tier-god  { font-size: 32px; color: #ff2d6f; }
.combo-display.pulse{ animation: comboPulse .14s ease-out; }
.combo-display.tier-fire.pulse,
.combo-display.tier-god.pulse{ animation: comboShake .14s ease-out; }
@keyframes comboPulse{
  0%  { transform: translateX(-50%) scale(1) }
  40% { transform: translateX(-50%) scale(1.18) }
  100%{ transform: translateX(-50%) scale(1) }
}
@keyframes comboShake{
  0%  { transform: translateX(-50%) scale(1) rotate(0) }
  25% { transform: translateX(-52%) scale(1.15) rotate(-3deg) }
  50% { transform: translateX(-48%) scale(1.15) rotate(3deg) }
  100%{ transform: translateX(-50%) scale(1) rotate(0) }
}

/* taps */
.taps{ position:absolute; inset:0; z-index:15; display:flex; touch-action:manipulation }
.btn{ touch-action:manipulation }
.taps > [data-dir]{ flex:1; }

/* ===== HOW TO PLAY tutorial (first-time overlay) ===== */
.htp-stage{
  position: relative;
  width: 100%;
  max-width: 260px;
  height: 220px;
  margin: 6px auto 8px;
}
.htp-brick{
  position: absolute; width: 54px; height: 44px;
  background: #d0b4ff;
  border-radius: 6px;
  box-shadow:
    inset 0 -8px 0 #a58bd4,
    inset 0 4px 0 #e5d3ff,
    0 4px 0 #7a63a3;
}
.htp-brick::before{
  content:""; position:absolute; top:-6px; left:10px; right:10px; height:8px;
  background: #e5d3ff; border-radius: 3px;
  box-shadow: inset 0 2px 0 rgba(255,255,255,.4);
}
.htp-brick.start        { left: 25%; bottom: 12px; transform: translateX(-50%); }
.htp-brick.target-above { left: 25%; top: 16px;   transform: translateX(-50%); }
.htp-brick.target-right { left: 75%; top: 16px;   transform: translateX(-50%); }

#htpCat{
  position: absolute;
  left: 25%; bottom: 50px;
  transform: translateX(-50%);
  width: 86px; height: 66px;
}
#htpCat > .cat-wrap{
  transform: scale(0.55);
  transform-origin: top center;
}

.htp-arrows{
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  pointer-events: none;
}

.htp-taps{
  display:flex; justify-content:space-between;
  padding: 0 4px;
  margin: 2px 0 14px;
  font-family: var(--font-heading-s);
  letter-spacing: 2px;
  font-size: 13px;
  color: var(--ink);
}
.htp-taps .pill{
  display:inline-flex; align-items:center; gap:6px;
  padding: 7px 13px;
  background: rgba(42,35,64,.08);
  border-radius: 20px;
}

/* overlays */
.overlay{
  position:absolute; inset:0; z-index:30;
  display:flex; align-items:center; justify-content:center;
  background: rgba(10,8,30,.45);
}
.overlay.hidden{ display:none }
.overlay.fading{ opacity: 0; transition: opacity .5s ease-out; pointer-events:none; }
.overlay.fading-in{ animation: overlayFadeIn .5s ease-out forwards; }
@keyframes overlayFadeIn { from { opacity: 0; } to { opacity: 1; } }
/* HOW TO PLAY → start screen transition: cat jumps off-screen to the right */
#htpCat.jumping{ animation: htpJumpOut .45s ease-in forwards; }
@keyframes htpJumpOut{
  0%   { transform: translateX(-50%) translateY(0); opacity: 1; }
  30%  { transform: translateX(10%) translateY(-55px); opacity: 1; }
  100% { transform: translateX(160%) translateY(20px); opacity: 0; }
}
.panel{
  background: #fff7e8; padding: 24px 28px 18px;
  border-radius: 16px; min-width: 320px; text-align:center;
  box-shadow: 0 10px 0 rgba(0,0,0,.3), inset 0 4px 0 rgba(255,255,255,.5), inset 0 -6px 0 rgba(0,0,0,.1);
}
.panel::after{
  content: 'kattyjump.com';
  display: block;
  width: fit-content;
  margin: 16px auto 0;
  padding-left: 31px;
  height: 25px; line-height: 25px;
  background: url('assets/brand-mark.png') no-repeat left center / 25px 25px;
  font-family: 'Fredoka', system-ui, sans-serif;
  font-weight: 700;
  font-size: 14px;
  color: var(--ink);
  letter-spacing: .04em;
}
.panel h1{
  font-family: var(--font-heading-s);
  font-size: 42px; margin: 4px 0 2px;
  color: var(--hud-1); letter-spacing: 1.5px;
}
.panel h1 span{ color: var(--hud-2); }
.panel .sub{
  font-family: var(--font-heading-s);
  font-size: 42px; letter-spacing: 1.5px;
  margin: 0 0 6px;
}
.panel .hint{
  font-size: 15px;
  line-height: 1.45;
  color: var(--ink);
  margin: 4px 0 8px;
}
.panel .sub .go-dark  { color: var(--hud-1); }
.panel .sub .go-orange{ color: var(--hud-2); }
.panel .big{
  font-family: var(--font-heading-s); font-size: 50px;
  color: var(--hud-1); line-height: 1;
}
.panel .big .u{ font-size:18px; color:#888; margin-left:4px }
.panel .row{
  display:flex; justify-content:space-between; align-items:center;
  padding: 8px 4px; gap: 12px;
  border-top: 2px dashed #e5dac0; font-size: 14px;
  white-space: nowrap;
}
.panel .row:first-of-type{ border-top:none; padding-top: 2px; }

/* DIED IN row — biome name auto-shrinks if it would overflow */
/* Invariant: flex-shrink + min-width:0 lets the value bend before the row breaks */
.panel .row.died-in-row{ min-width: 0; }
.panel .row.died-in-row > span{ flex-shrink: 0; }
.panel .row.died-in-row .died-in-value{
  min-width: 0;
  flex: 0 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: clamp(11px, 3.2vw, 14px);
  letter-spacing: .5px;
}

/* NAME row — tappable to edit player name */
.panel .row.name-row .name-value{
  cursor: pointer;
  user-select: none;
}
.panel .row.name-row .name-value::before{
  content: "✎";
  margin-right: 8px;
  color: var(--hud-2);
  font-size: 18px;
  font-weight: 800;
}
#finalName.locked{ pointer-events: none; cursor: default; }
#finalName.locked::before{ color: #bbb; }
.name-input{
  font-family:"Fredoka", sans-serif;
  font-size: 16px; font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  width: 130px;
  padding: 3px 8px;
  border: 2px solid var(--hud-1);
  border-radius: 6px;
  background: #fff;
  color: var(--ink);
  outline: none;
}

/* Name confirm/lock modal */
.panel.name-modal{ min-width: 240px; }
.panel.name-modal #nameModalBody{ font-size: 36px; margin: 4px 0 6px; }
.panel.name-modal #nameModalNote{ font-size: 12px; color:#888; }
.panel.name-modal #nameModalButtons{
  display:flex; gap:10px; justify-content:center; margin-top:16px;
}
.btn{
  font-family: var(--font-heading-s);
  padding: 16px 26px 10px;
  font-size: 19px; line-height: 1; letter-spacing:1px;
  border: none; border-radius: 10px; cursor:pointer;
  color: #fff; background: var(--hud-2);
  box-shadow: 0 5px 0 #a04024, inset 0 3px 0 rgba(255,255,255,.3);
  transition: transform .05s, box-shadow .05s;
  white-space: nowrap; min-width: 124px;
}
.btn:hover{ transform: translateY(1px); box-shadow: 0 4px 0 #a04024, inset 0 3px 0 rgba(255,255,255,.3); }
.btn:active{ transform: translateY(3px); box-shadow: 0 2px 0 #a04024 }
.btn.blue{ background: #4aa8ff; box-shadow: 0 5px 0 #2471bf, inset 0 3px 0 rgba(255,255,255,.3); }
.btn.blue:hover{ box-shadow: 0 4px 0 #2471bf, inset 0 3px 0 rgba(255,255,255,.3); }
.btn.blue:active{ box-shadow: 0 2px 0 #2471bf }

/* Start screen — primary CTA larger than standard .btn */
#startBtn{
  margin-top: 15px;
  padding: 22px 44px 14px;
  font-size: 26px;
  min-width: 180px;
}

/* milestone popup */
.milestone{
  position:absolute; top: calc(40% - 135px); left: 50%; transform: translateX(-50%);
  font-family: var(--font-heading-s);
  font-size: 40px; color: #fff;
  text-shadow: var(--outline-3), 0 8px 0 rgba(0,0,0,.3);
  z-index: 23; opacity: 0; pointer-events:none;
  letter-spacing: 2px;
  white-space: nowrap;
}
.milestone.on{ animation: msPop 1.3s ease-out forwards }
@keyframes msPop{
  0%{ opacity:0; transform: translate(-50%,30px) scale(.6) }
  20%{ opacity:1; transform: translate(-50%,0) scale(1.1) }
  30%{ transform: translate(-50%,0) scale(1) }
  80%{ opacity:1 }
  100%{ opacity:0; transform: translate(-50%,-27px) scale(1) }
}

/* ===== panel helpers — extracted from inline styles ===== */
.panel .tap-hint{
  margin-top:22px; font-size:13px; color:#a89c84;
  letter-spacing:2px; font-family: var(--font-heading-s);
}
.panel .score-label{
  font-size:14px; color:#888; letter-spacing:1.5px; margin-bottom:2px;
}

/* Death headline — block-specific taunt shown instead of dominant score.
   Toggled via .panel.taunt class by showGameOver() when state.deathHeadline is set.
   Registry-driven: any block with cfg.deathHeadline triggers this (currently starTrap). */
.panel .death-headline{
  display: none;
  font-family: var(--font-heading-s);
  font-size: 48px; line-height: 1.05;
  letter-spacing: 1px;
  text-align: center;
  margin: 6px 0 14px;
}
.panel .death-headline .dh-orange{ color: var(--hud-1); }
.panel .death-headline .dh-dark  { color: var(--hud-2); }
.panel.taunt .death-headline{ display: block; }
.panel.taunt .sub { display: none; }
.panel.taunt .score-label { font-size: 11px; margin-top: 6px; }
.panel.taunt .big > #finalScore{ font-size: 30px; }

.panel .big + .row{ margin-top:12px; }
.panel .btn-row{
  display:flex; gap:10px; justify-content:center; margin-top:16px;
}

/* particle burst on milestone/land */
.particle{ position:absolute; width:10px; height:10px; z-index:9; pointer-events:none; }

/* "วิ๊งๆ" sparkle on land — 4-point star above cat's head */
.sparkle{
  position:absolute; width:14px; height:14px; z-index:11; pointer-events:none;
  transform: translate(-50%,-50%);
  clip-path: polygon(50% 0%, 58% 42%, 100% 50%, 58% 58%, 50% 100%, 42% 58%, 0% 50%, 42% 42%);
  box-shadow: 0 0 8px #fff8c8, 0 0 14px rgba(255,236,120,.7);
}
/* ===== MUTE BUTTON ===== */
.hud-right{
  display: flex;
  align-items: center;
  gap: 8px;
  pointer-events: none; /* ลูกเป็น auto เอง */
}
.mute-btn{
  width: 44px; height: 44px;
  border: none; border-radius: 10px;
  background: #ffc6d9;
  box-shadow: var(--brick-shadow-rest);
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  padding: 0; transition: transform .05s, box-shadow .05s;
  -webkit-tap-highlight-color: transparent; user-select: none;
  pointer-events: auto;
  flex-shrink: 0;
}
.mute-btn:active{ box-shadow: var(--brick-shadow-pressed); transform: translateY(3px); }
.mute-btn.muted{ background: #c8c4d0; }
.mute-btn svg{ width: 22px; height: 22px; display: block; }
.mute-btn .icon-on, .mute-btn .icon-off{ display: none; }
.mute-btn:not(.muted) .icon-on{ display: block; }
.mute-btn.muted .icon-off{ display: block; }

/* ===== ROTATE WARNING (landscape lock) =====
   Uses the same #catDead pattern + Game Over panel styles.
   Overlay is position:fixed (outside .frame) so it covers the full viewport
   — frame is 9:16 and would clip the panel in landscape. */
#rotateOverlay{
  position: fixed;
  z-index: 100;
}
