/* ========================= Mermaid Tail — styles ========================= */

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* The two racers are colors the emoji set doesn't have, so we dye them ourselves.
   Both appear in the score bar, on the cards, in the banner and on the die — see
   .token--player / .token--goblin and the #die-who rules further down.
   Each one always has to be wrapped on its own, or the emoji standing next to it
   (🏰, 🎉, 😤, 🪸) gets dyed too. */
:root {
  /* 👺 is a red tengu mask; one spin round the wheel turns that red green. */
  --goblin-tint: hue-rotate(110deg) saturate(1.15);

  /* 🧜‍♀️ can't be done that way — her orange hair and green tail sit on opposite
     sides of the wheel, so no single spin makes both purple. Flatten her to one
     hue first, then dye that. Brightness survives it, so her tail still comes out
     deeper than her skin instead of one flat purple blob.
     The brightness step goes LAST and takes the finished purple down to a deep
     plum. It scales every channel by the same amount, so the light-and-dark of
     her still reads — she goes darker, not flatter. */
  --mermaid-tint: grayscale(1) sepia(1) hue-rotate(230deg) saturate(3) brightness(0.7);
}

.gob-emoji,
.mer-emoji { display: inline-block; }

.gob-emoji { filter: var(--goblin-tint); }
.mer-emoji { filter: var(--mermaid-tint); }

/* touch-action is the only thing that actually stops a phone zooming the page.
   The viewport meta says user-scalable=no, but iOS has ignored that for years.
   "manipulation" kills double-tap-to-zoom everywhere without killing panning,
   so the overlay cards can still be scrolled if they're taller than the screen —
   which is why this isn't the blunter touch-action: none the other games use. */
html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  touch-action: manipulation;
  font-family: "Baloo 2", system-ui, "Segoe UI", sans-serif;
  user-select: none;
  -webkit-user-select: none;
}

#game {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  background: radial-gradient(130% 90% at 50% 0%, #e2f8ff 0%, #a5e4f8 45%, #6cc4e8 100%);
  transition: background 0.5s ease;
}

/* Lets the portal ring animate smoothly between the ticks the clock writes.
   It has to inherit: the clock writes --p on the portal tile, but the ring that
   draws it is that tile's ::after. */
@property --p {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

/* ------------------------------- HUD -------------------------------- */

#hud {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(8px, 2.8vw, 26px);
  padding: clamp(5px, 1.6vw, 12px) clamp(8px, 2vw, 16px);
  flex-wrap: wrap;
}

.hud-item { flex: 0 0 auto; text-align: center; }

.stat-label {
  font-size: clamp(12px, 3vw, 18px);
  font-weight: 700;
  color: #12506b;
  white-space: nowrap;
}

.stat-label b { font-size: 1.2em; color: #0a6f9c; }
.stat.mine .stat-label b { color: #6a2fb5; }
.stat.theirs .stat-label b { color: #3f7a24; }

.stat-label b.bump { display: inline-block; animation: bump 0.3s ease; }
@keyframes bump {
  0% { transform: scale(1); }
  50% { transform: scale(1.55); }
  100% { transform: scale(1); }
}

/* ------------------------------ Stage -------------------------------- */

/* The portal is the one and only place you tap the BOARD instead of a button,
   and the game shouts at you to do it fast. Nothing here ever needs to be
   panned, pinched or double-tapped, so the whole stage refuses those outright —
   the overlay cards sit outside it and keep their scrolling. */
#stage {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: 0;
  touch-action: none;
}

/* The wiggly path. JS sets --cols / --rows / --tile-px and the pixel size. */
#track {
  --gap: clamp(2px, 0.8vmin, 9px);
  --tile-px: 30px;
  position: relative;
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: repeat(var(--cols, 9), 1fr);
  grid-template-rows: repeat(var(--rows, 9), 1fr);
  gap: var(--gap);
}

/* ------------------------------- Tiles -------------------------------- */

.tile {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  font-size: calc(var(--tile-px) * 0.5);
  border-radius: 30%;
  border: 2px solid rgba(255, 255, 255, 0.75);
  box-shadow: 0 2px 4px rgba(6, 60, 90, 0.16);
}

/* The mermaid's half of the ocean, and the goblin's swampy half. */
.side-m { background: linear-gradient(160deg, #d8f6ff, #a7e6fa); }
.side-g { background: linear-gradient(160deg, #e8f8d4, #c2e7a0); }
.side-c { background: linear-gradient(160deg, #fff3cf, #ffd98f); }

/* The little bars that join one tile to the next, so it reads as one ribbon. */
.link-r::before, .link-l::before, .link-d::before {
  content: "";
  position: absolute;
  background: rgba(255, 255, 255, 0.65);
}

.link-r::before, .link-l::before {
  top: 50%;
  height: 26%;
  width: calc(var(--gap) + 4px);
  transform: translateY(-50%);
}
.link-r::before { left: calc(100% - 2px); }
.link-l::before { right: calc(100% - 2px); }

.link-d::before {
  left: 50%;
  width: 26%;
  height: calc(var(--gap) + 4px);
  top: calc(100% - 2px);
  transform: translateX(-50%);
}

/* --- the two castles --- */

.castle {
  font-size: calc(var(--tile-px) * 0.74);
  border-width: 3px;
  z-index: 3;
  animation: castleGlow 2.4s ease-in-out infinite;
}

.castle-m {
  background: linear-gradient(160deg, #7b4fc4, #3d1a72);
  border-color: #b57bff;
}

.castle-g {
  background: linear-gradient(160deg, #e4ffc8, #a8dd6c);
  border-color: #5f9b30;
}

@keyframes castleGlow {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.85), 0 2px 4px rgba(6, 60, 90, 0.2); }
  50% { box-shadow: 0 0 0 6px rgba(255, 255, 255, 0), 0 2px 4px rgba(6, 60, 90, 0.2); }
}

/* --- the start shell in the middle --- */

.start {
  border-color: #ffab2e;
  font-size: calc(var(--tile-px) * 0.6);
  z-index: 2;
}

/* --- the magic portals --- */

.portal {
  background: radial-gradient(circle at 50% 45%, #f6e8ff, #c58bff 70%, #9b4dff);
  border-color: #7b2ff7;
  color: #fff;
  font-size: calc(var(--tile-px) * 0.62);
  font-weight: 800;
  text-shadow: 0 0 6px rgba(255, 255, 255, 0.9);
  z-index: 2;
  animation: portalSwirl 2.6s linear infinite;
}

@keyframes portalSwirl {
  0%, 100% { box-shadow: 0 0 0 0 rgba(155, 77, 255, 0.55), 0 2px 4px rgba(6, 60, 90, 0.2); }
  50% { box-shadow: 0 0 0 7px rgba(155, 77, 255, 0), 0 2px 4px rgba(6, 60, 90, 0.2); }
}

/* A portal that's already been used up — it goes quiet. */
.portal.used {
  background: linear-gradient(160deg, #e6e2ee, #c3bcd0);
  border-color: #9a92ab;
  color: #7a7189;
  text-shadow: none;
  animation: none;
}

/* You just landed on it — one big attention-grabbing ping. */
.tile.ping { animation: portalPing 0.7s ease-out; }
@keyframes portalPing {
  0% { transform: scale(1); }
  35% { transform: scale(1.5); }
  100% { transform: scale(1); }
}

/* --- the ten-second ring --- */

.tile.counting {
  z-index: 8;
  --ring-c: #37e0a0;
  transform: scale(1.25);
}

.tile.counting::after {
  content: "";
  position: absolute;
  inset: -22%;
  border-radius: 50%;
  transition: --p 0.1s linear;
  background: conic-gradient(from -90deg, var(--ring-c) calc(var(--p, 0) * 360deg), rgba(255, 255, 255, 0.3) 0);
  -webkit-mask: radial-gradient(closest-side, transparent 68%, #000 70%);
          mask: radial-gradient(closest-side, transparent 68%, #000 70%);
  filter: drop-shadow(0 0 4px rgba(0, 0, 0, 0.3));
}

.tile.counting.hurry {
  --ring-c: #ff2d63;
  animation: portalShake 0.28s linear infinite;
}

@keyframes portalShake {
  0%, 100% { transform: scale(1.25) translate(0, 0); }
  25% { transform: scale(1.3) translate(-2px, 1px); }
  75% { transform: scale(1.3) translate(2px, -1px); }
}

/* The real tap target — much bigger than the tile, because fingers. */
.portal-hit {
  position: absolute;
  border-radius: 50%;
  z-index: 20;
  cursor: pointer;
  touch-action: manipulation;
}

/* ------------------------------ Racers -------------------------------- */

/* The outer element only ever slides; the inner one only ever wiggles. */
.token {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 0;
  z-index: 10;
  pointer-events: none;
  will-change: transform;
}

.token--player { z-index: 11; }

.tok-inner {
  position: absolute;
  left: 0;
  top: 0;
  display: block;
  white-space: nowrap;
  line-height: 1;
  font-size: calc(var(--tile-px) * 0.95);
  filter: var(--tint, none) drop-shadow(0 3px 3px rgba(0, 40, 60, 0.4));
  transform: translate(calc(-50% + var(--nudge)), -50%);
}

.token.sad .tok-inner { filter: var(--tint, none) grayscale(0.7) drop-shadow(0 3px 3px rgba(0, 40, 60, 0.4)); }

/* The tokens on the board — dyed the same way as every other copy of the two. */
.token--player { --tint: var(--mermaid-tint); }
.token--goblin { --tint: var(--goblin-tint); }

@keyframes hop {
  0%, 100% { transform: translate(calc(-50% + var(--nudge)), -50%) scale(1); }
  45% { transform: translate(calc(-50% + var(--nudge)), -100%) scale(1.1, 0.9); }
}

@keyframes warpOut {
  0% { transform: translate(calc(-50% + var(--nudge)), -50%) scale(1) rotate(0deg); opacity: 1; }
  100% { transform: translate(calc(-50% + var(--nudge)), -50%) scale(0.05) rotate(540deg); opacity: 0.15; }
}

@keyframes warpIn {
  0% { transform: translate(calc(-50% + var(--nudge)), -50%) scale(0.05) rotate(-540deg); opacity: 0.15; }
  100% { transform: translate(calc(-50% + var(--nudge)), -50%) scale(1) rotate(0deg); opacity: 1; }
}

@keyframes dragged {
  0% { transform: translate(calc(-50% + var(--nudge)), -50%) rotate(0deg) scale(1); }
  50% { transform: translate(calc(-50% + var(--nudge)), -50%) rotate(-200deg) scale(0.75); }
  100% { transform: translate(calc(-50% + var(--nudge)), -50%) rotate(-360deg) scale(1); }
}

/* ------------------------------- Traps -------------------------------- */

/* The mermaid's half of the ocean is the infested one. Each kind gets its own
   color so you can see what you're about to blunder into. */
.trap {
  font-size: calc(var(--tile-px) * 0.6);
  border-color: #d24a2f;
  background: linear-gradient(160deg, #ffe3d2, #ffb08c);
  z-index: 2;
}

.trap--whirlpool { background: linear-gradient(160deg, #d9ecff, #86b8f0); border-color: #3f7fd0; }
.trap--seaweed   { background: linear-gradient(160deg, #dcf7cf, #86cf7a); border-color: #3f9c46; }
.trap--jellyfish { background: linear-gradient(160deg, #fbe0ff, #d79ff0); border-color: #9b4dd0; }
.trap--crab      { background: linear-gradient(160deg, #ffdcd6, #ff9d86); border-color: #d2452f; }
.trap--sand      { background: linear-gradient(160deg, #fff3d6, #f0d089); border-color: #c9a24a; }

/* Sprung: it's had its fun and goes limp. */
.trap.used {
  background: linear-gradient(160deg, #ece8ea, #cfc7cb);
  border-color: #a89ea3;
  filter: grayscale(1);
  opacity: 0.65;
}

.tile.sprung {
  animation: trapSpring 0.55s cubic-bezier(0.36, 1.5, 0.6, 1);
  z-index: 7;
}

@keyframes trapSpring {
  0% { transform: scale(1) rotate(0deg); }
  30% { transform: scale(1.45) rotate(-14deg); }
  60% { transform: scale(1.2) rotate(12deg); }
  100% { transform: scale(1) rotate(0deg); }
}

/* ---------------------------- Say banner ------------------------------ */

#say {
  flex: 0 0 auto;
  align-self: center;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 0;   /* the countdown digit brings its own margin — see #say-count */
  max-width: 94%;
  margin-bottom: clamp(3px, 1vw, 8px);
  padding: clamp(5px, 1.4vw, 10px) clamp(12px, 3vw, 22px);
  font-size: clamp(14px, 3.4vw, 21px);
  font-weight: 800;
  color: #0d4a66;
  background: rgba(255, 255, 255, 0.92);
  border: 3px solid #7fd7f2;
  border-radius: 99px;
  box-shadow: 0 5px 0 rgba(10, 90, 130, 0.18), 0 8px 16px rgba(0, 0, 0, 0.14);
  pointer-events: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

#say.pop { animation: sayPop 0.4s cubic-bezier(0.34, 1.6, 0.64, 1); }
@keyframes sayPop {
  0% { transform: scale(0.7); }
  60% { transform: scale(1.08); }
  100% { transform: scale(1); }
}

#say.urgent {
  color: #b0004a;
  border-color: #ff2d63;
  animation: sayUrgent 0.5s ease-in-out infinite;
}

@keyframes sayUrgent {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.05); }
}

/* The digit is half again the size of the banner text, so letting it pop into
   the flow and out again changes how tall #say is. #say is one row of the #game
   column, so every change steals height from #stage or hands it back — and the
   ResizeObserver watching #stage answers by re-folding and re-scaling the whole
   board. Twice per portal, at the exact moment you're staring at it.
   So the tall line is held open always and only the width collapses. */
#say-count {
  font-size: 1.5em;
  line-height: 1;
  min-height: 1em;   /* 1em of ITS OWN size: the tall line, reserved for good */
  font-weight: 800;
  color: #0a8f5f;
  min-width: 1.2em;
  text-align: center;
}

#say-count:empty { min-width: 0; }
#say-count:not(:empty) { margin-left: 10px; }

#say-count.hurry { color: #ff2d63; }

/* ------------------------------- The d20 ------------------------------- */

#dock {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(6px, 1.6vw, 14px) 10px calc(clamp(8px, 2vw, 16px) + env(safe-area-inset-bottom));
}

#die-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0;
  width: clamp(84px, 22vw, 122px);
  height: clamp(70px, 18vw, 100px);
  font-family: inherit;
  color: #fff;
  background: linear-gradient(180deg, #a86ce8, #6a2fb5);
  border: 4px solid #fff;
  border-radius: 26px;
  cursor: pointer;
  box-shadow: 0 8px 0 #45197d, 0 12px 20px rgba(0, 0, 0, 0.25);
  transition: transform 0.1s ease, box-shadow 0.1s ease, filter 0.25s ease;
  touch-action: manipulation;
}

#die-btn:active:not(:disabled) {
  transform: translateY(5px);
  box-shadow: 0 3px 0 #45197d, 0 6px 12px rgba(0, 0, 0, 0.25);
}

#die-btn.off {
  filter: saturate(0.4) brightness(0.92);
  cursor: default;
}

/* When it's the goblin's roll the die goes green — it's his turn, not yours. */
#die-btn.goblin-roll {
  background: linear-gradient(180deg, #a5d96a, #5f9b30);
  box-shadow: 0 8px 0 #3d6c18, 0 12px 20px rgba(0, 0, 0, 0.25);
}

#die-who {
  font-size: clamp(13px, 3.2vw, 18px);
  line-height: 1.1;
}

/* The little face above the number is whoever's rolling, so it's dyed to match. */
#die-btn:not(.goblin-roll) #die-who { filter: var(--mermaid-tint); }
#die-btn.goblin-roll #die-who { filter: var(--goblin-tint); }

#die-face {
  font-size: clamp(28px, 8vw, 44px);
  font-weight: 800;
  line-height: 1.05;
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.22);
}

#die-btn.rolling { animation: dieRoll 0.22s linear infinite; }
@keyframes dieRoll {
  0%, 100% { transform: rotate(-3deg) translateY(0); }
  50% { transform: rotate(3deg) translateY(-4px); }
}

#die-btn.landed #die-face { animation: dieLand 0.3s cubic-bezier(0.34, 1.7, 0.64, 1); }
@keyframes dieLand {
  0% { transform: scale(1.9); }
  100% { transform: scale(1); }
}

/* --------------------------- FX canvas ------------------------------ */

#fx {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  /* Above the overlays (60), so the win card's fireworks burst over the card and
     not behind it. It never swallows taps — pointer-events is off. */
  z-index: 70;
}

/* ---------------------------- Overlays ------------------------------ */

.overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(10, 60, 90, 0.45);
  backdrop-filter: blur(3px);
  overflow-y: auto;
}

.overlay.show { display: flex; }
.overlay.lose { background: rgba(35, 60, 20, 0.55); }
.overlay.win { background: rgba(46, 26, 82, 0.5); }

.card-box {
  background: #fff;
  border-radius: 28px;
  border: 6px solid #7fd7f2;
  box-shadow: 0 16px 0 rgba(10, 90, 130, 0.25), 0 24px 40px rgba(0, 0, 0, 0.3);
  padding: clamp(20px, 5vw, 40px);
  max-width: 460px;
  width: 100%;
  text-align: center;
  animation: cardIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes cardIn {
  from { transform: scale(0.6) translateY(30px); opacity: 0; }
}

.overlay.win .card-box { border-color: #a86ce8; }
.overlay.lose .card-box { border-color: #a8dd6c; }

.card-emoji {
  font-size: clamp(42px, 11vw, 66px);
  line-height: 1;
  margin-bottom: 6px;
}

/* The 🎉 on the win card rattles harder and harder on its fuse, then blows itself
   clean off the card. Keep this in step with popperFuseMs in the config — the
   real fireworks are fired the instant it vanishes. */
.overlay.win .popper {
  display: inline-block;
  animation: popperFuse 0.9s ease-in forwards;
}

@keyframes popperFuse {
  0%   { transform: rotate(0deg) scale(1); }
  20%  { transform: rotate(-6deg) scale(1.04); }
  35%  { transform: rotate(7deg) scale(1.06); }
  50%  { transform: rotate(-10deg) scale(1.1); }
  62%  { transform: rotate(11deg) scale(1.14); }
  74%  { transform: rotate(-13deg) scale(1.2); }
  84%  { transform: rotate(14deg) scale(1.26); }
  93%  { transform: rotate(-9deg) scale(1.4); opacity: 1; }
  100% { transform: rotate(6deg) scale(2.1); opacity: 0; }
}

.card-box h1 {
  margin: 6px 0 12px;
  font-size: clamp(27px, 7.4vw, 44px);
  font-weight: 800;
  color: #0a8ac0;
  letter-spacing: 1px;
}

.overlay.win .card-box h1 { color: #7b3fd0; }
.overlay.lose .card-box h1 { color: #4c7f21; }

.card-box p {
  margin: 8px 0;
  font-size: clamp(15px, 3.6vw, 19px);
  font-weight: 500;
  color: #24506a;
}

.card-box p.small { font-size: clamp(13px, 3vw, 16px); color: #6b8a9a; }

.card-box .new-best {
  font-weight: 800;
  color: #ff8a00;
  font-size: clamp(17px, 4.2vw, 22px);
}

.big-btn {
  margin-top: 18px;
  font-family: inherit;
  font-size: clamp(20px, 5vw, 28px);
  font-weight: 800;
  color: #fff;
  background: linear-gradient(180deg, #a86ce8, #6a2fb5);
  border: none;
  border-radius: 99px;
  padding: 14px 34px;
  cursor: pointer;
  box-shadow: 0 6px 0 #45197d, 0 10px 18px rgba(0, 0, 0, 0.25);
  transition: transform 0.1s ease, box-shadow 0.1s ease;
  touch-action: manipulation;
}

.big-btn:active {
  transform: translateY(4px);
  box-shadow: 0 2px 0 #45197d, 0 4px 10px rgba(0, 0, 0, 0.25);
}
