/* Patrick Hand by Patrick Wagesreiter — SIL Open Font License 1.1.
   Self-hosted (latin subset only, ~14 KB) so first paint is in the
   right font without a third-party round-trip. font-display:swap is
   safe here because the same-origin fetch resolves before paint. */
@font-face {
  font-family: 'Patrick Hand';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/patrick-hand.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Animatable CSS vars for transform composition */
@property --rot      { syntax: '<angle>';  inherits: false; initial-value: 0deg; }
@property --dx       { syntax: '<length>'; inherits: false; initial-value: 0px;  }
@property --dy       { syntax: '<length>'; inherits: false; initial-value: 0px;  }
@property --scale    { syntax: '<number>'; inherits: false; initial-value: 1;    }
@property --shake-x  { syntax: '<length>'; inherits: false; initial-value: 0px;  }
@property --fill     { syntax: '<percentage>'; inherits: false; initial-value: 0%; }
@property --bob      { syntax: '<length>'; inherits: false; initial-value: 0px; }
/* Additive rotation channel layered on top of --rot. Lets appear/pulse add
   a transient overshoot or settle without overriding cardIdle's --rot. */
@property --rot-extra { syntax: '<angle>';  inherits: false; initial-value: 0deg; }

:root {
  --bg: #fef3d8;
  --ink: #1a1a1a;
  --accent: #ff6a3d;
  --muted: #7a6f5a;
  --btn-bg: #ffffff;
  --text-stroke: 1px rgba(254, 243, 216, 0.7);
  --op-stroke: 1px rgba(254, 243, 216, 0.7);
  --c1: #ff9c8c;
  --c2: #7fc8a0;
  --c3: #8eb5e0;
  --c4: #f29ac3;
  --c5: #f0c84a;
  --c6: #b89ce6;
  --op-add: #f0c84a;
  --op-sub: #f29ac3;
  --op-mul: #7fc8a0;
  --op-div: #8eb5e0;
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0e0c0a;
    --ink: #f4ecd5;
    --accent: #ff8a5d;
    --muted: #9c8e6c;
    --btn-bg: #1f1c18;
    --text-stroke: 1.3px rgba(26, 26, 26, 0.7);
    --op-stroke: 1.3px rgba(26, 26, 26, 0.7);
    --c1: #ec7868;
    --c2: #5dba8a;
    --c3: #5fa0d6;
    --c4: #e07ab0;
    --c5: #dab93d;
    --c6: #a07ad8;
    --op-add: #dab93d;
    --op-sub: #e07ab0;
    --op-mul: #5dba8a;
    --op-div: #5fa0d6;
  }
}
:root[data-theme="light"] {
  --bg: #fef3d8; --ink: #1a1a1a; --accent: #ff6a3d; --muted: #7a6f5a; --btn-bg: #ffffff;
  --text-stroke: 1px rgba(254, 243, 216, 0.7);
  --op-stroke: 1px rgba(254, 243, 216, 0.7);
  --c1: #ff9c8c; --c2: #7fc8a0; --c3: #8eb5e0; --c4: #f29ac3; --c5: #f0c84a; --c6: #b89ce6;
  --op-add: #f0c84a; --op-sub: #f29ac3; --op-mul: #7fc8a0; --op-div: #8eb5e0;
}
:root[data-theme="dark"] {
  --bg: #0e0c0a; --ink: #f4ecd5; --accent: #ff8a5d; --muted: #9c8e6c; --btn-bg: #1f1c18;
  --text-stroke: 1.3px rgba(26, 26, 26, 0.7);
  --op-stroke: 1.3px rgba(26, 26, 26, 0.7);
  --c1: #ec7868; --c2: #5dba8a; --c3: #5fa0d6; --c4: #e07ab0; --c5: #dab93d; --c6: #a07ad8;
  --op-add: #dab93d; --op-sub: #e07ab0; --op-mul: #5dba8a; --op-div: #5fa0d6;
}

* {
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* View Transitions: theme toggle / screen change do a circular bubble
   reveal from the trigger. We disable the default cross-fade and stack
   old underneath new so the JS-driven clip-path animation is the only
   motion. */
::view-transition-old(root),
::view-transition-new(root) {
  animation: none;
  mix-blend-mode: normal;
}
::view-transition-old(root) { z-index: 1; }
::view-transition-new(root) { z-index: 2; }
/* Disable live-DOM transitions during the bubble reveal — see
   bubbleReveal() in main.js. Otherwise body/card/etc. background-color
   transitions run underneath the VT and flash at the swap moment. */
.vt-running, .vt-running *,
.vt-running *::before, .vt-running *::after {
  transition-duration: 0s !important;
}
html, body {
  margin: 0; padding: 0;
  background: var(--bg); color: var(--ink);
  font-family: "Patrick Hand", ui-rounded, "SF Pro Rounded", "Comic Sans MS", system-ui, sans-serif;
  overscroll-behavior: none;
  touch-action: manipulation;
  transition: background 0.25s ease, color 0.25s ease;
}
body {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex; flex-direction: column; align-items: center;
  padding: max(20px, env(safe-area-inset-top)) 16px max(20px, env(safe-area-inset-bottom));
  gap: 24px;
}

.theme-toggle {
  position: fixed;
  top: max(14px, env(safe-area-inset-top));
  right: 14px;
  width: 42px; height: 42px;
  border-radius: 50%;
  border: 2.5px solid var(--ink);
  background: var(--btn-bg);
  box-shadow: 3px 3px 0 var(--ink);
  cursor: pointer;
  color: var(--ink);
  display: flex; align-items: center; justify-content: center;
  z-index: 100;
  padding: 0;
  transition: transform 0.08s ease, box-shadow 0.08s ease, background 0.25s ease;
}
.theme-toggle:active { transform: translate(2px, 2px); box-shadow: 1px 1px 0 var(--ink); }
.theme-toggle svg { width: 20px; height: 20px; display: block; }
.theme-toggle .icon-moon { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: block; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .theme-toggle .icon-sun { display: none; }
  :root:not([data-theme]) .theme-toggle .icon-moon { display: block; }
}

.target {
  font-size: 18px; color: var(--muted); letter-spacing: 0.1em;
  text-align: center;
}
.target b {
  display: inline-block; font-size: 90px; color: var(--ink);
  font-weight: 400; margin-top: -8px;
  line-height: 1;
  transform: rotate(-2deg);
}

.board {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 18px;
  width: min(82vw, 340px);
  aspect-ratio: 1 / 1;
  touch-action: none;
  position: relative;
}
.card {
  /* --tilt is the persistent per-slot rotation set by JS; --rot is the
     animated value that cardIdle drives between --tilt and --tilt+amp.
     Same pattern as menu tiles. */
  --tilt: 0deg;
  --rot: var(--tilt); --dx: 0px; --dy: 0px; --scale: 1; --shake-x: 0px; --rot-extra: 0deg;
  /* Defined as a custom property so every override (selected, shake,
     pulse, etc.) can include cardIdle in its animation list without
     redefining the shorthand. This keeps the idle bob continuous —
     never replaced and restarted — across all interaction states. */
  --idle-anim: cardIdle var(--idle-dur, 3.8s) ease-in-out infinite var(--idle-delay, 0s);
  background: var(--bg-color, var(--c1));
  border: 3px solid var(--ink);
  border-radius: 18px;
  box-shadow: 4px 4px 0 var(--ink);
  display: flex; align-items: center; justify-content: center;
  font-size: 68px; font-weight: 400;
  color: var(--ink);
  -webkit-text-stroke: var(--text-stroke, 0);
  user-select: none; cursor: grab;
  touch-action: none;
  transform: translate(calc(var(--dx) + var(--shake-x)), var(--dy)) rotate(var(--rot)) rotate(var(--rot-extra)) scale(var(--scale));
  transition: box-shadow 0.12s ease, opacity 0.15s ease, background 0.25s ease;
  animation: var(--idle-anim);
}
.card.dragging {
  cursor: grabbing;
  box-shadow: 8px 10px 0 var(--ink);
  z-index: 10;
  --scale: 1.1;
  animation: var(--idle-anim), wobble 0.7s ease-in-out infinite;
}
.card.target-hover {
  box-shadow: 0 0 0 4px var(--accent), 4px 4px 0 var(--ink);
  --scale: 1.05;
  /* cardIdle keeps running so the bob doesn't snap when hover ends.
     The 1.05 scale + accent outline already give enough emphasis; the
     subtle continued wobble is fine. */
}
.card.selected {
  box-shadow: 0 0 0 4px var(--accent), 4px 4px 0 var(--ink);
  --scale: 1.06;
  animation: var(--idle-anim), selectedWobble 1s ease-in-out infinite;
}
@keyframes selectedWobble {
  0%, 100% { --rot: -2deg; }
  50%      { --rot:  2deg; }
}
.card.appear { animation: var(--idle-anim), appear 0.19s cubic-bezier(0.2, 0.9, 0.3, 1.3); }
.card.pulse { animation: var(--idle-anim), pulse 0.22s cubic-bezier(0.05, 0.9, 0.3, 1.3); }
.card.compressing { animation: var(--idle-anim), cardCompress 0.11s cubic-bezier(0.4, 0, 0.6, 1) forwards; }
.card .value {
  display: inline-block;
  transform-origin: center;
  paint-order: stroke fill;
  -webkit-text-stroke: 6px var(--bg);
}
.card.merging {
  pointer-events: none;
  transition: transform 0.11s cubic-bezier(0.4, 0, 0.7, 1),
              opacity 0.11s ease;
  z-index: 5;
}
.card.win {
  grid-column: 1 / 3 !important;
  grid-row: 1 / 3 !important;
  font-size: 110px;
  animation: winPulse 0.55s cubic-bezier(0.2, 0.9, 0.3, 1.4) forwards;
}
.card.win .value { -webkit-text-stroke-width: 8px; }
.card.win .frac  { font-size: 76px; -webkit-text-stroke-width: 6px; }

@keyframes wobble {
  0%   { --rot: -4deg; }
  50%  { --rot:  4deg; }
  100% { --rot: -4deg; }
}
/* Subtle bob between --tilt and --tilt + --idle-amp, with a small
   vertical float. Same shape as tileIdle — desync per card via the
   inline --idle-delay set in JS so all four don't bob in unison. */
@keyframes cardIdle {
  0%, 100% { --rot: var(--tilt, 0deg);
             --dy: 0px; }
  50%      { --rot: calc(var(--tilt, 0deg) + var(--idle-amp, 1.2deg));
             --dy: var(--idle-float, -3px); }
}
/* cardShake keyframes removed — shake is now driven by the Web
   Animations API in shakeEl() so it doesn't disturb cardIdle. The
   --shake-x @property declaration is still needed for the card's
   transform, even though no CSS keyframe animates it. */
/* Animates --scale and opacity only, so the base card transform keeps
   composing translate(--dx + --shake-x, --dy) rotate(--rot) underneath.
   Critical: writing transform directly here used to drop the translate
   segment, which pinned --dy at 0 during appear and then snapped to
   cardIdle's current --dy when the class ended — the "tiny jump" most
   visible on cards with large negative --idle-delay (top-right/bottom-
   left on new puzzle). */
@keyframes appear {
  0%   { --scale: 0.72; opacity: 0; }
  60%  { --scale: 1.05; opacity: 1; }
  100% { --scale: 1; }
}
/* "Pop" half of the inhale-and-pop merge: starts at scale 0.9 (matches
   the dst's compressed scale at the moment of swap — no visible "shrink
   smaller before growing" step) and pops with a quick overshoot. The
   front-loaded easing makes the pop register immediately, removing the
   perceived gap between merge-end and result-appearance. The +1deg
   overshoot lives on --rot-extra (additive) so cardIdle's --rot keeps
   flowing — see the appear comment above for why writing transform
   directly was the wrong shape. */
@keyframes pulse {
  0%   { --scale: 0.9;  --rot-extra: 0deg; }
  40%  { --scale: 1.11; --rot-extra: 1deg; }
  100% { --scale: 1;    --rot-extra: 0deg; }
}
/* "Inhale" half: dst card squeezes down gently while src is approaching. */
@keyframes cardCompress {
  0%   { --scale: 1; }
  100% { --scale: 0.9; }
}
@keyframes winPulse {
  0%   { transform: rotate(0deg) scale(0.55); opacity: 0; box-shadow: 4px 4px 0 var(--ink); }
  60%  { transform: rotate(-3deg) scale(1.04); opacity: 1; box-shadow: 0 0 0 5px var(--accent), 6px 6px 0 var(--ink); }
  100% { transform: rotate(-2deg) scale(1);   opacity: 1; box-shadow: 0 0 0 5px var(--accent), 6px 6px 0 var(--ink); }
}

.confetti {
  position: fixed; inset: 0;
  pointer-events: none;
  z-index: 50;
  overflow: hidden;
}
.confetti-piece {
  position: absolute;
  top: -20px;
  width: 9px; height: 13px;
  background: var(--c, var(--accent));
  border: 1.5px solid var(--ink);
  border-radius: 2px;
  animation: confettiFall var(--dur, 1.4s) cubic-bezier(0.35, 0.1, 0.55, 1) forwards;
  animation-delay: var(--delay, 0s);
  will-change: transform, opacity;
}
@keyframes confettiFall {
  0%   { transform: translate(0, 0) rotate(0deg); opacity: 1; }
  100% { transform: translate(var(--x, 0), 90vh) rotate(var(--r, 0deg)); opacity: 0.4; }
}

.card .frac {
  display: flex; flex-direction: column; align-items: center;
  line-height: 1; font-size: 46px;
  paint-order: stroke fill;
  -webkit-text-stroke: 4px var(--bg);
}
.card .frac .num { border-bottom: 3px solid currentColor; padding: 0 6px 4px; }
.card .frac .den { padding-top: 4px; }

.ops {
  display: flex; gap: 14px;
}
.ops.nudge { animation: opsNudge 0.42s ease; }
@keyframes opsNudge {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-7px) rotate(-1deg); }
  40% { transform: translateX(7px) rotate(1deg); }
  60% { transform: translateX(-5px) rotate(-0.5deg); }
  80% { transform: translateX(5px) rotate(0.5deg); }
}
.op {
  width: 72px; height: 72px;
  border-radius: 50%;
  border: 3px solid var(--ink);
  background: var(--op-color, #fff);
  color: var(--ink);
  paint-order: stroke fill;
  -webkit-text-stroke: 4px var(--bg);
  font-size: 44px;
  font-weight: 400;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: transform 0.1s cubic-bezier(0.2, 0.9, 0.3, 1.4),
              box-shadow 0.1s ease,
              background 0.25s ease;
  box-shadow: 3px 3px 0 var(--ink);
  font-family: inherit;
  line-height: 1;
  padding: 0 0 6px 0;
}
.op[data-op="+"] { --op-color: var(--op-add); }
.op[data-op="-"] { --op-color: var(--op-sub); }
.op[data-op="*"] { --op-color: var(--op-mul); }
.op[data-op="/"] { --op-color: var(--op-div); }
.op:not(.pop-on):not(.pop-off):active { transform: translate(2px, 2px); box-shadow: 1px 1px 0 var(--ink); }
.op.active {
  transform: scale(1.14) rotate(-5deg);
  box-shadow: 5px 5px 0 var(--ink);
}
.op.active:not(.pop-on):not(.pop-off):active { transform: scale(1.08) rotate(-5deg) translate(1px, 1px); box-shadow: 3px 3px 0 var(--ink); }

/* Tap feedback: squish-then-overshoot when activating, overshoot-then-squish when deselecting.
   Animation overrides the static transforms during play; lands cleanly on the resting state. */
.op.pop-on  { animation: opPopOn  0.24s cubic-bezier(0.4, 0, 0.2, 1); }
.op.pop-off { animation: opPopOff 0.24s cubic-bezier(0.4, 0, 0.2, 1); }
@keyframes opPopOn {
  0%   { transform: scale(1)    rotate(0deg); }
  18%  { transform: scale(0.86) rotate(0deg); }
  55%  { transform: scale(1.26) rotate(-7deg); }
  100% { transform: scale(1.14) rotate(-5deg); }
}
@keyframes opPopOff {
  0%   { transform: scale(1.14) rotate(-5deg); }
  20%  { transform: scale(1.22) rotate(-7deg); }
  55%  { transform: scale(0.88) rotate(3deg); }
  100% { transform: scale(1)    rotate(0deg); }
}

.controls {
  display: flex; gap: 10px; flex-wrap: wrap; justify-content: center;
}
.btn {
  padding: 8px 18px;
  border: 2.5px solid var(--ink);
  background: var(--btn-bg);
  color: var(--ink);
  border-radius: 999px;
  font-size: 18px;
  font-family: inherit;
  cursor: pointer;
  box-shadow: 2px 2px 0 var(--ink);
  transition: transform 0.08s ease, box-shadow 0.08s ease, background 0.25s ease;
}
.btn:active { transform: translate(2px, 2px); box-shadow: 0 0 0 var(--ink); }
.btn:disabled { opacity: 0.35; cursor: default; box-shadow: 2px 2px 0 var(--ink); transform: none; }
/* Long-press fill: --fill animates 0% → 100% over HOLD_MS to signal the reset trigger. */
.btn.holding {
  background: linear-gradient(to right, var(--accent) var(--fill), var(--btn-bg) var(--fill));
  animation: holdFill 0.8s linear forwards;
}
@keyframes holdFill {
  from { --fill: 0%; }
  to   { --fill: 100%; }
}

.status {
  min-height: 24px;
  font-size: 18px; color: var(--muted);
  text-align: center;
  transition: color 0.15s ease;
}
.status.win { color: var(--accent); font-size: 28px; transform: rotate(-1.5deg); }

.solution {
  font-family: "Patrick Hand", ui-monospace, "SF Mono", monospace;
  font-size: 16px;
  color: var(--muted);
  text-align: center;
  max-width: 340px;
  word-break: break-word;
  min-height: 18px;
}

/* Profile screen: three stacked stats in a row. Numbers picks up the
   wordmark / target treatment (big Patrick Hand, slight tilt) so it
   feels handwritten next to the other screens. */
.stats {
  display: flex;
  gap: 28px;
  justify-content: center;
  align-items: flex-start;
}
.stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-width: 64px;
}
.stat b {
  font-size: 64px;
  font-weight: 400;
  line-height: 1;
  color: var(--ink);
  transform: rotate(-2deg);
}
.stat:nth-child(2) b { transform: rotate(1.5deg); }
.stat:nth-child(3) b { transform: rotate(-1deg); }
.stat span {
  font-size: 14px;
  color: var(--muted);
  letter-spacing: 0.1em;
  text-align: center;
  white-space: nowrap;
}

/* ===== screens =====
   Only one .screen is shown at a time. The .active class controls
   visibility. The menu starts .active in HTML so it shows even without
   JS — JS just swaps the .active onto a different screen on navigation. */
.screen { display: none; }
.screen.active {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Fill the body's vertical space and center content. `safe center`
     falls back to top-alignment if content overflows (small phones in
     Safari mode), so we never push content off both ends. Without
     this the play/profile/settings screens were top-aligned and
     looked off-center in PWA mode where the viewport is taller. */
  justify-content: safe center;
  flex: 1;
  gap: 24px;
  width: 100%;
}
/* Only menu fills the screen and vertically centers — game keeps its
   natural top-aligned layout. Menu uses touch-action:none so Chrome
   iOS's pull-to-refresh / share-sheet gestures don't steal the drag. */
.screen-menu.active {
  flex: 1;
  gap: 32px;
  justify-content: center;
  touch-action: none;
}

/* Nav-back mirrors the theme toggle: same chunky pill, top-left. Hidden
   on the menu screen (nowhere to go back to). */
.nav-back {
  position: fixed;
  top: max(14px, env(safe-area-inset-top));
  left: 14px;
  width: 42px; height: 42px;
  border-radius: 50%;
  border: 2.5px solid var(--ink);
  background: var(--btn-bg);
  box-shadow: 3px 3px 0 var(--ink);
  cursor: pointer;
  color: var(--ink);
  display: flex; align-items: center; justify-content: center;
  z-index: 100;
  padding: 0;
  transition: transform 0.08s ease, box-shadow 0.08s ease, background 0.25s ease;
}
.nav-back:active { transform: translate(2px, 2px); box-shadow: 1px 1px 0 var(--ink); }
.nav-back svg { width: 18px; height: 18px; display: block; }
.nav-back.hidden { display: none; }

.wordmark {
  font-size: 72px;
  letter-spacing: 0.01em;
  line-height: 1;
  color: var(--ink);
  paint-order: stroke fill;
  -webkit-text-stroke: 6px var(--bg);
  transform: rotate(-2deg);
}
.tiles {
  display: grid;
  grid-template-columns: 1.08fr 0.92fr;
  grid-template-rows: 1.95fr 0.85fr;
  gap: 18px;
  width: min(84vw, 360px);
  aspect-ratio: 1 / 1.05;
}
.tile {
  --rot: var(--tilt, 0deg);
  --scale: 1;
  --dy: 0px;
  background: var(--bg-color, var(--c1));
  border: 3px solid var(--ink);
  border-radius: 18px;
  box-shadow: 4px 4px 0 var(--ink);
  display: flex; align-items: center; justify-content: center;
  font-family: inherit;
  color: var(--ink);
  padding: 0;
  transform: translateY(var(--dy)) rotate(var(--rot)) scale(var(--scale));
  animation: tileIdle var(--idle-dur, 3.5s) ease-in-out infinite var(--idle-delay, 0s);
  transition: box-shadow 0.15s ease, background 0.25s ease;
}
.tile.size-lg { font-size: 54px; }
.tile.size-sm { font-size: 34px; }
.tile.target-hover {
  box-shadow: 0 0 0 4px var(--accent), 4px 4px 0 var(--ink);
  --scale: 1.06;
}
.tile.pop {
  animation: tileIdle var(--idle-dur, 3.5s) ease-in-out infinite var(--idle-delay, 0s),
             tilePop  0.34s cubic-bezier(0.2, 0.9, 0.3, 1.4);
}
@keyframes tileIdle {
  0%, 100% { --rot: var(--tilt, 0deg);
             --dy: 0px; }
  50%      { --rot: calc(var(--tilt, 0deg) + var(--idle-amp, 1.5deg));
             --dy: var(--idle-float, -3px); }
}
@keyframes tilePop {
  0%   { --scale: 1; }
  28%  { --scale: 0.86; }
  60%  { --scale: 1.14; }
  100% { --scale: 1; }
}
.tile.size-lg .tile-label { -webkit-text-stroke-width: 5px; }
.tile.size-sm .tile-label { -webkit-text-stroke-width: 4px; }
.tile-label {
  display: inline-block;
  paint-order: stroke fill;
  -webkit-text-stroke: 4px var(--bg);
  letter-spacing: 0.01em;
}

.pet {
  --dx: 0px;
  --dy: 0px;
  --rot: 0deg;
  --scale: 1;
  --bob: 0px;
  /* Body-level fixed position so the pet keeps its DOM identity (and
     its animation phase) across menu↔play-menu transitions. The pet
     is visually persistent — only the surrounding tiles change.
     left:50% + margin-left:-half-width centers horizontally without
     touching transform (which is reserved for drag/idle composition). */
  position: fixed;
  bottom: calc(env(safe-area-inset-bottom) + 60px);
  left: 50%;
  margin-left: -41px;
  width: 82px; height: 82px;
  border-radius: 50%;
  border: 3px solid var(--ink);
  background: var(--c5);
  box-shadow: 4px 4px 0 var(--ink);
  color: var(--ink);
  padding: 0;
  cursor: grab;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  pointer-events: auto;
  z-index: 50;
  display: none; /* shown by data-screen rules below */
  align-items: center; justify-content: center;
  transform: translate(var(--dx), calc(var(--dy) + var(--bob))) rotate(var(--rot)) scale(var(--scale));
  animation: petIdle 2.6s ease-in-out infinite;
  transition: --dx 0.22s cubic-bezier(0.2, 0.9, 0.3, 1.4),
              --dy 0.22s cubic-bezier(0.2, 0.9, 0.3, 1.4),
              box-shadow 0.12s ease, background 0.25s ease;
  font-family: inherit;
}
/* Persistent pet + menu status are visible only on the two menu
   screens. data-screen is set on <html> by applyScreen in main.js. */
[data-screen="menu"] .pet,
[data-screen="play-menu"] .pet {
  display: flex;
}
#menuStatus {
  position: fixed;
  bottom: calc(env(safe-area-inset-bottom) + 16px);
  left: 0;
  right: 0;
  text-align: center;
  z-index: 49;
  display: none;
}
[data-screen="menu"] #menuStatus,
[data-screen="play-menu"] #menuStatus {
  display: block;
}
/* Menu screens need room at the bottom so the centered flex content
   doesn't overlap the fixed pet+status. Non-menu screens (play,
   profile, settings) don't show the pet so they keep their original
   padding from .screen.active. */
.screen-menu.active,
.screen-play-menu.active {
  padding-bottom: 150px;
}

/* Block all default touch gestures on the interactive game screens.
   Card and board already have touch-action: none, but iOS will still
   interpret a vertical drag that briefly crosses the gap between
   rows (or strays past the board edge) as a scroll / pull-to-refresh
   if the surrounding screen container's touch-action falls back to
   the body's "manipulation". This pins the policy at the screen
   level so vertical card drags can't be hijacked. The menu screen
   already does this (see .screen-menu.active above). */
.screen-play.active,
.screen-play-menu.active {
  touch-action: none;
}

/* Play sub-menu uses a "hero + alts" layout — Smash Bros home menu
   energy. random is the headline (big, full-width, top); easy / hard
   / 24 are alt selections in a row beneath. Grid-template-areas lets
   each tile pick its slot by data-mode so the HTML order is just
   "hero first, alts after" regardless of where they sit visually.
   The wider aspect-ratio gives the hero room to breathe. */
.screen-play-menu .tiles {
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 1.7fr 1fr;
  grid-template-areas:
    "random random random"
    "easy   hard   twentyfour";
  aspect-ratio: 1 / 1.15;
}
.screen-play-menu .tile[data-mode="random"]  { grid-area: random; }
.screen-play-menu .tile[data-mode="easy"]    { grid-area: easy; }
.screen-play-menu .tile[data-mode="hard"]    { grid-area: hard; }
.screen-play-menu .tile[data-mode="24mode"]  { grid-area: twentyfour; }

/* Play-menu tiles stack icon-above-label. Main-menu tiles don't have
   icons so they stay label-only via the base .tile flex centering. */
.screen-play-menu .tile {
  flex-direction: column;
  gap: 6px;
}
.screen-play-menu .tile.size-lg { gap: 12px; }
.tile-icon {
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  color: var(--ink);
  /* Cream disc behind the icon with a chunky ink outline — mirrors
     the .tile / .theme-toggle / .pet treatment (var(--bg) fill, dark
     ink stroke). The icon then reads as a small "badge" sitting on
     the tile. Padding gives the icon breathing room inside the disc
     so its stroke doesn't touch the ring. */
  background-color: var(--bg);
  border: 2.5px solid var(--ink);
  border-radius: 50%;
  padding: 8px;
  box-sizing: content-box;
}
.screen-play-menu .tile.size-lg .tile-icon {
  width: 88px;
  height: 88px;
  padding: 14px;
  stroke-width: 2;
}
/* Face is hardcoded dark so it reads on the yellow pet in BOTH themes
   (cream --ink in dark mode disappears against yellow). */
.pet-face { width: 72%; height: 72%; display: block; color: #1a1a1a; }
.eye { fill: currentColor; }
.mouth-closed {
  stroke: currentColor; fill: none;
  transition: opacity 0.12s ease;
}
.mouth-open {
  fill: currentColor;
  opacity: 0;
  transition: opacity 0.12s ease;
}
.pet.dragging {
  cursor: grabbing;
  box-shadow: 8px 10px 0 var(--ink);
  --scale: 1.12;
  animation: petWobble 0.55s ease-in-out infinite;
  transition: box-shadow 0.12s ease, background 0.25s ease;
}
.pet.near-target .mouth-closed { opacity: 0; }
.pet.near-target .mouth-open   { opacity: 1; }
@keyframes petIdle {
  0%, 100% { --rot: -3deg; --bob: 0px; }
  50%      { --rot:  3deg; --bob: -3px; }
}
@keyframes petWobble {
  0%, 100% { --rot: -7deg; }
  50%      { --rot:  7deg; }
}
