/* Sweet Crush: strawberry-milk sky, grape-licorice ink, everything cut out like a sticker. */
/* Fonts ship with the app (SIL OFL 1.1, see fonts/) so no outside server is contacted. */
@font-face {
  font-family: "Bagel Fat One";
  src: url(../fonts/bagel-fat-one-latin.woff2) format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Rethink Sans";
  src: url(../fonts/rethink-sans-latin.woff2) format("woff2");
  font-weight: 400 800;
  font-display: swap;
}
:root {
  --bg: #ffe6f0;
  --bg-2: #ffd0e3;
  --ink: #2b0f33;
  --line: #2b0f33; /* sticker outline, dark in both themes */
  --ink-soft: #6b4a73;
  --card: #fffafc;
  --cut: #ffffff;
  --cherry: #ff4d6d;
  --cherry-ink: #b3123a;
  --mint: #2fd38e;
  --lemon: #ffd23f;
  --grape: #3a2560;
  --path: #ff9cc0;
  --locked: #e9c9d8;
  --star-off: #e9c9d8;
  --tile-a: #3a2560;
  --tile-b: #44306e;
  --wrap: #ff4d6d; /* the candy wrapper; same in both themes so it matches the install splash */
  --print: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120' fill='%232b0f33' fill-opacity='.1'%3E%3Cpath d='M20 36c-10-6-11-13-7.5-16.5S20 18 20 22c0-4 4-5.5 7.5-2.5S30 30 20 36z'/%3E%3Cpath d='M82 10l3.2 7 7.6.7-5.8 5 1.8 7.4L82 26.2l-6.8 3.9 1.8-7.4-5.8-5 7.6-.7z'/%3E%3Cpath d='M52 58c4.5 5.5 7.5 9.5 7.5 13a7.5 7.5 0 0 1-15 0c0-3.5 3-7.5 7.5-13z'/%3E%3Cpath d='M100 68l9 8-9 11-9-11z'/%3E%3Crect x='14' y='84' width='17' height='17' rx='4.5' transform='rotate(14 22.5 92.5)'/%3E%3Cpath d='M70 110a11 11 0 0 1 22 0z'/%3E%3C/svg%3E");
  --display: "Bagel Fat One", "Arial Rounded MT Bold", system-ui, sans-serif;
  --body: "Rethink Sans", system-ui, sans-serif;
  --pop: cubic-bezier(.2, .9, .3, 1.3);
  --out: cubic-bezier(.2, .8, .2, 1);
  /* Apple-style springs sampled into easings: cards settle without overshoot (damping 1, response .35s);
     sheets use the drawer spring (damping .8, response .3s). Older browsers fall back to ease. */
  /* Depth means "press me": tappable stickers stand on a hard lip; floating surfaces get one soft shadow. */
  --lip: 0 4px 0 var(--line);
  --float: 0 10px 24px -8px rgb(43 15 51 / .4);
  --spring-ui: linear(0, 0.051, 0.162, 0.293, 0.421, 0.536, 0.634, 0.715, 0.781, 0.833, 0.873, 0.905, 0.929, 0.947, 0.96, 0.971, 0.978, 0.984, 0.988, 0.991, 0.994, 0.995, 0.997, 0.998, 1);
  --spring-sheet: linear(0, 0.048, 0.16, 0.3, 0.443, 0.575, 0.689, 0.783, 0.856, 0.912, 0.952, 0.979, 0.997, 1.008, 1.013, 1.015, 1.015, 1.013, 1.011, 1.009, 1.007, 1.005, 1.004, 1.003, 1);
  color-scheme: light dark;
}
/* Dark follows the phone unless the player picks a theme in Settings (data-theme on <html>). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #1a0c26;
    --bg-2: #24123a;
    --ink: #fff0f6;
    --ink-soft: #c9a9d6;
    --card: #2d1745;
    --cut: #5b3577;
    --line: #0b0412;
    --cherry-ink: #ff8fa6;
    --path: #6a3b80;
    --locked: #3b2350;
  --star-off: #6a4a86;
    --tile-a: #2a1747;
    --tile-b: #331d55;
    --float: 0 10px 24px -8px rgb(0 0 0 / .65);
    --track: #8466a3; /* empty bars and off switches: the dark outline vanishes at night, so the track itself carries the edge (3:1+) */
  }
}
:root[data-theme="dark"] {
  --bg: #1a0c26;
  --bg-2: #24123a;
  --ink: #fff0f6;
  --ink-soft: #c9a9d6;
  --card: #2d1745;
  --cut: #5b3577;
  --line: #0b0412;
  --cherry-ink: #ff8fa6;
  --path: #6a3b80;
  --locked: #3b2350;
  --star-off: #6a4a86;
  --tile-a: #2a1747;
  --tile-b: #331d55;
  --float: 0 10px 24px -8px rgb(0 0 0 / .65);
  --track: #8466a3;
  color-scheme: dark;
}
:root[data-theme="light"] { color-scheme: light; }

/* Each episode is its own little world on the map: sky, two hill tones, and its node colour.
   Day skies wear a white sprinkle print and a sun haze; night skies a star print and a glow in the episode's colour. */
:root {
  --zone-print: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140' fill='%23fff' fill-opacity='.6'%3E%3Crect x='18' y='20' width='14' height='5' rx='2.5' transform='rotate(30 25 22)'/%3E%3Crect x='92' y='14' width='14' height='5' rx='2.5' transform='rotate(-40 99 16)'/%3E%3Crect x='60' y='62' width='14' height='5' rx='2.5' transform='rotate(70 67 64)'/%3E%3Crect x='14' y='100' width='14' height='5' rx='2.5' transform='rotate(-15 21 102)'/%3E%3Crect x='108' y='96' width='14' height='5' rx='2.5' transform='rotate(55 115 98)'/%3E%3Ccircle cx='50' cy='122' r='3'/%3E%3Ccircle cx='122' cy='56' r='2.5'/%3E%3Ccircle cx='36' cy='60' r='2'/%3E%3C/svg%3E");
  --glow: rgb(255 255 255 / .75);
  --prop-tint: 0%; /* how much of the episode colour tints the white parts of scenery */
  --lock-base: #fff;
  --halo: rgb(255 210 63 / .55); /* light around the current level: lemon by day, cherry at night */
}
.e0 { --sky: #ffe6f0; --land: #ffc1d8; --land-2: #ff9cc2; --ep: #ff6fa0; }
.e1 { --sky: #dcf6f3; --land: #a8eadf; --land-2: #6fd6c9; --ep: #22b8a8; }
.e2 { --sky: #fff0dc; --land: #ffd3a1; --land-2: #f6b06a; --ep: #f08a2c; }
.e3 { --sky: #efe6ff; --land: #d3baff; --land-2: #b394f7; --ep: #a07cf8; }
.e4 { --sky: #e4f1ff; --land: #bcd8ff; --land-2: #94bdf5; --ep: #4f8fe8; }
.e5 { --sky: #eef8dc; --land: #cfe9a3; --land-2: #a9d46c; --ep: #6aa82c; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --prop-tint: 45%;
    --lock-base: #1a0c26;
    --halo: rgb(255 77 109 / .4);
    --zone-print: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160' fill='%23fff'%3E%3Ccircle cx='12' cy='18' r='1.3' fill-opacity='.9'/%3E%3Ccircle cx='70' cy='9' r='.9' fill-opacity='.6'/%3E%3Ccircle cx='130' cy='30' r='1.4' fill-opacity='.8'/%3E%3Ccircle cx='40' cy='76' r='1' fill-opacity='.5'/%3E%3Ccircle cx='100' cy='88' r='1.2' fill-opacity='.7'/%3E%3Ccircle cx='150' cy='112' r='.9' fill-opacity='.5'/%3E%3Ccircle cx='22' cy='132' r='1.3' fill-opacity='.8'/%3E%3Ccircle cx='84' cy='148' r='.9' fill-opacity='.6'/%3E%3Ccircle cx='118' cy='62' r='.8' fill-opacity='.4'/%3E%3Cpath fill-opacity='.85' d='M55 34q1 5 6 6q-5 1-6 6q-1-5-6-6q5-1 6-6z'/%3E%3Cpath fill-opacity='.6' d='M136 128q.8 3.5 4 4q-3.2.5-4 4q-.8-3.5-4-4q3.2-.5 4-4z'/%3E%3C/svg%3E");
  }
  :root:not([data-theme="light"]) .zone { --glow: color-mix(in srgb, var(--ep) 34%, transparent); }
  :root:not([data-theme="light"]) .e0 { --sky: #241030; --land: #4a1d5c; --land-2: #6a2a78; }
  :root:not([data-theme="light"]) .e1 { --sky: #08202c; --land: #0f4a55; --land-2: #156a6c; }
  :root:not([data-theme="light"]) .e2 { --sky: #1f1010; --land: #55290f; --land-2: #7a3f17; }
  :root:not([data-theme="light"]) .e3 { --sky: #140c34; --land: #33246e; --land-2: #4c3596; }
  :root:not([data-theme="light"]) .e4 { --sky: #0b1830; --land: #1a3463; --land-2: #26488a; }
  :root:not([data-theme="light"]) .e5 { --sky: #0e1d0b; --land: #27431a; --land-2: #3a6024; }
}
:root[data-theme="dark"] {
  --prop-tint: 45%;
  --lock-base: #1a0c26;
  --halo: rgb(255 77 109 / .4);
  --zone-print: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160' fill='%23fff'%3E%3Ccircle cx='12' cy='18' r='1.3' fill-opacity='.9'/%3E%3Ccircle cx='70' cy='9' r='.9' fill-opacity='.6'/%3E%3Ccircle cx='130' cy='30' r='1.4' fill-opacity='.8'/%3E%3Ccircle cx='40' cy='76' r='1' fill-opacity='.5'/%3E%3Ccircle cx='100' cy='88' r='1.2' fill-opacity='.7'/%3E%3Ccircle cx='150' cy='112' r='.9' fill-opacity='.5'/%3E%3Ccircle cx='22' cy='132' r='1.3' fill-opacity='.8'/%3E%3Ccircle cx='84' cy='148' r='.9' fill-opacity='.6'/%3E%3Ccircle cx='118' cy='62' r='.8' fill-opacity='.4'/%3E%3Cpath fill-opacity='.85' d='M55 34q1 5 6 6q-5 1-6 6q-1-5-6-6q5-1 6-6z'/%3E%3Cpath fill-opacity='.6' d='M136 128q.8 3.5 4 4q-3.2.5-4 4q-.8-3.5-4-4q3.2-.5 4-4z'/%3E%3C/svg%3E");
}
:root[data-theme="dark"] .zone { --glow: color-mix(in srgb, var(--ep) 34%, transparent); }
:root[data-theme="dark"] .e0 { --sky: #241030; --land: #4a1d5c; --land-2: #6a2a78; }
:root[data-theme="dark"] .e1 { --sky: #08202c; --land: #0f4a55; --land-2: #156a6c; }
:root[data-theme="dark"] .e2 { --sky: #1f1010; --land: #55290f; --land-2: #7a3f17; }
:root[data-theme="dark"] .e3 { --sky: #140c34; --land: #33246e; --land-2: #4c3596; }
:root[data-theme="dark"] .e4 { --sky: #0b1830; --land: #1a3463; --land-2: #26488a; }
:root[data-theme="dark"] .e5 { --sky: #0e1d0b; --land: #27431a; --land-2: #3a6024; }

*, ::before, ::after { box-sizing: border-box; }
html {
  -webkit-tap-highlight-color: transparent;
  -webkit-text-size-adjust: 100%;
  overscroll-behavior: none;
  height: 100%;
}
body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
  background: var(--bg);
  color: var(--ink);
  font: 1rem/1.4 var(--body);
}
button {
  font: inherit;
  color: inherit;
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  cursor: pointer;
}
button:focus-visible { outline: 3px solid var(--cherry); outline-offset: 3px; }
[hidden] { display: none !important; }

#app { height: 100dvh; position: relative; }
.screen { height: 100%; display: flex; flex-direction: column; }

/* ---------- bars ---------- */
.bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px max(12px, env(safe-area-inset-right)) 8px max(12px, env(safe-area-inset-left));
  flex-shrink: 0;
  width: 100%;
  max-width: 640px; /* as wide as the largest board, so controls line up with it */
  margin-inline: auto;
}
.bar.top { padding-top: max(8px, env(safe-area-inset-top)); }
.bar.bottom { padding-bottom: max(10px, env(safe-area-inset-bottom)); justify-content: center; }
.bar-actions { display: flex; gap: 6px; }

/* Pressable stickers: a darker lip inside gives them depth; pressing sinks them onto their shadow. */
.icon-btn, .btn, .node, .booster {
  border: 3px solid var(--line);
  box-shadow: inset 0 -5px 0 rgb(43 15 51 / .16), var(--lip);
  transition: transform 100ms var(--out), box-shadow 100ms var(--out);
}
.icon-btn:active, .btn:active, .node:active, .booster:active {
  transform: translateY(3px);
  box-shadow: inset 0 -2px 0 rgb(43 15 51 / .16), 0 1px 0 var(--line);
}
.icon-btn {
  width: 44px;
  height: 44px;
  border-radius: 14px;
  background: var(--card);
  font-size: 1.3rem;
  line-height: 1;
  display: grid;
  place-items: center;
  padding: 0;
}
.ico {
  display: block;
  width: 26px;
  height: 26px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.5; /* the one line weight for every icon; never override per icon */
  stroke-linecap: round;
  stroke-linejoin: round;
}
.ico .y { fill: url(#g-lemon); }
.ico .r { fill: url(#g-cherry); }
.ico .m { fill: url(#g-mist); }
/* One soft highlight per icon, top-left, like light catching a glossy candy. */
.ico .shine { fill: none; stroke: #fff; stroke-opacity: .85; stroke-width: 1.6; }
.booster .ico { width: 30px; height: 30px; }
.booster.locked .ico { width: 22px; height: 22px; }
.reward .ico { margin: auto; }
/* ---------- map ---------- */
#map-screen { position: relative; }
/* Bars float over the map so the world runs edge to edge, under the notch too. */
.map-top, .map-foot { position: absolute; left: 0; right: 0; z-index: 3; pointer-events: none; }
.map-top { top: 0; gap: 6px; justify-content: space-between; }
.map-foot { bottom: 0; }
.map-top > *, .map-foot > * { pointer-events: auto; }
.pill {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 44px;
  padding: 0 14px 0 4px;
  border: 3px solid var(--line);
  border-radius: 999px;
  background: var(--card);
  box-shadow: var(--float);
  font: 400 1.15rem/1 var(--display);
  white-space: nowrap;
}
.lives .heart { position: relative; display: grid; place-items: center; width: 36px; height: 36px; }
.lives .heart svg { position: absolute; inset: -2px; width: 40px; height: 40px; }
.lives .heart span { position: relative; padding-bottom: 3px; color: #fff; -webkit-text-stroke: 4px var(--line); paint-order: stroke fill; }
.lives small { font: 800 .8rem/1 var(--body); color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.stars-total { padding-left: 8px; }
.stars-total .star { font-size: 1.4rem; }
.map-top .bar-actions { margin-left: auto; }

.map-scroll {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  scrollbar-width: none;
}
.map-scroll::-webkit-scrollbar { display: none; }
.map { position: relative; }
/* Sky deepens toward the horizon (where it meets the hills) with a haze or glow up top. */
.zone {
  position: absolute;
  left: 0;
  right: 0;
  background: var(--sky);
  background:
    var(--zone-print) 0 0 / 140px,
    radial-gradient(circle at 80% 160px, var(--glow), transparent 260px),
    linear-gradient(to top, color-mix(in srgb, var(--land) 45%, var(--sky)) 0, var(--sky) 900px);
}
/* A far skyline behind the hills: no outline, because distant things are not stickers. */
.far {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 52px;
  height: 120px;
  background: var(--land);
  background: color-mix(in srgb, var(--land) 60%, var(--sky));
  -webkit-mask: var(--skyline) repeat-x 0 100% / 400px 120px;
  mask: var(--skyline) repeat-x 0 100% / 400px 120px;
}
.zone.e0 { --skyline: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='120'%3E%3Cpath d='M0 104h400v16H0z'/%3E%3Ccircle cx='40' cy='50' r='22'/%3E%3Cpath d='M37 50h6v60h-6z'/%3E%3Ccircle cx='122' cy='72' r='16'/%3E%3Cpath d='M119 72h6v40h-6z'/%3E%3Ccircle cx='210' cy='38' r='27'/%3E%3Cpath d='M207 40h6v70h-6z'/%3E%3Ccircle cx='298' cy='66' r='18'/%3E%3Cpath d='M295 66h6v44h-6z'/%3E%3Ccircle cx='366' cy='52' r='21'/%3E%3Cpath d='M363 52h6v58h-6z'/%3E%3C/svg%3E"); }
.zone.e1 { --skyline: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='120'%3E%3Cpath d='M0 92q25-12 50 0t50 0 50 0 50 0 50 0 50 0 50 0 50 0V120H0z'/%3E%3Ccircle cx='60' cy='66' r='9'/%3E%3Ccircle cx='72' cy='42' r='5'/%3E%3Ccircle cx='180' cy='58' r='11'/%3E%3Ccircle cx='196' cy='30' r='6'/%3E%3Ccircle cx='188' cy='12' r='3.5'/%3E%3Ccircle cx='300' cy='66' r='8'/%3E%3Ccircle cx='312' cy='46' r='4.5'/%3E%3Ccircle cx='364' cy='24' r='5'/%3E%3C/svg%3E"); }
.zone.e2 { --skyline: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='120'%3E%3Cpath d='M0 120V78h70V58h80V34h90v30h70V48h90v72z'/%3E%3Cpath d='M96 58v14a4 4 0 0 0 8 0V58zM176 34v20a4 4 0 0 0 8 0V34zM214 34v10a4 4 0 0 0 8 0V34zM340 48v16a4 4 0 0 0 8 0V48z'/%3E%3C/svg%3E"); }
.zone.e3 { --skyline: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='120'%3E%3Cpath d='M0 120V96Q35 26 70 96 110 6 160 96 195 44 230 96 280 2 330 96 365 48 400 96V120z'/%3E%3C/svg%3E"); }
.zone.e4 { --skyline: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='120'%3E%3Cpath d='M0 104h400v16H0z'/%3E%3Crect x='18' y='58' width='54' height='56' rx='16'/%3E%3Crect x='84' y='78' width='40' height='36' rx='12'/%3E%3Crect x='150' y='40' width='62' height='74' rx='18'/%3E%3Crect x='226' y='70' width='44' height='44' rx='13'/%3E%3Crect x='296' y='50' width='58' height='64' rx='17'/%3E%3Crect x='364' y='82' width='34' height='32' rx='10'/%3E%3C/svg%3E"); }
.zone.e5 { --skyline: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='120'%3E%3Cpath d='M0 120V98l28-38 22 30 34-62 30 64 34-42 26 34 38-66 34 70 30-36 30 40 32-56 28 50 34-24v52z'/%3E%3C/svg%3E"); }
.hills { position: absolute; left: 0; right: 0; bottom: 0; width: 100%; height: 130px; }
.hills.flip { transform: scaleX(-1); }
.hills path { stroke: var(--line); stroke-width: 3; vector-effect: non-scaling-stroke; stroke-linejoin: round; }
.hills .back { fill: var(--land); }
.hills .front { fill: var(--land-2); }
/* Path and nodes keep to a centre column so tablets get a wider world, not a stretched road. */
.track { position: absolute; top: 0; bottom: 0; left: 50%; width: min(100%, 520px); transform: translateX(-50%); }
.track svg.road { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.road path { fill: none; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.road .edge { stroke: var(--line); stroke-width: 24; }
.road .base { stroke: #fff; stroke-width: 17; }
.road .stripe { stroke: var(--cherry); stroke-width: 17; stroke-dasharray: 9 13; stroke-linecap: butt; }
.road .ahead { stroke: var(--line); stroke-width: 11; stroke-dasharray: 0 22; opacity: .28; }
.prop { position: absolute; width: 56px; height: 56px; margin: -28px 0 0 -28px; pointer-events: none; }
.prop svg { width: 100%; height: 100%; overflow: visible; }
.prop * { stroke: var(--line); stroke-width: 2.6; stroke-linejoin: round; stroke-linecap: round; }
.prop .a { fill: var(--ep); }
.prop .b { fill: #fff; fill: color-mix(in srgb, var(--ep) var(--prop-tint), #fff); }
.prop .s { fill: none; stroke: #fff; stroke-width: 3; }
.prop .shine { fill: #fff; stroke: none; opacity: .7; }

.node {
  position: absolute;
  width: 62px;
  height: 62px;
  margin: -31px 0 0 -31px;
  border-radius: 50%;
  background: var(--ep);
  color: #fff;
  font: 400 1.45rem/1 var(--display);
  -webkit-text-stroke: 4px var(--line);
  paint-order: stroke fill;
  padding: 0 0 3px;
}
/* One shine, like the candies on the board. */
.node::before {
  content: "";
  position: absolute;
  left: 17%;
  top: 13%;
  width: 30%;
  height: 17%;
  border-radius: 50%;
  background: #fff;
  opacity: .55;
  transform: rotate(-28deg);
}
.node.current {
  width: 76px;
  height: 76px;
  margin: -38px 0 0 -38px;
  background: var(--cherry);
  font-size: 1.8rem;
  box-shadow: inset 0 -5px 0 rgb(43 15 51 / .16), var(--lip), 0 0 44px 14px var(--halo);
  animation: bob 1.4s ease-in-out infinite;
}
.node.current::after {
  content: "";
  position: absolute;
  inset: -12px;
  border: 4px solid var(--cherry);
  border-radius: 50%;
  animation: ring 1.6s var(--out) infinite;
}
@keyframes ring { from { transform: scale(.8); opacity: .9; } to { transform: scale(1.25); opacity: 0; } }
.node.locked {
  background: var(--locked);
  background: color-mix(in srgb, var(--ep) 24%, var(--lock-base));
  color: var(--ink-soft);
  -webkit-text-stroke: 0;
  box-shadow: none;
  border-color: var(--ink-soft);
  cursor: default;
}
.node.locked::before { display: none; }
.node .stars {
  position: absolute;
  left: 50%;
  bottom: -15px;
  transform: translateX(-50%);
  display: flex;
  gap: 1px;
  padding: 1px 6px 2px;
  border: 2px solid var(--line);
  border-radius: 99px;
  background: var(--card);
  -webkit-text-stroke: 0;
  font-size: .85rem;
  line-height: 1;
  white-space: nowrap;
}
/* A small cherry pill marking tough levels, on the map node and the level card; very tough ones go licorice and lemon. */
.tag {
  display: inline-block;
  padding: 2px 7px 3px;
  border: 2px solid var(--line);
  border-radius: 99px;
  background: var(--cherry);
  color: #fff;
  -webkit-text-stroke: 0;
  font: 800 .75rem/1 var(--body);
  white-space: nowrap;
}
.node .tag { position: absolute; left: 50%; top: -13px; transform: translateX(-50%) rotate(-6deg); }
.tag.very { background: var(--line); color: var(--lemon); }
.node.locked .tag { background: var(--card); color: var(--ink-soft); }
/* Tough pins are the episode colour steeped in licorice, darker still when very tough. */
.node.done.tough1 { background: color-mix(in srgb, var(--ep) 72%, #2b0f33); }
.node.done.tough2 { background: color-mix(in srgb, var(--ep) 40%, #2b0f33); }
/* Milestone pins (each episode's last level) wear a lemon ring. */
.node.milestone { outline: 4px solid var(--lemon); outline-offset: 2px; }
.node.locked.milestone { outline-color: var(--locked); }
/* Won on the first try: a small lemon crown tipped on the pin. */
.crown { display: inline-block; width: 1em; height: 1em; vertical-align: -.15em; }
.crown svg { display: block; width: 100%; height: 100%; overflow: visible; }
.node .crown { position: absolute; left: -18px; top: calc(50% - 12px); width: 24px; height: 24px; transform: rotate(-24deg); }
/* The player's own sticker stands beside the level they are on, on the side facing the middle. */
.node .me {
  position: absolute;
  top: -14px;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: var(--cut);
  padding: 3px;
  box-shadow: var(--float);
}
.node .me.l { right: calc(100% - 4px); }
.node .me.r { left: calc(100% - 4px); }
.node .me svg { display: block; width: 100%; height: 100%; }
/* The episode chest sits on the last level of each episode until it is won. */
.node .prize {
  position: absolute;
  right: -16px;
  bottom: -6px;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 2px solid var(--line);
  border-radius: 50%;
  background: var(--card);
  color: var(--line);
  -webkit-text-stroke: 0;
}
.node .prize .ico { width: 20px; height: 20px; }
.star { display: inline-block; color: var(--star-off); }
.star svg { display: block; width: 1em; height: 1em; overflow: visible; fill: currentColor; stroke: var(--line); stroke-width: 2.2; stroke-linejoin: round; }
.star.on svg { fill: url(#g-lemon); }
@keyframes bob { 50% { transform: translateY(-6px); } }
/* A level opened by the last win pops onto the map once the wrapper has parted. */
.node.fresh { animation: arrive 520ms var(--pop) 420ms backwards, bob 1.4s ease-in-out 940ms infinite; }
@keyframes arrive { from { scale: .4; opacity: 0; } }
/* Behind Trophies or Settings the map is covered; stop its loops so the phone can idle. */
#map-screen[inert] .node, #map-screen[inert] .node::after { animation-play-state: paused; }
/* A counter that just went up gives a small bounce, like a reward landing in it. */
.pill.bump { animation: bump 380ms var(--out) 480ms both; }
.pill.bump.now { animation-delay: 0ms; } /* a flying reward already timed the landing */
/* Win stars on their way to the counter (moved by app.js fly()). */
.token { position: fixed; left: 50%; top: 50%; z-index: 40; font-size: 34px; line-height: 0; pointer-events: none; }
@keyframes bump { 40% { scale: 1.14; } }
/* Episode signs stand on the hills where each world begins. */
.episode {
  position: absolute;
  left: 50%;
  transform: translate(-50%, -50%);
  display: grid;
  justify-items: center;
  gap: 2px;
  padding: 8px 22px 9px;
  border: 3px solid var(--line);
  border-radius: 16px;
  background: var(--ep);
  box-shadow: var(--float);
  width: max-content; /* left: 50% alone would cap it at half the screen */
  max-width: calc(100% - 32px);
  text-align: center;
}
.episode b {
  font: 400 1.3rem/1.05 var(--display);
  color: #fff;
  -webkit-text-stroke: 5px var(--line);
  paint-order: stroke fill;
}
.episode small { font: 800 .78rem/1.2 var(--body); color: var(--line); }

.play-cta {
  min-width: min(260px, 100%);
  min-height: 64px;
  font-size: 1.5rem;
  -webkit-text-stroke: 0;
}

/* ---------- tab bar ---------- */
/* Room the floating bar takes at the bottom: its height, the gap under it, and the home indicator. */
:root { --bar-space: calc(64px + max(12px, env(safe-area-inset-bottom)) + 12px); }
.bar.map-foot { padding-bottom: var(--bar-space); } /* .bar.bottom would win otherwise */
.tabbar {
  position: absolute; /* pinned to #app like Play, so both move together when Chrome's address bar slides in or out */
  left: 50%;
  bottom: max(12px, env(safe-area-inset-bottom));
  z-index: 6;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  width: min(340px, calc(100% - 32px));
  height: 64px;
  padding: 5px;
  translate: -50% 0;
  border: 3px solid var(--line);
  border-radius: 999px;
  background: var(--card);
  box-shadow: var(--float);
}
/* Frosted candy glass: the map shows through, blurred, with a bright rim where light catches the top edge. */
@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .tabbar {
    background: rgb(255 250 252 / .6);
    -webkit-backdrop-filter: blur(14px) saturate(1.8);
    backdrop-filter: blur(14px) saturate(1.8);
    box-shadow: inset 0 2px 0 rgb(255 255 255 / .85), var(--float);
  }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .tabbar { background: rgb(45 23 69 / .62); box-shadow: inset 0 2px 0 rgb(255 255 255 / .16), var(--float); }
  }
  :root[data-theme="dark"] .tabbar { background: rgb(45 23 69 / .62); box-shadow: inset 0 2px 0 rgb(255 255 255 / .16), var(--float); }
}
@media (prefers-reduced-transparency: reduce) {
  .tabbar { background: var(--card) !important; -webkit-backdrop-filter: none !important; backdrop-filter: none !important; }
}
/* Landscape phones are short: tab bar to the left corner, Play beside it, one row instead of two. */
@media (orientation: landscape) and (max-height: 500px) {
  .tabbar { left: max(16px, env(safe-area-inset-left)); translate: none; }
  .bar.map-foot {
    max-width: none;
    justify-content: flex-end;
    padding-bottom: max(12px, env(safe-area-inset-bottom));
    padding-left: calc(max(16px, env(safe-area-inset-left)) + 356px);
    padding-right: max(16px, env(safe-area-inset-right));
  }
}
.tab {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 2px;
  min-width: 0;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--ink-soft);
  font: 800 .75rem/1 var(--body);
  padding: 0;
  transition: color 200ms;
}
.tab .ico { width: 24px; height: 24px; }
.tab[aria-current] { color: var(--line); }
/* The candy lozenge under the chosen tab; it slides between tabs (see showTab). */
.tab-ind {
  position: absolute;
  top: 5px;
  bottom: 5px;
  left: 5px;
  width: calc((100% - 10px) / 3);
  border: 2.5px solid var(--line);
  border-radius: 999px;
  background: linear-gradient(#ffe680, #ffc21a);
  box-shadow: inset 0 -3px 0 rgb(43 15 51 / .16), inset 0 2px 0 rgb(255 255 255 / .6);
  translate: calc(var(--i, 0) * 100%) 0;
  transition: translate 340ms var(--spring-sheet);
}
.tab .ico { transition: scale 160ms var(--out); }
.tab:active .ico { scale: .88; }

/* ---------- trophies and settings ---------- */
.tab-screen {
  scrollbar-width: thin;
  scrollbar-color: var(--ink-soft) transparent;
  position: absolute;
  inset: 0;
  z-index: 5;
  display: block;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: max(16px, env(safe-area-inset-top)) max(16px, env(safe-area-inset-right)) calc(var(--bar-space) + 16px) max(16px, env(safe-area-inset-left));
  background: var(--bg);
}
.tab-screen > * { max-width: 520px; margin-inline: auto; }
.screen-title {
  margin-top: 8px;
  margin-bottom: 18px;
  font: 400 2.4rem/1.05 var(--display);
  color: var(--cherry);
  -webkit-text-stroke: 7px var(--line);
  paint-order: stroke fill;
}
.group {
  margin-bottom: 18px;
  border: 3px solid var(--line);
  border-radius: 22px;
  background: var(--card);
  overflow: hidden; /* in-page lists sit flat: only floating things cast a shadow */
}
.group > .row, .group > .setting { min-height: 60px; padding: 12px 16px; }
.group > * + * { border-top: 2px solid rgb(43 15 51 / .12); }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .group > * + * { border-top-color: rgb(255 255 255 / .1); } }
:root[data-theme="dark"] .group > * + * { border-top-color: rgb(255 255 255 / .1); }
/* An on/off switch: the knob slides, the track turns mint when on. */
.toggle {
  position: relative;
  flex: none;
  width: 60px;
  height: 36px;
  padding: 0;
  border: 3px solid var(--line);
  border-radius: 99px;
  background: var(--track, var(--locked));
  transition: background-color 200ms;
}
.toggle::before { content: ""; position: absolute; inset: -6px -4px; } /* a 44px tap area around the small track */
.toggle::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 26px;
  height: 26px;
  border: 2.5px solid var(--line);
  border-radius: 50%;
  background: #fff;
  box-shadow: inset 0 -3px 0 rgb(43 15 51 / .14);
  transition: translate 320ms var(--spring-sheet);
}
.toggle[aria-checked="true"] { background: var(--mint); }
.toggle[aria-checked="true"]::after { translate: 24px 0; }

.trophy-total { display: flex; align-items: center; gap: 14px; padding: 14px 16px; }
.trophy-total .star { font-size: 3rem; }
.trophy-total b { display: block; font: 400 2rem/1 var(--display); font-variant-numeric: tabular-nums; }
.trophy-total small, .zone-card small { display: block; color: var(--ink-soft); font-weight: 700; font-size: .85rem; }
.zone-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 0 12px;
  width: 100%;
  padding: 12px 16px;
  border: 0;
  background: none;
  text-align: left;
}
/* Name and score take the full width; the star count sits at the end of the meter. */
.zone-card > span:nth-child(2) { display: contents; }
.zone-card .zone-badge { grid-row: span 3; }
.zone-card b, .zone-card small { grid-column: 2 / -1; }
.zone-stars { grid-column: 3; grid-row: 3; margin-top: 6px; } /* same top gap as the meter, so the two line up */
.zone-card[aria-disabled="true"] { cursor: default; }
.zone-card:not([aria-disabled]):active { background: rgb(43 15 51 / .06); }
.zone-badge {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border: 3px solid var(--line);
  border-radius: 16px;
  background: var(--sky);
}
.zone-badge .prop { position: static; width: 40px; height: 40px; margin: 0; }
.zone-card[aria-disabled="true"] .zone-badge { filter: grayscale(1); opacity: .6; }
.zone-card b { display: block; font: 400 1.15rem/1.1 var(--display); overflow-wrap: anywhere; }
.zmeter { display: block; height: 10px; margin-top: 6px; border: 2px solid var(--line); border-radius: 99px; background: var(--track, var(--bg)); overflow: hidden; }
.zmeter i { display: block; height: 100%; background: linear-gradient(#ffe680, #ffc21a); transform-origin: left; transform: scaleX(var(--f, 0)); transition: transform 600ms var(--out); }
.zone-stars { display: flex; justify-content: flex-end; min-width: 4.6em; align-items: center; gap: 3px; font: 400 1.1rem/1 var(--display); font-variant-numeric: tabular-nums; white-space: nowrap; }
.zone-stars small { display: inline; font: 700 .8rem/1 var(--body); }

/* ---------- game ---------- */
.hud { gap: 6px; justify-content: space-between; }
.moves {
  display: grid;
  justify-items: center;
  min-width: 60px;
  padding: 4px 8px;
  border: 3px solid var(--line);
  border-radius: 16px;
  background: var(--cherry);
  color: #fff;
}
.moves span { font: 400 1.6rem/1 var(--display); -webkit-text-stroke: 4px var(--line); paint-order: stroke fill; }
.moves small { font: 800 .75rem/1 var(--body); color: var(--line); } /* white on cherry is only 3.2:1 at this size */
/* Running low: a calm lemon counter instead of a throbbing one. */
.moves.low { background: var(--lemon); }
.goals { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; flex: 1; min-width: 0; }
.goal {
  position: relative;
  display: flex;
  align-items: center;
  gap: 2px;
  font: 400 1rem/1 var(--display);
}
.goal img { width: 34px; height: 34px; }
.goal.met span { color: var(--mint); }
.score { display: grid; justify-items: end; gap: 4px; min-width: 76px; font: 400 1.05rem/1 var(--display); }
.meter {
  position: relative;
  width: 76px;
  height: 12px;
  border: 2px solid var(--line);
  border-radius: 99px;
  background: var(--track, var(--card));
  overflow: hidden;
}
.meter i { position: absolute; inset: 0; transform-origin: left; transform: scaleX(0); background: var(--lemon); transition: transform 300ms var(--out); }
.meter b { position: absolute; top: 0; bottom: 0; width: 2px; background: var(--line); }
/* Past three stars the fill turns deeper gold on its way to the gold star. */
.meter.gold i { background: #ffa51f; }
.star.gold svg { fill: #ffa51f; }

.board-wrap {
  position: relative;
  flex: 1;
  min-height: 0;
  display: grid;
  place-items: center;
  padding: 14px; /* room for the board's 12px sticker edge */
}
#board {
  display: block;
  touch-action: none;
  border-radius: 14px;
  outline: 4px solid var(--line);
  box-shadow: 0 0 0 8px var(--cut);
}
/* The board's frame takes the colour of the level's tough tag. */
#game-screen[data-tough="1"] #board { box-shadow: 0 0 0 8px var(--cherry); }
#game-screen[data-tough="2"] #board { box-shadow: 0 0 0 8px var(--lemon); }

.boosters { gap: 14px; }
/* More boosters than fit: the row scrolls sideways (down, on a phone on its side) and stays centred when it fits.
   Top room keeps the count badges and the raised armed booster inside the scroll box. */
#game-screen > .boosters { justify-content: flex-start; overflow: auto; overscroll-behavior: contain; scrollbar-width: none; scroll-padding: 16px; padding-top: 16px; padding-bottom: max(14px, env(safe-area-inset-bottom)); }
#game-screen > .boosters::-webkit-scrollbar { display: none; }
.boosters > :first-child { margin-inline-start: auto; }
.boosters > :last-child { margin-inline-end: auto; }
.booster { flex: none; }
.booster {
  position: relative;
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 18px;
  background: var(--card);
  font-size: 1.6rem;
  padding: 0;
}
.booster[disabled] { opacity: .45; cursor: default; }
.booster.armed { background: var(--lemon); transform: translateY(-4px); }
.booster .count {
  position: absolute;
  right: -10px;
  top: -10px;
  min-width: 24px;
  height: 24px;
  padding: 0 5px;
  border: 2px solid var(--line);
  border-radius: 99px;
  background: var(--mint);
  color: var(--line);
  font: 800 .8rem/20px var(--body);
}
.booster .count.zero { background: var(--locked); }
/* Upgrade level: one plus per tier above the first. */
.booster .tier {
  position: absolute;
  left: -10px;
  top: -10px;
  min-width: 22px;
  height: 22px;
  padding: 0 4px;
  border: 2px solid var(--line);
  border-radius: 99px;
  background: var(--lemon);
  color: var(--line);
  font: 800 .8rem/18px var(--body);
}
/* Something waiting to be claimed. */
.icon-btn.dot { position: relative; }
.icon-btn.dot::after { content: ''; position: absolute; top: -5px; right: -5px; width: 14px; height: 14px; border: 2px solid var(--line); border-radius: 50%; background: var(--cherry); }

/* ---------- dialogs ---------- */
dialog {
  border: 0;
  padding: 0;
  background: none;
  color: var(--ink);
  width: min(380px, calc(100vw - 32px));
  max-height: calc(100dvh - 32px);
  overflow: visible;
}
/* Dim and soften what is behind, so the card reads as the one thing to deal with. */
dialog::backdrop { background: rgb(26 12 38 / .5); -webkit-backdrop-filter: blur(5px) saturate(1.2); backdrop-filter: blur(5px) saturate(1.2); animation: dim 300ms var(--out); }
@keyframes dim { from { opacity: 0; } }
dialog[open] .card { animation: enter 480ms var(--spring-ui); }
@keyframes enter { from { transform: scale(.9); opacity: 0; } }
/* Leaving retraces the way in: a card shrinks back, a sheet sinks (from wherever a drag left it). */
dialog.closing::backdrop { opacity: 0; transition: opacity 200ms; }
dialog.closing .card { animation: leave 200ms cubic-bezier(.4, 0, 1, 1) forwards; }
@keyframes leave { to { transform: scale(.94); opacity: 0; } }
@media (prefers-reduced-transparency: reduce) {
  dialog::backdrop { background: rgb(26 12 38 / .72); -webkit-backdrop-filter: none; backdrop-filter: none; }
}
@keyframes pop { from { transform: scale(.85); opacity: 0; } }
.card {
  position: relative;
  display: grid;
  gap: 14px;
  justify-items: center;
  text-align: center;
  padding: 22px 18px 20px;
  border: 4px solid var(--line);
  border-radius: 28px;
  background: var(--card);
  box-shadow: var(--float);
  max-height: calc(100dvh - 48px);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--ink-soft) transparent; /* a thin bar in the card's own colours, not the grey system one */
}
.card h2 {
  margin: 0;
  font: 400 2rem/1.05 var(--display);
  color: var(--cherry);
  -webkit-text-stroke: 6px var(--line);
  paint-order: stroke fill;
}
/* The card's title sits on a band of the same printed paper as the loading wrapper. */
.card > h2:first-child, .card > .close + h2 {
  justify-self: stretch;
  margin: -22px -18px 2px;
  padding: 18px 18px 14px;
  background: var(--print) 0 0 / 120px, var(--band, var(--wrap));
  border-bottom: 4px solid var(--line);
  color: #fff;
}
/* Room for the close button only on cards that have one. */
.card > .close + h2 { padding-inline: 56px; }
.card > h2.win { --band: var(--mint); }
/* A first-try win or a finished episode gets one line of its own under the score. */
.card .feat { font-weight: 800; }
.card .feat .crown { margin-right: 6px; font-size: 1.3em; }
.card > h2.lose { --band: var(--grape); }
.card > h2.treat { --band: var(--lemon); }
.card > h2.zoned { --band: var(--ep); } /* a level card wears its episode's colour, like its spot on the map */
.card .close {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 1;
  width: 44px;
  height: 44px;
  border-radius: 50%;
}
.card .close .ico { width: 22px; height: 22px; }
.card p { margin: 0; color: var(--ink-soft); font-weight: 600; max-width: 30ch; }
.card .where { margin-top: -8px; font-weight: 800; }
.card .big-stars { display: flex; align-items: flex-end; gap: 4px; font-size: 2.8rem; line-height: 1; }
.card .big-stars .star { animation: pop 340ms var(--pop) backwards; }
.card .big-stars .star:nth-child(2) { font-size: 3.4rem; animation-delay: 140ms; }
.card .big-stars .star:nth-child(3) { animation-delay: 280ms; }
.card .big-score { margin: -4px 0 0; font: 400 2.2rem/1 var(--display); color: var(--ink); font-variant-numeric: tabular-nums; }
.ticket {
  display: grid;
  gap: 10px;
  justify-items: center;
  width: 100%;
  padding: 12px;
  border: 3px dashed var(--line);
  border-radius: 18px;
  background: var(--bg-2);
}
.ticket p { color: var(--ink); font-weight: 800; }
.ticket b { font: 400 1.3rem/1 var(--display); }
.goal-row { display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; }
.goal-row .goal img { width: 44px; height: 44px; }
.goal-row .goal { font-size: 1.2rem; }
.picks { display: flex; flex-wrap: wrap; gap: 14px 12px; justify-content: center; }
.picks .booster { width: 56px; height: 56px; }
.picks .booster[aria-pressed="true"] { background: var(--lemon); }
.actions { display: grid; gap: 10px; width: 100%; }
.btn {
  min-height: 54px;
  padding: 8px 20px;
  border-radius: 18px;
  background: var(--mint);
  color: var(--line);
  font: 400 1.35rem/1 var(--display);
}
.btn small { display: block; margin-top: 4px; font: 700 .8rem/1 var(--body); }
.btn.alt { background: var(--card); color: var(--ink); font: 800 1rem/1 var(--body); }
.btn[disabled] { opacity: .5; cursor: default; }
.reward { display: flex; align-items: center; gap: 8px; font-weight: 800; }
.btn.small { flex: none; min-height: 44px; padding: 6px 14px; font-size: 1.05rem; }
.card h3 { margin: 4px 0 0; font: 400 1.3rem/1.2 var(--display); }
.quests { width: 100%; margin: 0; padding: 0; list-style: none; text-align: left; }
.quests li { display: flex; align-items: center; gap: 12px; padding: 12px 14px; }
.quests li > span { flex: 1; min-width: 0; display: grid; gap: 2px; }
.quests b { font-weight: 800; }
.quests small { color: var(--ink-soft); font-weight: 700; }
.quests .claimed { flex: none; }
.odds { width: 100%; text-align: left; }
.odds summary { display: flex; align-items: center; min-height: 44px; font-weight: 800; cursor: pointer; }
.odds summary::after { content: '\25BE'; margin-left: 6px; transition: transform .2s; }
.odds[open] summary::after { transform: rotate(180deg); }
.odds ul { margin: 0; padding: 0; list-style: none; }
.odds li { display: flex; justify-content: space-between; gap: 12px; padding: 4px 0; font-weight: 600; }
.reward span { flex: none; width: 2.4rem; font-size: 1.8rem; text-align: center; }
/* Treat card: seven round stamps, lemon once stamped, today's raised on its lip; day 7 is the gift. */
.stamps { display: grid; grid-template-columns: repeat(7, minmax(0, 40px)); justify-content: center; gap: 6px; width: 100%; margin: 0; padding: 0 0 4px; list-style: none; }
.stamps li { display: grid; place-items: center; aspect-ratio: 1; border: 2px solid var(--line); border-radius: 50%; background: var(--track, var(--bg)); color: var(--ink); font: 400 1rem/1 var(--display); }
.stamps li.on { background: var(--lemon); color: #2b0f33; }
.stamps li.now { transform: translateY(-3px); box-shadow: 0 3px 0 var(--line); }
.stamps svg { width: 70%; height: 70%; }
/* Chocolate boxes: four square slots that fill cocoa brown. */
.boxes { display: flex; justify-content: center; gap: 10px; margin: 0; padding: 0; list-style: none; }
.boxes li { width: 40px; height: 40px; border: 2px solid var(--line); border-radius: 10px; background: var(--track, var(--bg)); }
.boxes li.on { background: linear-gradient(135deg, #8a4b2a 0 50%, #6e3a1f 50%); box-shadow: 0 3px 0 var(--line); }

.booster.locked { background: var(--locked); box-shadow: none; border-color: var(--ink-soft); font-size: 1.2rem; }
.booster.locked .count { background: var(--card); color: var(--ink); right: 50%; transform: translateX(50%); top: auto; bottom: -12px; white-space: nowrap; }
.reward-list { display: grid; gap: 8px; text-align: left; }
.reward small { display: block; font-weight: 600; color: var(--ink-soft); }
.intro { display: grid; gap: 10px; justify-items: center; }
.intro img { width: 72px; height: 72px; }
.intro .title { font: 400 1.6rem/1 var(--display); color: var(--ink); }
.actions .pair { display: grid; grid-auto-columns: 1fr; grid-auto-flow: column; gap: 10px; }
.actions .pair .btn { padding-inline: 8px; }

/* Help pages: each line pairs the game's own candy picture with one plain sentence. */
.card > .seg { width: 100%; }
.card > .seg button { padding: 0 4px; font-size: .9rem; }
/* Every tab sits in the same grid cell, so the sheet keeps the tallest tab's height and never jumps. */
.help-panels { display: grid; width: 100%; }
.help-panel { grid-area: 1 / 1; display: grid; gap: 12px; align-content: start; width: 100%; justify-items: start; text-align: left; }
.help-panel[inert] { visibility: hidden; } /* inert hides it from screen readers and Tab; it still holds its height */
.help-panel > p { max-width: none; }
.card h3 { justify-self: stretch; margin: 8px 0 -4px; text-align: left; font: 400 1.25rem/1.2 var(--display); color: var(--ink); }
.howto { display: grid; gap: 12px; width: 100%; margin: 0; padding: 0; list-style: none; text-align: left; }
.howto li { display: grid; grid-template-columns: 64px 1fr; gap: 12px; align-items: center; color: var(--ink-soft); font-weight: 600; overflow-wrap: anywhere; }
.howto b { color: var(--ink); font-weight: 800; }
.howto a { color: var(--ink); font-weight: 800; }
.howto b.t { display: block; }
.howto .pics { display: flex; justify-content: center; }
.howto .pics img { width: 40px; height: 40px; }
/* Two or three candies overlap like stickers: a combo, or the start boosters. */
.howto .pics img:not(:only-child) { width: 34px; height: 34px; transform: rotate(-6deg); }
.howto .pics img + img { margin-left: -10px; transform: rotate(8deg); }
.howto .pics img:nth-last-child(3), .howto .pics img:nth-last-child(3) ~ img { width: 26px; height: 26px; }
.howto .pics .ico, .howto .pics > svg { width: 36px; height: 36px; }
.howto .pics .star { font-size: 34px; }
/* What's new: plain bullet lines; save code: a sticker-outlined box the code wraps inside. */
.news { display: grid; gap: 8px; width: 100%; margin: 0; padding-left: 1.2em; text-align: left; color: var(--ink-soft); font-weight: 600; }
.news li::marker { color: var(--cherry); }
.card .code {
  width: 100%;
  padding: 10px 12px;
  border: 3px solid var(--line);
  border-radius: 14px;
  background: var(--bg);
  color: var(--ink);
  font: 600 .8125rem/1.35 var(--body);
  word-break: break-all;
  resize: none;
}
.card .code[readonly] { color: var(--ink-soft); }
.card .code:not([readonly]) { font-size: 1rem; } /* 16px, so phones do not zoom in when it is tapped */
.card .note { max-width: none; color: var(--ink); font-weight: 800; }
.card .tip { max-width: 32ch; font-size: .875rem; }
.card .tip b { margin-right: 4px; padding: 1px 8px; border: 2px solid var(--line); border-radius: 99px; background: var(--lemon); color: #2b0f33; font-weight: 800; }
/* Phones: the level card and settings rise from the bottom, where the thumb already is. */
@media (max-width: 599px) {
  dialog.sheet {
    margin: auto 0 0;
    width: 100%;
    max-width: 100%;
    max-height: calc(100dvh - env(safe-area-inset-top, 0px) - 24px);
  }
  dialog.sheet .card {
    border-bottom: 0;
    border-radius: 28px 28px 0 0;
    padding-bottom: calc(20px + env(safe-area-inset-bottom, 0px));
    max-height: inherit;
    box-shadow: none;
  }
  dialog.sheet[open] .card { animation: rise 390ms var(--spring-sheet); }
  dialog.sheet.dragging .card { animation: none; } /* before .closing, so a dragged sheet can still sink */
  dialog.sheet.closing .card { animation: sink 220ms cubic-bezier(.4, 0, 1, 1) forwards; }
  /* The grab bar: drag the title band down to put the sheet away. */
  dialog.sheet .card::before {
    content: "";
    position: absolute;
    top: 7px;
    left: 50%;
    z-index: 1;
    width: 40px;
    height: 5px;
    margin-left: -20px;
    border-radius: 3px;
    background: rgb(255 255 255 / .7);
  }
  dialog.sheet .card > .close + h2 { touch-action: none; cursor: grab; }
}
@keyframes rise { from { transform: translateY(100%); } }
@keyframes sink { to { transform: translateY(100%); } }

/* ---------- settings ---------- */
.setting { display: grid; gap: 8px; width: 100%; justify-items: stretch; text-align: left; }
.setting > span { font-weight: 800; }
.seg { display: grid; grid-auto-columns: 1fr; grid-auto-flow: column; gap: 6px; } /* any number of equal options */
.seg button {
  min-height: 44px;
  border: 3px solid var(--line);
  border-radius: 14px;
  background: var(--card);
  font-weight: 800;
  padding: 0 8px;
}
.seg button[aria-checked="true"], .seg button[aria-selected="true"] { background: var(--lemon); color: var(--line); }
.row { display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; font-weight: 800; }
.row small { display: block; margin-top: 2px; color: var(--ink-soft); font-size: .8125rem; font-weight: 600; }
:where(.row.link) { border: 0; background: none; text-align: left; } /* zero weight, so the group's dividers still show */
.row.link:focus-visible { outline-offset: -5px; } /* the group clips anything outside it */
.row.link .ico { flex: none; width: 22px; height: 22px; transform: scaleX(-1); color: var(--ink-soft); }
.group-title { margin: 26px 6px 10px; font: 400 1.25rem/1.2 var(--display); }
.pledge { margin-top: 0; padding: 0; list-style: none; }
.pledge li { display: grid; gap: 2px; padding: 12px 16px; }
.pledge b { font-weight: 800; }
.pledge span { color: var(--ink-soft); font-weight: 600; font-size: .9rem; }

/* ---------- wrapper (loading and level changes) ---------- */
.curtain { position: fixed; inset: 0; z-index: 50; pointer-events: none; }
.curtain.shut { pointer-events: auto; }
.flap {
  position: absolute;
  left: 0;
  right: 0;
  height: 50%;
  background: var(--print) 0 0 / 120px, var(--wrap);
  transition: transform 560ms cubic-bezier(.3, .9, .3, 1.04);
}
.flap.up { top: 0; z-index: 1; transform: translateY(calc(-100% - 16px)); box-shadow: inset 0 -26px 22px -20px rgb(43 15 51 / .35); }
.flap.down { bottom: 0; transform: translateY(calc(100% + 16px)); box-shadow: inset 0 26px 22px -20px rgb(43 15 51 / .35); }
/* Crimped edges where the two halves meet; the top teeth sit over the bottom ones. */
.flap::after { content: ""; position: absolute; left: 0; right: 0; height: 11px; background-size: 16px 11px; background-repeat: repeat-x; }
.flap.up::after {
  top: 100%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='11'%3E%3Cpath d='M0 0H16V1.5L8 9.5L0 1.5Z' fill='%23ff4d6d'/%3E%3Cpath d='M0 1.5L8 9.5L16 1.5' fill='none' stroke='%232b0f33' stroke-width='2.6' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.flap.down::after {
  bottom: 100%;
  background-position: 8px 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='11'%3E%3Cpath d='M0 11H16V9.5L8 1.5L0 9.5Z' fill='%23ff4d6d'/%3E%3Cpath d='M0 9.5L8 1.5L16 9.5' fill='none' stroke='%232b0f33' stroke-width='2.6' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.curtain.shut .flap { transform: none; transition: transform 300ms cubic-bezier(.55, 0, .8, .2); }

.seal {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 2;
  display: grid;
  justify-items: center;
  width: max-content;
  max-width: calc(100vw - 32px);
  transform: translate(-50%, -50%) scale(.6);
  opacity: 0;
  transition: transform 200ms var(--out), opacity 160ms;
}
.curtain.shut .seal { transform: translate(-50%, -50%); opacity: 1; transition: transform 380ms var(--pop) 120ms, opacity 120ms 120ms; }
.booting.shut .seal { transition: none; }
/* Loading shows the app icon's wrapped heart (same spot as the phone's own launch screen), then the wordmark. */
.seal-heart, .wordmark, .load { display: none; }
.booting .seal-heart { display: block; width: 160px; height: 110px; animation: thump 900ms var(--pop) 80ms both; }
@keyframes thump { 0% { transform: scale(.82); } 35% { transform: scale(1.08); } 60% { transform: scale(.97); } 100% { transform: none; } }
.booting .wordmark {
  display: grid;
  justify-items: center;
  margin: -16px 0 0;
  font: 400 clamp(2.8rem, 15vw, 4.2rem)/.86 var(--display);
  color: #fff;
  -webkit-text-stroke: 11px var(--line);
  paint-order: stroke fill;
  text-shadow: 0 7px 0 var(--line);
  opacity: 0;
  transform: rotate(-5deg) scale(1.5);
}
.wordmark span:last-child { margin-left: .6em; }
/* The wordmark slaps on once its font is in, so the stand-in face never shows. */
.booting.ready .wordmark { opacity: 1; transform: rotate(-5deg); transition: transform 420ms var(--pop), opacity 120ms; }
.seal-label {
  margin: 0;
  padding: 8px 22px 9px;
  border: 3px solid var(--line);
  border-radius: 999px;
  background: var(--card);
  color: var(--ink);
  font: 400 1.6rem/1 var(--display);
  box-shadow: var(--float);
  white-space: nowrap;
}
.seal-label:empty, .booting .seal-label { display: none; }
.booting .load {
  display: block;
  width: 180px;
  height: 18px;
  margin-top: 30px;
  border: 3px solid var(--line);
  border-radius: 99px;
  background: #fff;
  overflow: hidden;
}
.load i { display: block; height: 100%; background: var(--lemon); transform-origin: left; transform: scaleX(var(--p, 0)); transition: transform 300ms var(--out); }

/* ---------- tutorial coach ---------- */
.coach {
  position: absolute;
  left: 50%;
  top: 8px;
  transform: translateX(-50%);
  z-index: 2;
  width: min(340px, calc(100% - 32px));
  padding: 10px 14px;
  border: 3px solid var(--line);
  border-radius: 18px;
  background: var(--card);
  box-shadow: var(--float);
  font-weight: 800;
  text-align: center;
  pointer-events: none;
}
.coach.low { top: auto; bottom: 8px; }

.toast {
  position: fixed;
  left: 50%;
  bottom: calc(108px + env(safe-area-inset-bottom));
  transform: translate(-50%, 20px);
  opacity: 0;
  pointer-events: none;
  padding: 10px 16px;
  border: 3px solid var(--line);
  border-radius: 16px;
  background: var(--card);
  font-weight: 800;
  width: max-content;
  max-width: calc(100vw - 32px);
  text-align: center;
  transition: opacity 200ms, transform 200ms var(--out);
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
/* A toast with a button: tappable, laid out in one line with the button at the end. */
.toast.act { display: flex; align-items: center; gap: 12px; padding: 6px 6px 6px 16px; text-align: left; }
.toast.act.show { pointer-events: auto; }
#app:not(.playing) .toast { bottom: calc(var(--bar-space) + 84px); }

@media (hover: hover) and (pointer: fine) {
  .icon-btn:hover, .btn:not([disabled]):hover, .booster:not([disabled]):hover { filter: brightness(1.06); }
  .node:not(.locked):hover { transform: scale(1.06); }
}
@media (max-width: 360px) {
  .goal img { width: 28px; height: 28px; }
  .score, .meter { min-width: 0; width: 60px; }
  .moves { min-width: 52px; }
  .boosters { gap: 10px; }
  .booster { width: 50px; height: 50px; }
}
/* Phone on its side: score down the left, boosters down the right, so the board gets the full height. */
@media (orientation: landscape) and (max-height: 560px) {
  #game-screen { display: grid; grid-template: minmax(0, 1fr) / auto minmax(0, 1fr) auto; }
  #game-screen > .bar { flex-direction: column; justify-content: center; width: auto; margin: 0; }
  #game-screen > .hud { padding: max(8px, env(safe-area-inset-top)) 4px max(8px, env(safe-area-inset-bottom)) max(12px, env(safe-area-inset-left)); }
  #game-screen > .boosters { padding: 16px max(16px, env(safe-area-inset-right)) max(14px, env(safe-area-inset-bottom)) 12px; }
  .hud .goals { flex: none; flex-direction: column; }
  .score { justify-items: center; }
  .boosters { gap: 12px; }
  .booster { width: 50px; height: 50px; }
  .boosters > :first-child { margin: auto 0 0; }
  .boosters > :last-child { margin: 0 0 auto; }
  .toast { bottom: calc(16px + env(safe-area-inset-bottom)); }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 1ms !important; animation-iteration-count: 1 !important; transition-duration: 1ms !important; }
  /* No sliding wrapper: it fades in and out instead, so the change still reads. */
  .curtain .flap { transform: none !important; }
  .curtain { transition: opacity 200ms ease !important; }
  .curtain:not(.shut) { opacity: 0; }
}
