/* Fredoka (SIL OFL, app/vendor/fredoka/OFL.txt), Latin subset with å ä ö. The fallback stack in --font shows until it loads. */
@font-face{font-family:"Fredoka";font-style:normal;font-weight:500;font-display:swap;src:url(vendor/fredoka/fredoka-latin-500-normal.woff2) format("woff2")}
@font-face{font-family:"Fredoka";font-style:normal;font-weight:600;font-display:swap;src:url(vendor/fredoka/fredoka-latin-600-normal.woff2) format("woff2")}
@font-face{font-family:"Fredoka";font-style:normal;font-weight:700;font-display:swap;src:url(vendor/fredoka/fredoka-latin-700-normal.woff2) format("woff2")}
/* Design system tokens: docs/superpowers/specs/2026-09-28-design-system-design.md. Provisional until a designer vets them.
   Colours live only here and in the world blocks below (test/tokens.test.mjs). */
:root{
  /* brand */
  --ink:#1E2A3A; --dark:#1E2A3A; --pim:#7B61FF; --pimD:#5A43C9; --sky:#CFE8EF; --paper:#FDFCF8; --white:#FFFFFF;
  /* game, fixed in every world */
  --sun:#FFC43D; --sunD:#C99212; --sunFade:rgba(255,196,61,.35); --veil:rgba(20,24,50,.6); --go:#06A77D; --goD:#04765A; --warn:#EF476F;
  --up:#4EA5D9; --down:#F28C28; --left:#E4579A; --right:#3BB273;
  /* world: Pim's, overridden by each [data-theme] below and checked by test/palettes.test.mjs */
  --bg:var(--sky); --board:#C98B4B; --boardD:#9A6532; --tile:var(--paper); --trail:var(--pim); --trailLine:#4824FF;
  /* type: Fredoka for adult text only; the child's interface has no words */
  --font:"Fredoka",ui-rounded,"SF Pro Rounded","Arial Rounded MT Bold","Trebuchet MS",system-ui,sans-serif;
  --t-display:clamp(2.6rem,6vw,4.5rem); --t-title:clamp(1.8rem,3.6vw,2.8rem); --t-lead:clamp(1.25rem,2.4vw,1.8rem);
  --t-body:clamp(1.05rem,1.7vw,1.3rem); --t-small:clamp(.9rem,1.3vw,1.05rem);
  /* shape and depth: only pressable things (and the board surface) have a hard shadow */
  --r-s:12px; --r-m:20px; --r-l:28px; --r-pill:999px;
  --press-s:4px; --press-m:6px; --press-l:8px;
  /* motion: game.js reads the durations with cssMs() */
  --ease-pop:cubic-bezier(.4,1.6,.5,1); --ease-glide:cubic-bezier(.3,.1,.3,1);
  --d-tap:150ms; --d-pop:300ms; --d-step:650ms; --d-fly:1300ms; --d-stagger:200ms;
  font-family:var(--font);
  box-sizing:border-box;
  padding-top:env(safe-area-inset-top,0px); padding-bottom:env(safe-area-inset-bottom,0px);
}
[data-theme="dino"]{--bg:#DCEBC6; --board:#6B8F3A; --boardD:#4A6628; --tile:#F6F8EA; --trail:#E0892B; --trailLine:#B46A1A}
[data-theme="luna"]{--bg:#F7E1F2; --board:#B98AD6; --boardD:#8C5FAE; --tile:#FFFAFE; --trail:#FF8FC7; --trailLine:#FF48A3}
[data-theme="kosmo"]{--ink:#F4F1FF; --bg:#1B1F4A; --board:#5558B9; --boardD:#3D4094; --tile:#2A2F6B; --trail:#FFC43D; --trailLine:#FFD470}
[data-theme="robo"]{--bg:#DDE3EA; --board:#5B6B7F; --boardD:#3F4C5C; --tile:#F3F6F9; --trail:#FF7A3D; --trailLine:#FF5000}
[data-theme="mimi"]{--bg:#F4E6C8; --board:#8A5A2B; --boardD:#633E1B; --tile:#FFF8EA; --trail:#E2A93B; --trailLine:#BA841C}
[data-theme="prick"]{--bg:#E3F1D8; --board:#7FB24A; --boardD:#5A8733; --tile:#FBFFF5; --trail:#E63946; --trailLine:#C81926}
[data-theme="bullo"]{--bg:#E7E0F7; --board:#4FB3A9; --boardD:#34847C; --tile:#FFFFFF; --trail:#9B5DE5; --trailLine:#7A28DD}
[data-theme="tuta"]{--bg:#E6E9EE; --board:#4A4F5A; --boardD:#30343C; --tile:#EEF0F3; --trail:#FFD23F; --trailLine:#AA8200}
*,*::before,*::after{box-sizing:inherit}
[hidden]{display:none!important}
html,body{height:100%;margin:0}
body{background:var(--bg);font-weight:500;color:var(--ink);overflow:hidden;-webkit-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;transition:background .6s}
[data-theme="kosmo"] body{background:radial-gradient(1.5px 1.5px at 10% 20%,rgba(255,255,255,.53),transparent),radial-gradient(1.5px 1.5px at 70% 10%,rgba(255,255,255,.53),transparent),radial-gradient(2px 2px at 40% 80%,rgba(255,255,255,.4),transparent),radial-gradient(1.5px 1.5px at 85% 60%,rgba(255,255,255,.53),transparent),radial-gradient(1px 1px at 25% 55%,rgba(255,255,255,.6),transparent),var(--bg)}
button{font:inherit;color:inherit;border:0;cursor:pointer;background:none}
button:focus-visible{outline:6px solid var(--sun);outline-offset:4px}
.screen{display:none;height:100%;flex-direction:column;align-items:center;justify-content:center;gap:clamp(.5rem,2.4vh,1.4rem);padding:clamp(.6rem,3vh,1.5rem);text-align:center}
.screen.on{display:flex}
h1{font-size:var(--t-display);margin:0;line-height:.95;font-weight:700;letter-spacing:-.02em}
.lead{margin:0;font-size:var(--t-lead);font-weight:600;max-width:34ch}
.small{margin:0;font-size:var(--t-small);opacity:.7;word-break:break-all;max-width:60ch}
.btn{background:var(--tile);color:var(--ink);border-radius:var(--r-m);padding:.7em 1.4em;font-size:var(--t-lead);font-weight:600;box-shadow:0 var(--press-m) 0 var(--boardD)}
.btn.yes{background:var(--go);color:var(--white);font-weight:700;box-shadow:0 var(--press-m) 0 var(--goD)}
.btn:active{transform:translateY(var(--press-m));box-shadow:0 0 0 var(--boardD)}
.btn.quiet{background:transparent;box-shadow:none;font-size:var(--t-small);text-decoration:underline;opacity:.7}

/* tv: pairing */
.qr{background:var(--white);padding:18px;border-radius:var(--r-m);width:min(36vh,300px,70vw);aspect-ratio:1;display:grid;place-items:center}
.qr img,.qr canvas{width:100%!important;height:auto!important;image-rendering:pixelated}

/* tv: pick adventure */
.cast{display:flex;flex-wrap:wrap;justify-content:center;gap:clamp(.6rem,2.5vw,2rem);align-items:flex-end;max-width:min(92vw,1100px)}
.cast div{width:clamp(64px,9vw,120px);animation:bob 2.4s ease-in-out infinite}
.cast div:nth-child(3n+2){animation-delay:.3s}.cast div:nth-child(3n){animation-delay:.6s}.cast div:nth-child(4n){animation-delay:.9s}
@keyframes bob{50%{transform:translateY(-10px)}}

/* tv: game */
.tvtop{display:flex;align-items:center;justify-content:space-between;width:min(92vw,calc(64vh*5/4));font-size:var(--t-lead);font-weight:700}
.dots{display:flex;gap:.6rem}
.dots i{width:clamp(14px,2vw,26px);aspect-ratio:1;border-radius:50%;background:var(--tile);display:block;opacity:.8}
.dots i.done{background:var(--sun);opacity:1}
.dots i.now{background:var(--tile);outline:4px solid var(--trail);opacity:1;transform:scale(1.2)}
.board{position:relative;width:min(92vw,calc(64vh*5/4));aspect-ratio:5/4;background:var(--board);border-radius:var(--r-l);padding:14px;box-shadow:0 14px 0 var(--boardD);transition:background .6s}
.grid{position:relative;width:100%;height:100%;display:grid;grid-template-columns:repeat(5,1fr);grid-template-rows:repeat(4,1fr);gap:6px}
.cell{background:var(--tile);border-radius:var(--r-s);transition:background .6s}
.token{position:absolute;left:0;top:0;width:calc((100% - 24px)/5);height:calc((100% - 18px)/4);display:grid;place-items:center;transition:transform .5s var(--ease-pop)}
.token svg{width:80%;height:80%;transition:transform .25s}
/* The trail dot keeps its world colour; the ring in --trailLine carries the contrast against the tile (spec 4). */
.trail::after{content:"";width:22%;aspect-ratio:1;border-radius:50%;background:var(--trail);box-shadow:0 0 0 .35vmin var(--trailLine);animation:pop .3s}
@keyframes pop{from{transform:scale(0)}}
#hero{z-index:3}
/* handoff part 2, 4: the character has jumped to the pad; a faded copy stays where it stopped */
#hero.away{visibility:hidden}
.ghostHero{opacity:.35;z-index:2}
/* handoff part 2, 5: the tv goes quiet. Nothing on the board or in the row loops, and both dim. */
#tvGame .board,#tvProg{transition:background .6s,opacity .4s}
#tvGame.quiet .board,#tvGame.quiet #tvProg{opacity:.6}
#tvGame.quiet #grid *,#tvGame.quiet #grid *::before,#tvGame.quiet #grid *::after,#tvGame.quiet #tvProg,#tvGame.quiet #tvProg *,#tvGame.quiet #tvProg *::before,#tvGame.quiet #tvProg *::after{animation:none!important}
/* guess first: a question mark over the character */
#hero.think::before{content:"?";position:absolute;top:-38%;right:-8%;width:44%;aspect-ratio:1;border-radius:50%;background:var(--tile);color:var(--ink);display:grid;place-items:center;font-weight:700;font-size:var(--t-title);animation:pop .3s}
#hero svg{position:relative}
/* the svg's transform holds the facing (face() in game.js), so the hop moves it with top */
#hero.hop svg{animation:hop .5s}
#goal svg{animation:glow 2s ease-in-out infinite}
#goal.win svg{animation:spin 1s ease-in-out}
@keyframes hop{40%{top:-14%}}
@keyframes glow{50%{transform:scale(1.08)}}
@keyframes spin{to{transform:rotate(360deg) scale(1.25)}}
.banner{position:fixed;left:0;right:0;margin:auto;width:max-content;max-width:90vw;top:10vh;background:var(--sun);color:var(--dark);font-size:var(--t-title);font-weight:700;padding:.4em 1.1em;border-radius:var(--r-pill);z-index:5;animation:drop .4s var(--ease-pop)}
@keyframes drop{from{transform:translateY(-60px);opacity:0}}
.parent{max-width:min(60ch,90vw);text-align:center;margin-top:.4rem;font-size:var(--t-small);font-weight:600;background:var(--tile);background:color-mix(in srgb,var(--tile) 85%,transparent);color:var(--ink);padding:.6em .9em;border-radius:var(--r-m);opacity:.9}
.parent b{display:block;font-size:.75em;opacity:.65;font-weight:500}
/* handoff part 2, 6: the line that asks the adult to point, with a small pad */
.parent .turnLine{display:flex;gap:.6em;align-items:center;margin-top:.5em;padding-top:.5em;border-top:1px dashed rgba(0,0,0,.2)}
.parent .turnLine[hidden]{display:none}
.turnLine i{flex:none;width:1.6em;height:1.2em;border-radius:.25em;background:var(--dark);display:grid;place-items:center}
.turnLine i::after{content:"";width:.75em;height:.55em;border-radius:.15em;background:var(--pim)}
/* tv: game layout (spec 8.1). Wide screens: board and program row at full height, a sidebar with the
   name, the dots and the adult text. Narrow screens (4:3 and below): the same pieces stacked. */
#tvGame.on{display:grid;grid-template-columns:minmax(0,1fr) clamp(200px,22vw,360px);grid-template-rows:auto minmax(0,1fr);grid-template-areas:"play top" "play note";gap:2vh 2.5vw;padding:3vh 2.5vw;align-items:start;justify-items:stretch;text-align:left}
#tvGame .tvtop{grid-area:top;width:auto;flex-wrap:wrap;gap:.8rem;padding-right:64px}
#tvGame .playArea{grid-area:play;height:100%;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2.5vh}
#tvGame .parent{grid-area:note;align-self:end;margin:0;max-width:none;text-align:left;opacity:.75}
#tvGame .board{width:min(100%, calc((94vh - 16vh) * 5 / 4))}
/* the row's width beside the sidebar. The row is centred, so the pad picture (.hand, 16vh) to its right
   takes its room from both sides. */
#tvGame{--rowW:calc(100vw - clamp(200px,22vw,360px) - 7.5vw - 32vh)}
@media (max-aspect-ratio: 4/3){
  #tvGame{--rowW:calc(92vw - 32vh)}
  #tvGame.on{grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto;grid-template-areas:"top" "play" "note";justify-items:center;text-align:center}
  #tvGame .tvtop{width:min(92vw, calc(54vh * 5 / 4));padding-right:0}
  #tvGame .parent{align-self:auto;text-align:center}
  #tvGame .board{width:min(92vw, calc((94vh - 32vh) * 5 / 4))}
}
/* Portrait: the pad picture goes under the row instead of beside it, so the row gets the full width
   and the board leaves room for the picture below. */
@media (orientation: portrait){
  #tvGame{--rowW:92vw}
  #tvGame .board{width:min(92vw, calc((94vh - 50vh) * 5 / 4))}
  #tvGame .hand{left:50%;bottom:auto;top:100%;margin:1vh 0 0 -6.5vh}
  #tvGame .playArea{justify-content:flex-start}
}
/* the program on the tv: a row of card slots under the board */
.prog{display:flex;gap:1vh;padding:1vh;border-radius:2vh;background:var(--tile);background:color-mix(in srgb,var(--tile) 60%,transparent)}
/* Square slots, 10vh at most, smaller when maxCards of them do not fit the width left for the row (--rowW). */
.pslot{position:relative;--s:min(10vh,calc((var(--rowW,92vw) - 2vh - (var(--n,5) - 1) * 1vh) / var(--n,5)));width:var(--s);height:var(--s);border-radius:1.6vh;background:rgba(0,0,0,.07);display:grid;place-items:center}
.pcard{background:var(--c)}
.pcard svg{width:70%;height:70%}
.pslot.now{outline:.6vh solid var(--sun);outline-offset:.4vh}
/* handoff on the tv (handoff spec 5): the pad rises beside the row and bobs; chevrons fall into it */
.progLine{position:relative}
.hand{position:absolute;left:100%;bottom:0;margin-left:3vh;width:13vh;height:15vh;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;gap:.6vh;color:var(--ink)}
.hand .chev{width:3.4vh;height:3.4vh;opacity:0}
.hand .mini{width:13vh;height:9.75vh;border-radius:1.4vh;background:var(--dark);box-shadow:0 0 0 .4vh var(--ink);padding:1vh;display:flex;align-items:center;justify-content:center}
.miniBtn{width:7.4vh;height:7.4vh;border-radius:1.6vh;display:flex;align-items:center;justify-content:center;background:var(--pim);box-shadow:0 .5vh 0 var(--pimD)}
.miniBtn.next{background:var(--sun);box-shadow:0 .5vh 0 var(--sunD)}
.miniBtn>svg{width:85%;height:85%}
.miniBtn .nextIcon{height:80%;gap:.3vh}
.miniBtn .nextIcon>svg{width:1.6vh;height:3vh}
.miniBtn .goalDisc{width:4.4vh;height:4.4vh}
.hand.in .mini{animation:handIn .7s var(--ease-pop) both,bob 2.4s ease-in-out .7s infinite}
.hand.again .mini{animation:handAgain .6s ease-out,bob 2.4s ease-in-out .6s infinite}
.hand.in .chev,.hand.again .chev{animation:chevFall .65s ease-in 2}
@keyframes handIn{from{transform:translateY(16vh);opacity:0}}
@keyframes handAgain{40%{transform:translateY(-2.5vh)}}
@keyframes chevFall{0%{opacity:0;transform:translateY(-1.2vh)}40%{opacity:1}100%{opacity:0;transform:translateY(1.2vh)}}
/* the next level picture: an arrow toward the world's goal on a white disc (tv and pad) */
.nextIcon{display:flex;align-items:center;justify-content:center;gap:6px;height:64px}
.nextIcon>svg{width:24px;height:36px}
.goalDisc{width:60px;height:60px;border-radius:50%;background:var(--white);display:flex;align-items:center;justify-content:center}
.goalDisc svg{width:80%;height:80%}
.confetti{position:fixed;top:-20px;width:14px;height:14px;border-radius:3px;z-index:6;pointer-events:none;animation:fall 2.2s ease-in forwards}
@keyframes fall{to{transform:translate(var(--dx),110vh) rotate(720deg)}}
.status{position:fixed;right:1rem;bottom:1rem;width:14px;height:14px;border-radius:50%;background:var(--warn)}
.status.ok{background:var(--go)}

/* tv: asleep */
.sleep{position:relative;width:clamp(120px,min(24vw,30vh),300px)}
.sleep .zz{position:absolute;right:-10%;top:-10%;font-size:clamp(2rem,5vw,4rem);font-weight:700;animation:zz 2.5s ease-in-out infinite}
@keyframes zz{50%{transform:translate(10px,-14px);opacity:.5}}
.sleep svg{transform:rotate(-8deg)}

/* scanning the tv's code (store apps) */
.scanView{position:relative;width:min(80vw,55vh);height:min(80vw,55vh);border-radius:var(--r-l);overflow:hidden;background:var(--dark)}
.scanView video{width:100%;height:100%;object-fit:cover;display:block}
.scanFrame{position:absolute;top:14%;right:14%;bottom:14%;left:14%;border:6px solid var(--white);border-radius:var(--r-m)}

/* dialog */
.veil{position:fixed;top:0;right:0;bottom:0;left:0;background:var(--veil);display:grid;place-items:center;z-index:10}
.dialog{background:var(--bg);border-radius:var(--r-l);padding:2rem 2.5rem;display:flex;flex-direction:column;gap:1.5rem;align-items:center;max-width:90vw}
.dialog .row{display:flex;gap:1rem}

/* pad */
/* the adult button sits in the top right corner: the row starts below it so no slot is ever covered */
#pad{justify-content:space-between;padding-top:calc(4.4rem + env(safe-area-inset-top,0px))}
/* The pad's row: exactly the world's maxCards slots on one line, cards shrink to fit (spec 8.2, #32). */
/* --wide and --narrow come from game.js: all slots on one line, or on two lines when there are many. */
.strip{--cols:var(--wide,5);flex:none;width:100%;max-width:760px;background:var(--board);border-radius:var(--r-l);padding:10px;display:grid;grid-template-columns:repeat(var(--cols,5),minmax(0,120px));justify-content:center;gap:8px;box-shadow:0 8px 0 var(--boardD)}
.slot{position:relative;width:100%;aspect-ratio:1;border-radius:var(--r-s);background:rgba(255,255,255,.3);background:color-mix(in srgb,var(--tile) 35%,transparent);display:grid;place-items:center;transition:transform .15s}
.slot.ghost{background:transparent;outline:3px dashed rgba(255,255,255,.85);outline-offset:-3px}
.slot.ghost.now{outline-color:var(--sun)}
.slot.card{background:var(--c);animation:snap .25s var(--ease-pop)}
@keyframes snap{from{transform:scale(.4)}}
.slot.card.now{outline:6px solid var(--sun);transform:scale(1.14)}
@keyframes shake{25%{transform:translateX(-6px)}75%{transform:translateX(6px)}}
.slot svg{width:70%;height:70%}
.dpad{--cell:min(26vw,124px,calc((100vh - 24rem - 20px) / 3));display:grid;grid-template-columns:repeat(3,var(--cell));grid-template-rows:repeat(3,var(--cell));gap:10px}
.arrow{border-radius:var(--r-l);background:var(--c);box-shadow:0 var(--press-l) 0 rgba(0,0,0,.25);display:grid;place-items:center}
.arrow:active{transform:translateY(var(--press-l));box-shadow:0 0 0 rgba(0,0,0,.25)}
.arrow svg{width:62%;height:62%}
.a-up{grid-area:1/2}.a-left{grid-area:2/1}.a-right{grid-area:2/3}.a-down{grid-area:3/2}
.center{grid-area:2/2;display:grid;place-items:center}
.center svg{width:78%}
/* handoff part 2, 4.1: the character is on the tv while the turn is there */
.center.away{visibility:hidden}
.actions{display:flex;gap:1rem;width:100%;max-width:760px}
/* the one big button: send (green, up), step (yellow), fetch (purple, down, pulsing), next (yellow) */
.go{flex:1;height:96px;border-radius:var(--r-l);display:grid;place-items:center;background:var(--go);box-shadow:0 var(--press-l) 0 var(--goD);transition:background .4s}
.go svg{width:56px;height:56px;transition:transform .4s}
.go[data-mode="step"],.go[data-mode="next"]{background:var(--sun);box-shadow:0 var(--press-l) 0 var(--sunD)}
.clear{width:96px;height:96px;border-radius:var(--r-l);background:var(--tile);box-shadow:0 var(--press-l) 0 var(--boardD);display:grid;place-items:center}
.clear svg{width:48px}
/* step mode: one card per tap */
.step{width:96px;height:96px;border-radius:var(--r-l);background:var(--sun);box-shadow:0 var(--press-l) 0 var(--sunD);display:grid;place-items:center}
.step svg{width:44px}
.step:active{transform:translateY(var(--press-l));box-shadow:none}
.go:active,.clear:active{transform:translateY(var(--press-l));box-shadow:none}
.go[disabled]{opacity:.4}
.pad-busy .arrow,.pad-busy .clear,.pad-busy .step{pointer-events:none;opacity:.25}
.pad-busy .slot{pointer-events:none;opacity:.7}
/* handoff on the pad (handoff spec 6): look up, then the call */
.go[data-mode="fetch"]{background:var(--pim);box-shadow:0 var(--press-l) 0 var(--pimD)}
.go[data-mode="look"]{background:transparent;box-shadow:none}
.go[data-mode="look"][disabled]{opacity:1}
.go[data-mode="look"] svg,.go[data-mode="fetch"] svg{width:72px;height:72px}
.lookChev{animation:lookUp 1.4s ease-in-out infinite}
@keyframes lookUp{0%{opacity:0;transform:translateY(6px)}40%{opacity:1}100%{opacity:0;transform:translateY(-6px)}}
.pad-call .clear{display:none}
/* the next level call: the outlines in the row glow gold like the cards on the tv (handoff spec 6.1) */
#pad[data-call="next"].pad-call .slot.ghost{outline-color:var(--sun)}
/* the next level picture on the pad's big button: larger than the base size, and not sized by .go svg */
.go .nextIcon{height:72px;gap:8px}
.go .nextIcon>svg{width:32px;height:48px}
.go .goalDisc{width:68px;height:68px}
.go .goalDisc svg{width:80%;height:80%}
#pad{position:relative}
#pad[data-call="fetch"]{--glow:var(--pim)}
#pad[data-call="next"]{--glow:var(--sun)}
.padGlow{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;z-index:2}
.call-loud .padGlow,.call-quiet .padGlow{animation:padGlow .9s ease-out}
.call-soft .padGlow{animation:padGlowSoft .9s ease-out}
@keyframes padGlow{0%{box-shadow:inset 0 0 0 0 var(--glow)}35%{box-shadow:inset 0 0 0 4vmin var(--glow)}100%{box-shadow:inset 0 0 0 0 var(--glow)}}
@keyframes padGlowSoft{0%{box-shadow:inset 0 0 0 0 var(--glow)}35%{box-shadow:inset 0 0 0 2vmin var(--glow)}100%{box-shadow:inset 0 0 0 0 var(--glow)}}
.call-loud .go,.call-quiet .go{animation:callIn 1.3s var(--ease-pop)}
.call-soft .go{animation:callSoft .8s ease-out}
@keyframes callIn{0%{transform:scale(.6);opacity:.4}30%{transform:scale(1.08);opacity:1}45%{transform:none}60%{transform:translateY(-12px)}72%{transform:none}84%{transform:translateY(-6px)}100%{transform:none}}
@keyframes callSoft{30%{transform:translateY(-6px)}55%{transform:none}75%{transform:translateY(-4px)}100%{transform:none}}

/* pad: pick adventure */
.picker{--pick:min(28vw,180px,calc((100vh - 4.4rem - 28px - 3rem) / 3));display:grid;grid-template-columns:repeat(3,var(--pick));gap:14px}
.pick{aspect-ratio:1;border-radius:var(--r-l);display:grid;place-items:center;box-shadow:0 var(--press-l) 0 rgba(0,0,0,.25)}
.pick:active{transform:translateY(var(--press-l));box-shadow:0 0 0 rgba(0,0,0,.25)}
.pick svg{width:72%}
.bigicon{width:min(46vw,200px)}
#padSleep .bigicon{transform:rotate(-8deg)}
/* splash */
/* The logo is an image with its own colours (brand/logo-centred.svg, and brand/logo-inline.svg on the pairing screen), so it looks the same in every world. */
.splash .logo{width:min(clamp(220px,42vw,560px),70vh);height:auto;animation:rise .7s var(--ease-pop)}
/* One line on the pairing screen, about as tall as the old text title, so the QR code keeps its room. */
#tvPair .logo{display:block;width:min(clamp(200px,36vw,520px),44vh,80vw);height:auto}
.peek{display:flex;gap:clamp(.3rem,1.5vw,1rem);justify-content:center;flex-wrap:wrap;max-width:min(92vw,900px)}
.peek div{width:clamp(44px,7vw,96px);opacity:0;animation:rise .5s var(--ease-pop) forwards}
@keyframes rise{from{transform:translateY(30px) scale(.6);opacity:0}to{transform:none;opacity:1}}
.splash .skip{font-size:var(--t-small);opacity:.6;margin:0}
/* adult menu */
.adultBtn{position:fixed;top:calc(env(safe-area-inset-top,0px) + .7rem);right:.7rem;width:48px;height:48px;border-radius:50%;background:var(--tile);opacity:.55;display:none;place-items:center;z-index:8;box-shadow:0 var(--press-s) 0 var(--boardD)}
.adultBtn svg{width:30px;height:30px}
.adultBtn:focus-visible{opacity:1}
body[data-screen="tvPick"] #tvMenuBtn,body[data-screen="tvGame"] #tvMenuBtn,body[data-screen="tvSleep"] #tvMenuBtn,
body[data-screen="pad"] #padMenuBtn,body[data-screen="padPick"] #padMenuBtn,body[data-screen="padSleep"] #padMenuBtn{display:grid}
.dialog .col{display:flex;flex-direction:column;gap:.8rem;min-width:min(80vw,340px)}
.gateAns{display:flex;gap:.7rem;flex-wrap:wrap;justify-content:center}
.gateAns .btn{min-width:3.4em}
.dialog.shake{animation:shake .3s 2}
.help{max-width:min(92vw,760px);max-height:88vh;overflow:auto;text-align:left;align-items:stretch;gap:1rem}
.help h2{margin:0;font-size:var(--t-title)}
.help h3{margin:.4rem 0 0;font-size:var(--t-lead)}
.help p,.help li{margin:0;font-size:var(--t-body);line-height:1.45}
.help a{color:var(--pimD);font-weight:600}
.help ol,.help ul{margin:0;padding-left:1.3em;display:flex;flex-direction:column;gap:.3rem}
.help .btn{align-self:center}
@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}
/* The app on an Android TV (android-tv spec 5 and 6). Tvs crop the edges: everything is drawn 5 % smaller,
   2.5 % in from each edge, fixed elements included (the scaled body is their containing block). The focus
   ring shows on :focus, because focus set from the code does not always match :focus-visible on a tv. */
html[data-tv] body{transform:scale(.95)}
html[data-tv] button:focus{outline:6px solid var(--sun);outline-offset:4px}
html[data-tv] .adultBtn:focus{opacity:1}
/* hint ladder: the next right cell */
.hintcell{z-index:2}
.hintcell::after{content:"";width:72%;aspect-ratio:1;border-radius:50%;border:6px solid var(--sun);box-shadow:0 0 18px var(--sun);animation:glow 1.2s ease-in-out infinite}

/* A long program on a narrow screen (a phone upright): two lines, read like text, so each card stays big enough to tap. */
@media (max-width: 599px){ .strip{--cols:var(--narrow,5)} }
/* Pad held sideways: the row across the top, the arrows on the left and the buttons on the right,
   everything sized by the height (a phone on its side is only about 390px tall). */
@media (orientation: landscape){
  #pad.on{display:grid;grid-template-columns:auto auto;grid-template-rows:auto auto;grid-template-areas:"strip strip" "dpad actions";
    align-content:center;justify-content:center;align-items:center;column-gap:clamp(1.5rem,6vw,5rem);row-gap:clamp(.6rem,3vh,1.6rem);
    padding:calc(env(safe-area-inset-top,0px) + clamp(.5rem,2vh,1rem)) 72px clamp(.5rem,2vh,1rem)}
  #pad .strip{grid-area:strip;justify-self:center;width:max-content;max-width:calc(100vw - 144px);padding:clamp(6px,1.4vh,10px);grid-template-columns:repeat(var(--cols,5),minmax(0,min(120px,17vh)))}
  #pad .dpad{grid-area:dpad;--cell:min(124px,calc((80vh - 83px) / 3))}
  #pad .actions{grid-area:actions;display:grid;grid-template-columns:repeat(2,var(--cell));gap:10px;width:auto;--cell:min(124px,calc((80vh - 83px) / 3))}
  #pad .go{grid-column:1 / 3;height:calc(var(--cell) * 1.25)}
  #pad .clear,#pad .step{width:auto;height:var(--cell)}
  #pad .clear svg{width:min(48px,55%)}
  #pad .go svg{width:min(56px,60%);height:min(56px,60%)}
}

/* Older TV browsers without aspect-ratio (Chromium < 88): give the board and square cells their height another way. */
@supports not (aspect-ratio: 1){
  .slot{height:min(120px, calc((100vw - 3rem) / var(--cols,5)))}
  #tvGame .board{height:min(calc(70vw * 4 / 5), 78vh)}
  @media (max-aspect-ratio: 4/3){ #tvGame .board{height:min(73.6vw, 62vh)} }
  .board{height:min(calc(92vw * 4 / 5), 64vh)}
  .qr{height:min(40vh,300px)}
  .pick{height:var(--pick)}
  .dots i{height:clamp(14px,2vw,26px)}
  .trail::after{height:22%}
  .hintcell::after{height:72%}
}

/* card marks, shared by the tv row and the pad row (spec section 5). Keep these last in the file. */
.m-bad{outline:6px solid var(--warn);outline-offset:2px;animation:shake .3s 3}
.m-bad::after{content:"";position:absolute;top:18%;right:18%;bottom:18%;left:18%;background:linear-gradient(135deg,transparent 44%,rgba(255,255,255,.95) 45%,rgba(255,255,255,.95) 55%,transparent 56%)}
.m-never{opacity:.3;filter:grayscale(1);outline:3px dashed rgba(0,0,0,.35);outline-offset:1px}
.m-missing,.m-missing-row{outline:5px dashed var(--sun);outline-offset:2px;animation:blink 1.2s ease-in-out infinite}
@keyframes blink{50%{outline-color:var(--sunFade)}}
.m-extra{outline:4px solid rgba(0,0,0,.25);animation:wobble 1s ease-in-out infinite}
@keyframes wobble{25%{transform:rotate(-8deg)}75%{transform:rotate(8deg)}}
.m-reached::before{content:"\2605";position:absolute;top:-34%;right:-18%;color:var(--sun);font-size:3vh;line-height:1;text-shadow:0 2px 0 var(--sunD)}
.m-gold{box-shadow:0 0 0 .6vh var(--sun),0 .5vh 0 rgba(0,0,0,.2);animation:goldin .5s ease-out both;animation-delay:calc(var(--i) * .15s)}
@keyframes goldin{50%{transform:scale(1.15)}}
/* outcome events on the board */
#hero.dizzy::after{content:"\2726\2726\2726";position:absolute;top:-24%;left:0;right:0;text-align:center;color:var(--sun);font-size:clamp(1rem,2.4vw,2rem);letter-spacing:.15em;animation:dizzy 1.2s linear infinite}
@keyframes dizzy{to{transform:rotate(360deg)}}
#goal.call svg{animation:call 1s ease-in-out infinite}
@keyframes call{50%{transform:scale(1.15) translateY(-8%)}}
#goal.pass svg{animation:pass .6s ease-out}
@keyframes pass{to{transform:rotate(360deg)}}
.token.hit svg{animation:hit .45s ease-out}
@keyframes hit{30%{transform:rotate(8deg)}60%{transform:rotate(-6deg)}}
