/* Tilewatch — twelve coasts, one tile wall.
   UI chrome, buttons and cards come from ../shared/shell.css and its --g-* vars.
   The backdrop and the top bar sit over representational art (sky, water, a city
   at night), so they use literal colors the way the tile ivory does, not theme
   variables: the ink has to stay readable on a dark scene in either theme. */
:root{
  --tw-water: 7%;
  --tw-poll: .28;
  --tw-scenery: 1;   /* 0..1 how well the dam is holding, eased over time */
  --tw-lit: 1;       /* share of the town that still has power */
}

.tw-wrap{
  /* The stage lays children out in a row with align-items:flex-start, and every
     child of the wrap is absolutely positioned, so the wrap gets its height
     from here — not from its content. */
  position: relative; width: 100%;
  height: min(92vh, 1000px); min-height: 480px;
}
/* The stage normally caps its children at 760px; Frostveil wants the wide
   seawall, but an uncapped wrap blows up on large monitors (a 2560 screen made
   each tile 136x180px). Cap it at a sane game width and center it. Full screen
   drops every cap and the padding so the player gets the whole monitor edge to
   edge — the floating toolbar overlays the top. */
.mj-game .mj-stage > .tw-wrap{ max-width: 1500px; }
.mj-game.is-fullscreen .mj-stage{ padding: 0; }
.mj-game.is-fullscreen .mj-stage > .tw-wrap{ max-width: none; width: 100%; height: 100%; min-height: 0; }
.mj-game.is-fullscreen .tw-scene{ border-radius: 0; }

/* ---------------- backdrop ---------------- */
.tw-scene{ position: absolute; inset: 0; z-index: 0; overflow: hidden; border-radius: 14px; }

/* Night over water: deep indigo overhead, city glow on the horizon. */
.tw-sky{ position: absolute; inset: -3%;
  background: linear-gradient(180deg,
    var(--tw-sky-top,#080f26) 0%,
    var(--tw-sky-mid,#24456b) 68%,
    var(--tw-sky-bot,#2d2b26) 100%);
  background-size: 100% 135%;
  transform: scale(1.08); will-change: transform;
  animation: tw-skybreath 24s ease-in-out infinite alternate; }
/* The sky breathes with a cheap GPU-composited scale instead of a repainted
   gradient or a filter, so it stays alive without costing frames. */
@keyframes tw-skybreath{
  0%   { transform: scale(1.05); }
  100% { transform: scale(1.11); } }
/* A soft, slow-moving curtain of green and blue light — clean-air aurora over the
   coast. It drifts and pulses so the upper sky feels alive, never a flat fill.
   The per-coast --tw-aurora-a sets how strong the curtain reads: faint over the
   tropical coasts, near a full northern display up by New York and Cape Cod. */
.tw-aurora{ position: absolute; inset: -12%; pointer-events: none;
  filter: blur(8px);
  background:
    radial-gradient(80% 36% at 22% 44%, rgba(63,207,142, var(--tw-aurora-a,.12)), transparent 70%),
    radial-gradient(90% 40% at 56% 30%, rgba(86,170,255, var(--tw-aurora-a,.12)), transparent 72%),
    radial-gradient(82% 34% at 84% 54%, rgba(120,255,210, calc(var(--tw-aurora-a,.10) * .85)), transparent 70%);
  will-change: transform;
  animation: tw-aurora 28s ease-in-out infinite alternate; }
/* The aurora drifts as a GPU-composited transform; the blur is halved and the
   shapes are stretched into soft horizontal ribbons so it stays alive without
   repainting the whole sky each frame. */
@keyframes tw-aurora{
  0%   { transform: translate3d(-6%, -3%, 0); opacity: .22; }
  50%  { transform: translate3d(7%, 1%, 0);  opacity: .48; }
  100% { transform: translate3d(3%, 6%, 0);  opacity: .30; } }
/* Town glow on the horizon. Its color is the coast's own --tw-glow, so a kept
   coast reads warm coral in Miami, teal at Key West, electric blue under the
   New York towers. It fades as the wall fails, so a losing coast goes cold. */
.tw-warm{ position: absolute; inset: auto 0 0 0; height: 62%; pointer-events: none;
  opacity: clamp(0, (var(--tw-scenery, 1) - .5) * 1.6, 1); transition: opacity .8s linear;
  background: radial-gradient(120% 82% at 50% 100%, var(--tw-glow, rgba(255,178,96,.34)), transparent 76%); }
/* A moon per coast: warm and low over the tropics, pale and high up north.
   Foggy and stormy coasts simply hide it (--tw-moon-op: 0). */
.tw-moon{ position: absolute; border-radius: 50%; pointer-events: none; z-index: 1;
  left: var(--tw-moon-x, 70%); top: var(--tw-moon-y, 15%);
  width: var(--tw-moon-size, 56px); height: var(--tw-moon-size, 56px);
  background: radial-gradient(circle at 38% 36%,
    rgb(var(--tw-moon-tint, 255,255,240)) 0%,
    rgb(var(--tw-moon-tint, 255,255,240)) 58%,
    rgba(var(--tw-moon-tint, 255,255,240), 0) 74%);
  box-shadow: 0 0 46px 14px rgba(var(--tw-moon-halo, 255,240,210), .32), inset -7px -7px 16px rgba(0,0,0,.28);
  opacity: var(--tw-moon-op, 1); transition: opacity 1.1s linear; }
.tw-haze{ position: absolute; inset: 0; opacity: var(--tw-poll); transition: opacity .9s linear;
  background: radial-gradient(130% 70% at 50% 96%, rgba(120,170,220,.50), rgba(80,120,170,.16) 46%, transparent 72%); }
.tw-stars{ position: absolute; inset: 0 0 45% 0; opacity: calc(.6 - var(--tw-poll) * .6); transition: opacity .9s; }
.tw-stars i{ position: absolute; background:#fff; border-radius: 50%;
  animation: tw-twinkle 3.4s ease-in-out infinite, tw-stardrift 46s ease-in-out infinite alternate; }
@keyframes tw-twinkle{ 0%,100%{opacity:.2} 50%{opacity:.9} }
@keyframes tw-stardrift{ 0%{ transform: translate3d(-5px,0,0); } 100%{ transform: translate3d(5px,0,0); } }

/* Night skies only: an occasional meteor streaks across. Daylight hides the
   layer, so nothing fires while the sun is up. Air-pollution haze (--tw-poll)
   is deliberately not applied here — a shooting star should still read even
   when the coast is taking damage. */
.tw-meteors{ position: absolute; inset: 0 0 45% 0; opacity: 1; pointer-events: none;
  transition: opacity .8s ease; }
.tw-meteor{ position: absolute; width: 76px; height: 2px; border-radius: 2px; opacity: 0;
  transform-origin: 0 50%;
  background: linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(214,236,255,.5) 55%, #fff 100%);
  box-shadow: 0 0 6px rgba(190,225,255,.75);
  animation: tw-meteor 1.15s ease-in forwards; }
@keyframes tw-meteor{
  0%   { opacity: 0; transform: rotate(var(--ma, 24deg)) translateX(0) scaleX(.2); }
  16%  { opacity: 1; }
  72%  { opacity: 1; }
  100% { opacity: 0; transform: rotate(var(--ma, 24deg)) translateX(var(--md, 300px)) scaleX(1); }
}

.tw-clouds{ position: absolute; inset: 0; }
.tw-clouds i{ position: absolute; height: 96px; border-radius: 50%;
  background: radial-gradient(ellipse at 50% 50%, rgba(186,206,230,.22), rgba(186,206,230,0) 70%);
  animation: tw-drift linear infinite; }
.tw-clouds .c1{ width: 260px; top: 14%; animation-duration: 120s; }
.tw-clouds .c2{ width: 180px; top: 26%; animation-duration: 88s; animation-delay: -30s; background: rgba(186,206,230,.11); }
.tw-clouds .c3{ width: 320px; top: 5%;  animation-duration: 150s; animation-delay: -70s; background: rgba(186,206,230,.09); }
@keyframes tw-drift{ from{ transform: translateX(-40vw) } to{ transform: translateX(115vw) } }

/* Per-coast weather: fog is a soft drifting layer over the skyline; snow and
   rain are drawn on the canvas. The sky palette also shifts with the weather. */
.tw-fog{ position: absolute; inset: 0; opacity: 0; pointer-events: none; transition: opacity 1.2s linear;
  background:
    radial-gradient(58% 42% at 30% 28%, rgba(214,226,238,.40), rgba(214,226,238,0) 70%),
    radial-gradient(66% 50% at 72% 22%, rgba(200,214,228,.34), rgba(200,214,228,0) 72%),
    linear-gradient(180deg, rgba(206,218,230,.16), rgba(206,218,230,0) 62%); }
.tw-fog::before, .tw-fog::after{ content: ""; position: absolute; top: -10%; height: 120%; width: 62%;
  background: radial-gradient(closest-side, rgba(224,234,244,.5), rgba(224,234,244,0));
  filter: blur(7px); animation: tw-fogdrift linear infinite; }
.tw-fog::before{ left: 4%; animation-duration: 46s; }
.tw-fog::after{ left: 46%; animation-duration: 62s; animation-delay: -20s; }
@keyframes tw-fogdrift{ 0%{ transform: translateX(-12%); } 50%{ transform: translateX(12%); } 100%{ transform: translateX(-12%); } }
.wx-fog .tw-fog{ opacity: 1; }

/* The city: three depths, so the air between them reads as distance. */
.tw-citysvg{ position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
/* The far skyline washes out as the dam fails, so depth goes first. */
.tw-far{ fill: #22374f; opacity: calc(.16 + var(--tw-scenery, 1) * .34); transition: opacity .6s linear; }
.tw-mid{ fill: #0a121c; }
.tw-palms{ stroke: #0a121c; stroke-width: 4; fill: none; stroke-linecap: round; }
.tw-win-layer{ fill: #ffd9a0; opacity: .92; }
/* Every lit window carries the brightness it was built with (--wo) and the order
   in which it gives up (--wt). --tw-lit is the share of the town still on the
   grid, driven by the wall's health, so the block dims one window at a time as
   the dam fails and slowly lights back up as it is mended. */
.tw-win-layer .w-lit{
  --wl: clamp(0, (var(--tw-lit, 1) - var(--wt, 0)) * 12, 1);
  opacity: calc(var(--wo, 1) * var(--wl));
}
.tw-win-dark{ fill: #141d28; opacity: 1; }
/* A handful of windows breathe: their light sinks slowly, then comes back.
   Durations and delays are randomised per window so the block never pulses
   in unison. The <use> that draws the drowned copy clones these classes, so
   a submerged window keeps blinking in step. */
.tw-win-layer .w-slow{ animation: tw-window 9s ease-in-out infinite; }
@keyframes tw-window{
  0%, 68%   { opacity: calc(var(--wo, .85) * var(--wl, 1)); }
  82%       { opacity: calc(.07 * var(--wl, 1)); }
  94%, 100% { opacity: calc(var(--wo, .85) * var(--wl, 1)); }
}

/* The freeze: a translucent ice sheet, so the wall and the drowned floors read through it. */
.tw-sea{ position: absolute; left: 0; right: 0; bottom: 0; z-index: 3; pointer-events: none;
  height: var(--tw-water); transition: height .22s linear;
  background: linear-gradient(180deg, rgba(214,240,255,.45) 0%, rgba(120,190,230,.66) 40%, rgba(40,98,140,.90) 100%); }
.tw-sea::after{ content:""; position: absolute; left:0; right:0; top:0; height:2px;
  background: linear-gradient(90deg, rgba(210,245,255,.15), rgba(210,245,255,.75), rgba(210,245,255,.15)); }
.tw-crest{ position:absolute; top:-12px; left:0; width:100%; height:24px; opacity:.55;
  background: radial-gradient(closest-side, rgba(205,242,255,.8), rgba(205,242,255,0));
  animation: tw-swell 4.6s ease-in-out infinite; }
@keyframes tw-swell{ 0%,100%{transform:translateY(0) scaleY(1)} 50%{transform:translateY(-6px) scaleY(1.5)} }

/* Street life behind the wall: cars and people on the avenue, gone when the
   water finally comes over the top. */
.tw-street{ position: absolute; left:0; right:0; z-index: 1; height: 34px;
  transition: opacity .7s linear; }
.tw-street.is-flooded{ opacity: 0; }
.tw-street::before{ content:""; position:absolute; left:0; right:0; bottom:0; height:2px;
  background: rgba(226,232,240,.18); }
.tw-car{ position:absolute; bottom:6px; width:24px; height:8px; border-radius:3px;
  background:#eee7d6; box-shadow:0 0 7px rgba(255,226,160,.5), 0 1px 4px rgba(0,0,0,.6);
  animation: tw-drive 17s linear infinite; }
.tw-car.b{ background:#c9d3dd; animation-duration:22s; animation-direction:reverse; bottom:18px; }
.tw-car.c{ background:#e0bd8c; animation-duration:27s; animation-delay:-7s; bottom:6px; }
@keyframes tw-drive{ from{ transform: translateX(-8vw) } to{ transform: translateX(108vw) } }
.tw-person{ position:absolute; bottom:4px; width:3px; height:9px; border-radius:2px;
  background:#f6ecd8; opacity:.9; animation: tw-walk 28s linear infinite; }
.tw-person.p2{ animation-duration:36s; animation-delay:-9s; bottom:17px; }
.tw-person.p3{ animation-duration:31s; animation-delay:-17s; }
.tw-person.p4{ animation-duration:40s; animation-delay:-25s; bottom:17px; }
@keyframes tw-walk{ from{ transform: translateX(-5vw) } to{ transform: translateX(105vw) } }

/* ---------------- readouts ---------------- */
.tw-hud{ position: absolute; top:0; left:0; right:0; z-index: 6; padding: 8px 11px 7px;
  background: linear-gradient(180deg, rgba(5,11,19,.86) 0%, rgba(5,11,19,.55) 62%, rgba(5,11,19,0) 100%);
  border-radius: 14px 14px 0 0; }
.tw-hud-row{ display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.tw-place{ display: flex; flex-direction: column; line-height: 1.2; margin-right: auto; }
.tw-place b{ font-size: 12.5px; font-weight: 600; color: #f4ecdb;
  text-shadow: 0 1px 6px rgba(0,0,0,.9); }
.tw-place i{ font-size: 10.5px; font-style: normal; color: #a9c2d6;
  text-shadow: 0 1px 6px rgba(0,0,0,.9); }
.tw-meter{ display: flex; align-items: center; gap: 6px; }
.tw-lab{ font-size: 10.5px; color: #cfdcea; opacity: .85; text-shadow: 0 1px 6px rgba(0,0,0,.9); }
.tw-leaf{ display: inline-flex; align-items: center; width: 15px; height: 15px; margin-right: 2px; }
.tw-leaf svg{ width: 15px; height: 15px; filter: drop-shadow(0 1px 2px rgba(0,0,0,.6)); }
.tw-lab.is-carbon{ color: #8ff0c2; font-weight: 600; opacity: 1; }
.tw-bar{ display: block; width: 74px; height: 7px; border-radius: 6px; overflow: hidden;
  background: rgba(255,255,255,.14); border: 1px solid rgba(255,255,255,.13); }
.tw-fill{ display: block; height: 100%; width: 100%; transition: width .2s linear; }
.tw-carbon{ background: linear-gradient(90deg,#e0584b,#e8b84b,#3fcf8e); }
.tw-city{ width: 100%; background: linear-gradient(90deg,#3fcf8e,#e8b84b,#e0584b); }
.tw-tide{ width: 0%; background: linear-gradient(90deg,#5fb6e0,#1d5f8c); }
.tw-num{ font-size: 10.5px; min-width: 32px; text-align: right; color: #e8eef4;
  font-variant-numeric: tabular-nums; text-shadow: 0 1px 6px rgba(0,0,0,.9); }
.tw-gaps{ font-size: 11px; color: #7ff0b8; text-shadow: 0 1px 6px rgba(0,0,0,.9); }
.tw-gaps.is-bad{ color: #ff8a7a; }
.tw-clock{ font-size: 12.5px; font-weight: 600; min-width: 36px; text-align: right;
  color: #f4ecdb; font-variant-numeric: tabular-nums; text-shadow: 0 1px 6px rgba(0,0,0,.9); }
.tw-shield{ margin-top: 4px; font-size: 11px; color: #b9cfe0;
  text-shadow: 0 1px 6px rgba(0,0,0,.95); }
.tw-shield b{ color: #7ff0b8; }

/* ---------------- action layer ---------------- */
/* Swells, shells, bursts and the cannon are all drawn on one canvas over the
   scene. It also takes every click: a swell means fire, a brick means pull. */
.tw-fx{ position: absolute; inset: 0; z-index: 5; display: block; touch-action: manipulation; }

/* The chain counter: the payoff for killing swells close together. */
.tw-chain{ position: absolute; left: 0; right: 0; top: 17%; z-index: 7; text-align: center;
  pointer-events: none; font: 800 30px/1 var(--sans, system-ui, sans-serif); letter-spacing: 2px;
  color: #ffe9a8; text-shadow: 0 0 18px rgba(255,190,80,.75), 0 3px 10px rgba(0,0,0,.9); }
.tw-chain[hidden]{ display: none; }
.tw-chain.is-pop{ animation: tw-chainpop .34s cubic-bezier(.2,1.7,.4,1); }
@keyframes tw-chainpop{ from{ transform: scale(.5); opacity: .2 } to{ transform: scale(1); opacity: 1 } }

/* The hand readout: what the linked group under the cursor will throw. */
.tw-cannon{ position: absolute; left: 8px; bottom: 6px; z-index: 7; pointer-events: none; }
.tw-read{ font-size: 12px; font-weight: 600; letter-spacing: .3px; padding: 5px 12px;
  border-radius: 999px; color: #f3ead6; background: rgba(6,12,20,.78);
  border: 1px solid rgba(214,192,140,.30); text-shadow: 0 1px 5px rgba(0,0,0,.9);
  box-shadow: 0 2px 10px rgba(0,0,0,.5); }
.tw-read.is-tidal{ color: #10240f; background: linear-gradient(90deg,#ffe08a,#fff3c4);
  border-color: #ffd76a; text-shadow: none; animation: tw-ready .8s ease-in-out infinite; }
.tw-read.is-pung{ color: #ffd9a8; border-color: rgba(255,180,110,.6); }
.tw-read.is-run{ color: #b6f0d4; border-color: rgba(126,232,178,.55); }
.tw-read.is-flush{ color: #cfe4f5; border-color: rgba(150,210,240,.5); }
@keyframes tw-ready{ 0%,100%{ box-shadow: 0 0 0 0 rgba(255,215,106,.9) } 50%{ box-shadow: 0 0 0 8px rgba(255,215,106,0) } }

/* A swell landing, or a shell going off: the action layer takes the hit — the
   shake rides the canvas only, not the whole stage. The stage holds a full wall
   of real mahjong tiles plus several blurred/filtered backdrop layers; jittering
   all of that forced a repaint of a hundred-plus shadowed nodes every frame and
   tanked the frame rate (the "shake stutters and speeds up" report). Moving just
   the canvas — one composited layer — keeps the punch with zero extra paint, and
   the click rect follows the transform so aiming is unaffected. */
.tw-fx.is-quake{ animation: tw-quake .2s linear infinite; will-change: transform; }
.tw-wrap.is-drown{ filter: saturate(.72) brightness(.92); }
@keyframes tw-quake{
  0%{ transform: translate(0,0) }
  25%{ transform: translate(calc(var(--tw-quake,1) * -1.6px), calc(var(--tw-quake,1) * 1.2px)) }
  50%{ transform: translate(calc(var(--tw-quake,1) * 1.2px), calc(var(--tw-quake,1) * -1.6px)) }
  75%{ transform: translate(calc(var(--tw-quake,1) * 1.6px), calc(var(--tw-quake,1) * .8px)) }
  100%{ transform: translate(0,0) }
}
.tw-wrap.is-alarm::after{ content: ""; position: absolute; inset: 0; z-index: 8; pointer-events: none;
  border-radius: 14px; box-shadow: inset 0 0 0 4px rgba(255,96,74,.9);
  animation: tw-alarm .44s ease-in-out infinite; }
/* FRENZY: the screen edge lights up while a long chain is running, so the
   player feels the streak without a banner in the middle of the fight. */
.tw-wrap.is-frenzy{ box-shadow: inset 0 0 64px rgba(122,222,255,.40); }
.tw-wrap.is-frenzy::after{ content: ""; position: absolute; inset: 0; z-index: 8; pointer-events: none;
  border-radius: 14px; box-shadow: inset 0 0 22px rgba(255,255,255,.28);
  animation: tw-frenzy .5s ease-in-out infinite alternate; }
@keyframes tw-frenzy{ from{ opacity:.35 } to{ opacity:.9 } }
@keyframes tw-alarm{ 0%,100%{ opacity: .12 } 50%{ opacity: 1 } }

/* ---------------- the full-width fortress wall ---------------- */
/* The coast's seawall: a band of real mahjong tiles, three rows deep, bricked
   right across the screen with no gap at the edges, so the freeze can only come
   over where a brick is knocked out. Raised off the waterline so there is open
   sky to see the ice coming. */
.tw-board{
  position: absolute; left: 0; right: 0; bottom: 12%; z-index: 2;
  width: 100%; box-sizing: border-box;
  display: grid; grid-template-columns: repeat(var(--tw-cols, 4), 1fr);
  gap: 2px; padding: 5px 5px 6px;
  background: linear-gradient(180deg, #1a1f1b 0%, #0c0f0d 100%);
  border: 1px solid rgba(214,192,140,.35);
  border-radius: 0 0 4px 4px;
  box-shadow: 0 -10px 26px rgba(0,0,0,.55), 0 4px 12px rgba(0,0,0,.45);
}
/* A crenellated stone coping along the top so it reads as a fortress, not a row of tiles. */
.tw-board::before{
  content:""; position: absolute; left: 0; right: 0; top: -7px; height: 7px;
  background: repeating-linear-gradient(90deg, #2a2f2a 0 14px, #0c0f0d 14px 21px);
  border: 1px solid rgba(214,192,140,.30); border-bottom: none;
  border-radius: 3px 3px 0 0;
}
/* Tile bodies are ivory: that is the physical tile, not a theme color. The face
   artwork is drawn by ../shared/tiles.js into the .tl wrapper inside. */
.tw-cell{
  position: relative; aspect-ratio: 58 / 79; border-radius: 4px; cursor: pointer;
  background: linear-gradient(180deg,#FDF9F0,#E9DFC8);
  box-shadow: inset 0 -3px 0 rgba(0,0,0,.20), 0 2px 4px rgba(0,0,0,.45);
  border: 2px solid transparent; transition: transform .09s, border-color .12s;
  overflow: hidden;
}
.tw-cell .tl{ position: relative; width: 100%; height: 100%; display: block;
  background: none; border: 0; box-shadow: none; }
.tw-cell .tl svg{ width: 100%; height: 100%; display: block; }
.tw-cell .tl .cnr{ position: absolute; top: 1px; left: 3px; z-index: 3;
  font: 600 10px/1 Georgia, 'Times New Roman', serif; color: #9A7B3F; }
.tw-cell.is-sel{ border-color: #e8b84b; transform: translateY(-3px) scale(1.06); }
.tw-cell.is-hint{ border-color: #ffd76a; animation: tw-hint 1.1s ease-in-out infinite; }
/* The linked group the cursor is over: lit up and ready to spend. */
.tw-cell.is-aim{ border-color: #ffd76a; transform: translateY(-3px) scale(1.05);
  box-shadow: inset 0 -3px 0 rgba(0,0,0,.20), 0 0 0 2px rgba(255,215,106,.65), 0 4px 12px rgba(0,0,0,.5);
  z-index: 3; }
@keyframes tw-hint{ 0%,100%{box-shadow:0 0 0 0 rgba(255,215,106,.9)} 50%{box-shadow:0 0 0 7px rgba(255,215,106,0)} }
.tw-cell.is-pop{ animation: tw-pop .22s forwards; }
@keyframes tw-pop{ to{ transform: scale(.15) rotate(12deg); opacity: 0 } }
/* A brick the surf tore out: hollow, and the harbor shows through it. */
.tw-cell.is-hole{
  background: linear-gradient(180deg, rgba(9,24,36,.94), rgba(5,14,24,.97));
  box-shadow: inset 0 3px 9px rgba(0,0,0,.8), inset 0 0 14px rgba(90,180,226,.30);
  cursor: pointer;
}
.tw-cell.is-hole::after{
  content: ""; position: absolute; inset: 24%; border-radius: 3px;
  background: radial-gradient(closest-side, rgba(150,215,245,.45), rgba(150,215,245,0));
}
/* A course hardened by a Run: the surf cannot bite through it. */
.tw-cell.is-hard{
  border-color: #ffd76a;
  box-shadow: inset 0 -3px 0 rgba(0,0,0,.20), 0 0 11px rgba(255,215,106,.5);
}
/* One ice ram has cracked the tile, but the brick is still in the course. */
.tw-cell.is-cracked{
  border-color: #ff9a4a;
  box-shadow: inset 0 -3px 0 rgba(0,0,0,.20), 0 0 0 2px rgba(255,150,70,.55), 0 4px 12px rgba(0,0,0,.5);
  animation: tw-crack .5s ease-in-out;
}
.tw-cell.is-cracked::after{
  content:""; position:absolute; inset:0; z-index:2; pointer-events:none;
  background: linear-gradient(135deg, transparent 46%, rgba(255,120,60,.75) 46%, rgba(255,120,60,.75) 54%, transparent 54%),
              linear-gradient(45deg, transparent 46%, rgba(255,120,60,.55) 46%, rgba(255,120,60,.55) 54%, transparent 54%);
}
@keyframes tw-crack{ 0%{ transform: scale(.97) } 50%{ transform: scale(1.04) } 100%{ transform: scale(1) } }
/* Ambient reshuffle: a whole brick flips in place to a fresh pip pattern. The
   course never moves — only the face turns, so the wall reads as alive, not broken. */
.tw-cell.is-flip{ perspective: 240px; }
.tw-cell.is-flip .tl{ animation: tw-flip .28s ease; transform-origin: 50% 50%; }
@keyframes tw-flip{
  0%{ transform: rotateY(82deg); opacity: 0; }
  55%{ opacity: 1; }
  100%{ transform: rotateY(0); opacity: 1; }
}
/* A tile the gunship just earned back: a quick green mend. */
.tw-cell.is-repair{
  animation: tw-repair .54s ease-out;
  border-color: #7ff0b8;
  box-shadow: inset 0 -3px 0 rgba(0,0,0,.20), 0 0 0 2px rgba(127,240,184,.70), 0 4px 12px rgba(0,0,0,.5);
}
@keyframes tw-repair{
  0%{ transform: scale(.82); filter: brightness(1.7) saturate(1.4); }
  60%{ transform: scale(1.05); }
  100%{ transform: scale(1); filter: none; }
}
/* Under the waterline: the brick is still there, just drowned. */
.tw-cell.is-wet::before{
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(180deg, rgba(120,200,236,.28), rgba(10,44,70,.42));
}

/* ---------------- start / result card ---------------- */
.tw-overlay{ position: absolute; inset: 0; z-index: 10; display: flex; align-items: center;
  justify-content: center; padding: 14px; background: rgba(5,9,13,.84);
  backdrop-filter: blur(3px); border-radius: 14px; }
.tw-overlay[hidden]{ display: none; }
.tw-card{ width: min(100%, 420px); max-height: 100%; overflow: auto; text-align: center;
  background: var(--g-card); border: 1px solid var(--g-line); border-radius: 16px;
  padding: 18px; box-shadow: var(--g-shadow-lg); }
.tw-card h1{ margin: 0 0 4px; font-family: var(--serif, Georgia, serif); font-size: 24px; letter-spacing: 2px;
  color: var(--g-strong); }
.tw-sub{ margin: 0 0 10px; font-size: 12px; color: var(--g-muted); }
.tw-blurb{ margin: 0 0 12px; font-size: 13px; line-height: 1.65; color: var(--g-fg); }
/* End-of-coast report: four quick stats plus a grade, so a clear feels earned. */
.tw-recap{ margin: 0 0 12px; padding: 11px 11px 9px; border: 1px dashed var(--g-line); border-radius: 12px;
  background: var(--g-card-soft, rgba(255,255,255,.03)); }
.tw-recap[hidden]{ display: none; }
.tw-recap-grid{ display: grid; grid-template-columns: repeat(4, 1fr); gap: 7px; }
.tw-rc{ display: flex; flex-direction: column; align-items: center; padding: 7px 4px; border-radius: 9px;
  background: rgba(255,255,255,.05); border: 1px solid var(--g-line); }
.tw-rc-v{ font: 800 19px/1 var(--sans, system-ui, sans-serif); color: var(--g-strong); font-variant-numeric: tabular-nums; }
.tw-rc-l{ margin-top: 3px; font-size: 10px; letter-spacing: .3px; color: var(--g-muted); }
.tw-recap-foot{ display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 10px;
  font-size: 12.5px; color: var(--g-fg); }
.tw-recap-foot b{ color: #3fcf8e; }
.tw-grade{ font: 800 16px/1 var(--sans, system-ui, sans-serif); width: 30px; height: 30px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--g-line); }
.tw-grade.s{ color: #ffd76a; border-color: #ffd76a; box-shadow: 0 0 12px rgba(255,215,106,.5); }
.tw-grade.a{ color: #7ff0b8; border-color: rgba(127,240,184,.6); }
.tw-grade.b{ color: #9fd2ff; border-color: rgba(159,210,255,.5); }
.tw-grade.c{ color: var(--g-muted); }
.tw-rules{ display: flex; flex-direction: column; gap: 7px; margin-bottom: 6px; }
.tw-rule{ display: flex; align-items: center; gap: 9px; text-align: left;
  background: var(--g-card-soft, rgba(255,255,255,.04)); border: 1px solid var(--g-line);
  border-radius: 10px; padding: 8px 9px; }
.tw-demo{ display: flex; gap: 3px; flex: 0 0 auto; }
.tw-demo .tl{ width: 25px; height: 34px; border-radius: 5px; background: linear-gradient(180deg,#FDF9F0,#E9DFC8);
  box-shadow: inset 0 -2px 0 rgba(0,0,0,.18); }
.tw-demo .tl svg{ width: 100%; height: 100%; display: block; }
.tw-demo .tl .cnr{ display: none; }
.tw-eff{ font-size: 12px; line-height: 1.5; color: var(--g-fg); }
.tw-eff b{ color: var(--g-accent); }
.tw-note{ margin: 10px 0 0; font-size: 11.5px; line-height: 1.6; color: var(--g-muted); }

/* ---------------- help sheet ---------------- */
.tw-help{ position: fixed; inset: 0; z-index: 50; display: flex; align-items: center;
  justify-content: center; padding: 14px; background: rgba(4,8,12,.88); border-radius: 0; }
.tw-help[hidden]{ display: none; }
.tw-help-card{ position: relative; width: min(100%, 460px); max-height: 100%; overflow: auto;
  background: var(--g-card); border: 1px solid var(--g-line); border-radius: 16px; padding: 20px;
  box-shadow: var(--g-shadow-lg); }
.tw-help-card h2{ margin: 0 0 10px; font-family: var(--serif, Georgia, serif); font-size: 19px; color: var(--g-strong); }
.tw-help-card h3{ margin: 14px 0 5px; font-size: 13.5px; color: var(--g-accent); }
.tw-help-card p{ margin: 6px 0; font-size: 13px; line-height: 1.7; color: var(--g-fg); }
.tw-help-lead{ color: var(--g-fg); }
.tw-help-note{ margin-top: 12px; font-size: 11.5px; color: var(--g-muted);
  border-top: 1px dashed var(--g-line); padding-top: 10px; }
.tw-help-x{ position: absolute; top: 8px; right: 10px; width: 30px; height: 30px; font-size: 20px;
  line-height: 1; background: none; border: 0; color: var(--g-muted); cursor: pointer; }

@media (max-width: 560px){
  .tw-hud{ padding: 7px 8px 6px; }
  .tw-hud-row{ gap: 7px; }
  .tw-bar{ width: 52px; }
  .tw-place b{ font-size: 11.5px; }
  .tw-lab{ font-size: 10px; }
  .tw-board{ gap: 2px; padding: 4px 4px 5px; }
  .tw-cell{ aspect-ratio: 3 / 4; }          /* mobile keeps taller tiles for touch */
  .tw-chain{ font-size: 23px; top: 15%; }
  .tw-slot{ width: 23px; height: 32px; }
  .tw-load{ font-size: 10px; padding: 2px 7px; }
}
