/* The page is a keyboard. Everything you can press is a keycap; game titles, the word you draw on,
   the blanks you fill, the chain you build. Light beige case, dye-sub ink legends, one coloured cap per game.
   One typeface. No rules, no labels. Dark mode (html.ch-dark, chosen on /account) is the same keyboard
   at night: black case, glowing caps, neon accents. */
:root{
  --desk:#faf7f1; --case:#efeae0; --cap:#fffdf8; --cap-edge:#cfc8b8; --cap-line:#e8e2d4; --blue:#1a56e8;   /* three surfaces: desk, case, key */
  --ink:#1b1b1f; --mute:#6e6a60; --bad:#d1242f;
  /* weights: 700 headings and scores, 500 legends and buttons, 400 everything else */
  /* per-game accents, chosen from colour-preference research: azure blue (top-ranked
     across cultures), electric violet (novelty/rarity), teal (calm, clean) */
  --rain:#1b6ef3; --invade:#7c3aed; --wordis:#0d9488; --dopamine:#db2777;
  /* the rainbow holo: a full spectrum with no orange stop, dark enough for white text */
  --holo-grad:linear-gradient(100deg,#b3184a,#a8780a,#1f7a3c,#0e7f8c,#1440c0,#8a16a8,#b3184a);
  --hi:#fff; --sheen:rgba(255,255,255,.5); --field:#fffefb;   /* keycap top light, panel sheen, input fill */
  --font:'Geist', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --s1:4px; --s2:8px; --s3:12px; --s4:24px; --s5:40px; --s6:64px; --s7:112px;
  color-scheme:light;
}
*{box-sizing:border-box}
html,body{height:100%}
html{scroll-behavior:smooth;scrollbar-gutter:stable;scrollbar-color:var(--cap-edge) var(--desk)}
/* a page that never scrolls keeps no strip for a scrollbar, so the footer's band runs right to the window's edge:
   one-screen pages (main.fit) from the first paint, any other short page once chorana.js sees it fits (html.footfill) */
html:has(main.fit),html.footfill{scrollbar-gutter:auto}
/* the browser scrollbar as a chorana keycap rail */
::-webkit-scrollbar{width:14px;height:14px}
::-webkit-scrollbar-track{background:var(--desk)}
::-webkit-scrollbar-thumb{background:var(--cap);border:3px solid var(--desk);border-radius:8px;box-shadow:inset 0 0 0 1px var(--cap-edge)}
::-webkit-scrollbar-thumb:hover{background:var(--case)}
::-webkit-scrollbar-corner{background:var(--desk)}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
body{margin:0;background:var(--desk);color:var(--ink);font-family:var(--font);font-size:16px;line-height:1.5;-webkit-font-smoothing:antialiased;font-variant-numeric:tabular-nums}
.wrap{max-width:1040px;margin:0 auto;padding-block:var(--s4) var(--s6);padding-left:max(16px,env(safe-area-inset-left,0px));padding-right:max(16px,env(safe-area-inset-right,0px))}
header{display:flex;align-items:center;justify-content:space-between;gap:var(--s3);margin-bottom:var(--s5);flex-wrap:wrap}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer;padding:0}
.brand{font-weight:700;font-size:30px;letter-spacing:-.015em;display:flex;align-items:center;gap:12px;color:var(--ink);text-decoration:none}
/* the logo: a black keycap with a white music note on it */
.brand::before{content:'';flex:none;width:40px;height:40px;box-sizing:border-box;border-radius:8px;background:#1b1b1f url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%3E%3Cpath%20d='M12%203v10.55c-.59-.34-1.27-.55-2-.55-2.21%200-4%201.79-4%204s1.79%204%204%204%204-1.79%204-4V7h4V3h-6z'%20fill='%23000'%20fill-opacity='.35'%20transform='translate%28.6%20.9%29'/%3E%3Cpath%20d='M12%203v10.55c-.59-.34-1.27-.55-2-.55-2.21%200-4%201.79-4%204s1.79%204%204%204%204-1.79%204-4V7h4V3h-6z'%20fill='%23fff'/%3E%3C/svg%3E") calc(50% + 1px) 6px/24px 24px no-repeat;border-bottom:5px solid #000;box-shadow:inset 0 1px 0 rgba(255,255,255,.12);transition:transform .07s,height .07s,border-bottom-width .07s}
/* hovering presses the whole logo: the name sinks with the key, and the key's edge shrinks so its bottom stays put */
.brand{height:40px;transition:transform .07s}
.brand::before{align-self:flex-start}
.brand:hover{transform:translateY(3px)}
.brand:hover::before{height:37px;border-bottom-width:2px}
.brand em{font-style:normal;font-weight:400;color:var(--mute)}
.sub{color:var(--mute);font-size:14px}
:focus-visible{outline:3px solid var(--ink);outline-offset:2px}
#kin{position:absolute;opacity:0;width:1px;height:1px;left:0;top:0;pointer-events:none}

/* keycaps */
.key{display:inline-grid;place-items:center;min-width:1.6em;height:1.6em;padding:0 .3em;background:var(--cap);color:var(--ink);border-radius:.18em;border:1px solid var(--cap-line);border-bottom:.16em solid var(--cap-edge);box-shadow:inset 0 .06em 0 var(--hi);line-height:1;font-weight:500;transition:transform .06s,border-bottom-width .06s;vertical-align:middle}
.key.down,.key:active{transform:translateY(.1em);border-bottom-width:.06em}
.key.gap{background:none;border:0;box-shadow:none;min-width:.6em}
.key.acc{background:var(--acc);color:#fff;border-bottom-color:color-mix(in srgb,var(--acc) 70%,#000)}
.key.cur{outline:3px solid var(--acc);outline-offset:-3px}
.caps{display:inline-flex;gap:.18em;flex-wrap:wrap}

/* buttons are keys too */
.btn{display:inline-grid;place-items:center;padding:0 14px;height:36px;background:var(--cap);border-radius:5px;border:1px solid var(--cap-line);border-bottom:4px solid var(--cap-edge);box-shadow:inset 0 1px 0 var(--hi);font-weight:500;font-size:15px;transition:transform .06s,border-bottom-width .06s}
.btn:hover{background:var(--field)}
.btn:active{transform:translateY(2px);border-bottom-width:2px}
.btn.primary{background:var(--acc);color:#fff;border-color:var(--acc);border-bottom-color:color-mix(in srgb,var(--acc) 70%,#000)}
.btn.primary:hover{background:color-mix(in srgb,var(--acc) 88%,#fff)}
.btn.quiet{background:none;border:0;box-shadow:none;color:var(--mute);text-decoration:underline;text-underline-offset:3px}
.btn.quiet:hover{color:var(--ink);background:none}
.btn[disabled]{opacity:.5;cursor:default}
.row-actions{display:flex;gap:var(--s3);align-items:center;flex-wrap:wrap;justify-content:center}

/* hub: three rows of keys */
.lede{font-size:22px;font-weight:500;margin:0 0 var(--s1);max-width:30ch;line-height:1.3}
.lede-sub{color:var(--mute);max-width:56ch;margin:0 0 var(--s5);font-size:15px}
.games{display:flex;gap:var(--s5);justify-content:center;flex-wrap:wrap;width:100%}
.gcard{--acc:var(--ink);display:flex;flex-direction:column;align-items:center;text-align:center;gap:var(--s3);padding:0;width:280px;min-width:0}
.gcard{cursor:pointer}
a.gcard{text-decoration:none;color:inherit}
.gcard:hover .caps .key{transform:translateY(.1em);border-bottom-width:.06em}
.gcard:active .caps .key{transform:translateY(.1em);border-bottom-width:.06em}
.gcard h2{margin:0;font-size:17px;font-weight:700;line-height:1}
.gcard h2 .caps{flex-wrap:nowrap}
.gcard p{margin:0;font-size:16px;max-width:24ch;min-width:0;color:var(--mute)}
.gcard h2.gname{font-size:28px;font-weight:700;letter-spacing:-.015em;line-height:1.1;color:var(--acc)}
/* every game's name, on its card and above its stage: its colour shimmering slowly into
   a lighter tint of itself (and glowing, in dark mode only); pointing at a card wakes it up */
.gcard h2.gname,.shell .strip .name,#boards h2.gname{background:linear-gradient(100deg,var(--acc) 0%,color-mix(in srgb,var(--acc) 72%,#fff) 25%,var(--acc) 50%,color-mix(in srgb,var(--acc) 72%,#fff) 75%,var(--acc) 100%);background-size:400% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-fill-color:transparent;filter:saturate(1.25);animation:holo 6s linear infinite;transition:filter .25s}
.gcard:hover h2.gname,.gcard:focus-visible h2.gname{filter:saturate(1.45) brightness(1.04);animation-duration:2s} /* light mode: no glow, just the shimmer; the glow is dark mode's */
.ch-dark .gcard h2.gname,.ch-dark .shell .strip .name,.ch-dark #boards h2.gname{filter:saturate(1.15) drop-shadow(0 0 5px color-mix(in srgb,var(--acc) 30%,transparent))} /* dark mode: a faint halo, no blaze */
.ch-dark .gcard:hover h2.gname,.ch-dark .gcard:focus-visible h2.gname{filter:saturate(1.3) drop-shadow(0 0 7px color-mix(in srgb,var(--acc) 45%,transparent)) brightness(1.08)}
@media (prefers-reduced-motion:reduce){.gcard h2.gname,.shell .strip .name,#boards h2.gname{animation:none}}
.shell .strip .name:has(.ntag){background:none;color:inherit;-webkit-text-fill-color:currentColor;filter:none;animation:none} /* a name drawn in a nametag keeps the tag's own look */
.gcard .best{font-size:13px;color:var(--mute);white-space:nowrap}
.gcard .best b{color:var(--ink);font-weight:700;font-size:20px;display:block;line-height:1.1}
@media (max-width:760px){.games{gap:var(--s5)}}

/* game shell */
.shell{--acc:var(--ink)}
.strip{display:flex;align-items:center;gap:var(--s4);font-size:15px;color:var(--mute);margin-bottom:var(--s3);flex-wrap:wrap}
.strip .name{font-weight:700;font-size:22px;color:var(--ink)}
.strip .sndwrap{margin-left:auto;position:relative}
.sndmenu{position:absolute;top:calc(100% + 8px);right:0;z-index:30;display:flex;flex-direction:column;gap:3px;padding:6px;background:var(--cap);border:1px solid var(--cap-line);border-bottom:4px solid var(--cap-edge);border-radius:8px;box-shadow:0 10px 24px rgba(0,0,0,.1)}
.sndmenu .snditem{font:inherit;font-size:14px;text-align:left;padding:7px 14px;border:0;background:none;border-radius:5px;cursor:pointer;white-space:nowrap;color:var(--ink)}
.sndmenu .snditem:hover{background:var(--case)}
.sndmenu .snditem.now{background:var(--acc);color:#fff}
/* 38 packs: the menu lays them out in three columns; anything that isn't a pack (headings, the typing test's
   ticks, the admin tools) runs across all three */
.sndmenu{display:grid;grid-template-columns:repeat(3,max-content);column-gap:3px;max-height:min(72vh,620px);overflow-y:auto;overflow-x:hidden}
.sndmenu > :not(.snditem),.sndmenu > .snditem.ttcheck{grid-column:1/-1}
.strip b{color:var(--ink);font-weight:700}
.strip .livestats{display:inline-flex;gap:18px;align-items:baseline}
.strip .livestats b{font-variant-numeric:tabular-nums}
.lives{display:inline-flex;gap:5px;vertical-align:middle}
.lives i{width:12px;height:12px;border-radius:3px;background:var(--acc);display:block;border-bottom:2px solid color-mix(in srgb,var(--acc) 70%,#000)}
.lives i.lost{background:var(--case);border-bottom-color:var(--cap-edge)}
/* the arena keeps the page column's width but stretches down to fill the viewport */
.stage{position:relative;border-radius:12px;background:var(--case);min-height:clamp(400px,100dvh - 300px,1400px);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--s4);padding:var(--s5) var(--s4);overflow:hidden;cursor:text}
@media (max-width:640px){.stage{min-height:clamp(320px,100dvh - 215px,1400px);padding:var(--s4) var(--s3)}}
.stage.shake{animation:shake .32s}
@keyframes shake{20%{transform:translateX(-7px)}40%{transform:translateX(6px)}60%{transform:translateX(-4px)}80%{transform:translateX(3px)}}
.flash{position:absolute;inset:0;background:#fff;opacity:0;pointer-events:none}
.flash.on{animation:flash .22s}
@keyframes flash{0%{opacity:.6}100%{opacity:0}}
.hint{color:var(--mute);font-size:14px;margin-top:var(--s3);display:flex;gap:var(--s4);flex-wrap:wrap}
kbd{font:inherit;display:inline-grid;place-items:center;min-width:1.6em;height:1.5em;padding:0 .35em;background:var(--cap);border-radius:4px;border-bottom:2px solid var(--cap-edge);font-size:12px;color:var(--ink)}
.msg{font-size:24px;font-weight:500;line-height:1.25;text-align:center;text-wrap:balance;max-width:26ch}
.msg small{display:block;font-size:15px;font-weight:400;color:var(--mute);margin-top:var(--s3);line-height:1.5;max-width:46ch}
/* the end screen owns the whole stage: it covers the game scene behind it */
.over{position:absolute;inset:0;z-index:6;background:var(--case);border-radius:12px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--s3);text-align:center;padding:var(--s4);overflow:auto}
.over .big{font-size:clamp(48px,9vw,72px);font-weight:700;line-height:1;color:var(--acc)}
.over .unit{color:var(--mute);font-size:15px;margin-top:-8px}
.tbl{display:grid;grid-template-columns:auto auto;gap:var(--s1) var(--s4);font-size:15px;color:var(--mute);margin-top:var(--s1)}
.tbl b{color:var(--ink);font-weight:700;text-align:right}
.tbl .new{color:var(--acc)}
.sub{color:var(--mute);font-size:14px}
@media (hover:none){.hint kbd{display:none}}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}

/* High Noon */
@media (max-width:640px){.noon .stage{padding-bottom:180px}}
.cue{font-size:20px;color:var(--mute);z-index:1;text-align:center}
.cue.loud{color:var(--ink);font-size:36px;font-weight:700}
.cue small{display:block;font-size:15px;font-weight:400;color:var(--mute);margin-top:8px}

/* Afterimage */

/* Chain */
@keyframes blink{50%{opacity:0}}

/* Downpour */
.rain .stage{justify-content:center}
.rain .field{position:absolute;inset:0;overflow:hidden}
.rain .drop{position:absolute;top:0;display:flex;gap:.14em;font-size:clamp(18px,2.1vw,30px);transition:opacity .25s}
.rain .drop.lock .key{outline:3px solid var(--acc);outline-offset:-3px}
.rain .drop .key.hit{background:var(--acc);color:#fff;border-color:var(--acc);border-bottom-color:color-mix(in srgb,var(--acc) 70%,#000);outline:0}
.rain .drop.pop{opacity:0}
.rain .puddle{position:absolute;left:0;right:0;bottom:26px;height:2px;background:var(--cap-edge)}

/* Tumbler */
.invade .field{position:absolute;inset:0;overflow:hidden}
.invade .inv{position:absolute;left:0;top:0;display:flex;gap:.14em;font-size:clamp(16px,1.8vw,26px);transition:opacity .25s}
.invade .inv.lock .key{outline:3px solid var(--acc);outline-offset:-3px}
.invade .inv .key.hit{background:var(--acc);color:#fff;border-color:var(--acc);border-bottom-color:color-mix(in srgb,var(--acc) 70%,#000);outline:0}
.invade .inv.pop{opacity:0}
.invade .ground{position:absolute;left:0;right:0;bottom:26px;height:2px;background:var(--cap-edge)}

/* Tightrope */
@media (max-width:640px){.rope .stage{padding-bottom:175px}}
.wordis .tower{position:absolute;bottom:34px;left:50%;transform:translateX(-50%);display:flex;flex-direction:column-reverse;align-items:center;gap:5px;z-index:1}
.wordis .brick{display:flex;gap:.14em;font-size:clamp(18px,2.3vw,30px);padding:3px 0;transform-origin:bottom;animation:brickland .22s cubic-bezier(.55,0,1,.45)}
.wordis .brick .key.hit{background:var(--acc);color:#fff;border-color:var(--acc);border-bottom-color:color-mix(in srgb,var(--acc) 70%,#000)}
.wordis .brick.pull{transform:translateX(140px) rotate(6deg);opacity:0;transition:transform .22s,opacity .22s}
.wordis .tower.danger .brick:last-child{outline:3px solid var(--bad);outline-offset:2px}
/* each brick free-falls from the very top of the stage; --drop is measured per brick */
@keyframes brickland{from{transform:translateY(calc(-1 * var(--drop,320px)))}88%{transform:translateY(0) scaleY(1)}94%{transform:translateY(0) scaleY(.88)}to{transform:none}}
.wordis .floorline{position:absolute;left:0;right:0;bottom:26px;height:2px;background:var(--cap-edge)}
@media (prefers-reduced-motion:reduce){.wordis .brick{animation:none}}

/* Forge */

/* Tempo */

/* Popcorn */

/* versus games (Crossfire, Tug of War, Hot Potato) */

/* Crossfire */
@media (max-width:640px){.cross .stage{padding-bottom:140px}}

/* Tug of War */

/* Hot Potato */

/* landing */
/* signed in (html.ch-in, set in <head> from the stored session): Account instead of Log in / Create account */
#navme{display:none}
.ch-in #navme{display:inline}
.ch-in #navlogin,.ch-in #navacct{display:none}
.nav{display:flex;gap:var(--s4);align-items:center;font-size:15px}
.nav a{color:var(--ink);text-decoration:none;padding:8px 2px}
.nav a.btn{padding:0 16px;height:36px;border-bottom:4px solid #000;line-height:1;position:relative;top:3px}
.nav a:not(.btn){border-bottom:2px solid transparent}
.nav a:not(.btn):hover{border-bottom-color:var(--cap-edge)}
.nav a.on{border-bottom-color:var(--ink)}
.nav .btn{--acc:var(--blue)}
@media (max-width:640px){.nav{gap:var(--s3)}}
.sets{display:flex;gap:var(--s2);flex-wrap:wrap;align-items:center}
.sets > .key{height:40px;padding:0 14px;font-size:15px;cursor:pointer;--acc:var(--ink)}
.sets > .key.acc{background:var(--ink);color:var(--cap);border-bottom-color:#000}
section{margin-top:var(--s7);scroll-margin-top:var(--s4);text-align:center;display:flex;flex-direction:column;align-items:center}
/* the first section sits as far under the hero keyboard as the hero sits under the navbar (40+40 = 40+40) */
.open + section{margin-top:var(--s5)}
section h2{font-size:clamp(28px,3.8vw,38px);font-weight:700;margin:0 0 var(--s4);letter-spacing:-.02em}
section > p{color:var(--mute);margin:-8px 0 var(--s5);max-width:48ch;font-size:18px}
/* word rarity: untyped keys carry a tinted edge so you can spot a rare word coming */
.tiered .key{border-bottom-color:color-mix(in srgb,var(--acc) 55%,var(--cap-edge));border-color:color-mix(in srgb,var(--acc) 25%,var(--cap-line))}
/* rainbow tier: a holographic shimmer, loud and proud of its 0.3% odds.
   deeper hues + a dark halo keep the letters readable on the moving gradient */
.tier-rainbow .key{color:#fff;font-weight:700;text-shadow:0 1px 2px rgba(0,0,0,.7),0 0 4px rgba(0,0,0,.45);border-color:transparent;background:var(--holo-grad);background-size:400% 100%;animation:holo 2.2s linear infinite;border-bottom-color:rgba(0,0,0,.4)}
.tier-rainbow .key.hit{background:var(--holo-grad);background-size:400% 100%;animation:holo .9s linear infinite;border-color:transparent;border-bottom-color:rgba(0,0,0,.5)}
@keyframes holo{to{background-position:400% 0}}
/* the buy button is the rarest drop on the site: full rainbow-tier holo - nav chip included */
.btn.primary.buybtn,.btn.primary.holo,.nav a.btn.primary{color:#fff;font-weight:700;text-shadow:0 1px 2px rgba(0,0,0,.55),0 0 4px rgba(0,0,0,.35);border-color:transparent;background:var(--holo-grad);background-size:400% 100%;animation:holo 2.4s linear infinite;border-bottom:4px solid rgba(0,0,0,.4)}
.nav a.btn.primary:hover{background:var(--holo-grad);background-size:400% 100%;filter:brightness(1.1)}
.btn.primary.buybtn:hover,.btn.primary.holo:hover{background:var(--holo-grad);background-size:400% 100%;filter:brightness(1.1)}
.btn.primary.buybtn:active,.btn.primary.holo:active{border-bottom-width:2px}
.stage.megahit{animation:megahit .6s}
@keyframes megahit{12%{transform:scale(1.04) rotate(.5deg)}30%{transform:scale(1.005) rotate(-.4deg)}48%{transform:scale(1.025)}70%{transform:scale(1.005)}}
@media (prefers-reduced-motion:reduce){.tier-rainbow .key,.tier-rainbow .key.hit,.stage.megahit,.btn.primary.buybtn,.btn.primary.holo,.nav a.btn.primary{animation:none}}

/* juice: floating score pops, confetti, combo badge, big-hit pulse */
.spop{position:absolute;z-index:4;font-weight:700;font-size:clamp(22px,2.4vw,30px);transform:translate(-50%,0);animation:spop .85s ease-out forwards;pointer-events:none;text-shadow:0 1px 0 rgba(255,255,255,.6)}
.spop.big{font-size:clamp(32px,3.6vw,44px)}
@keyframes spop{20%{opacity:1}100%{transform:translate(-50%,-46px);opacity:0}}
.confetti{position:absolute;z-index:5;width:8px;height:8px;border-radius:2px;border-bottom:2px solid rgba(0,0,0,.25);animation:confet .8s ease-out forwards;pointer-events:none}
@keyframes confet{to{transform:translate(var(--dx),var(--dy)) rotate(var(--rot));opacity:0}}
.combo{position:absolute;top:10px;right:12px;z-index:4;font-weight:700;font-size:22px;color:var(--mute);pointer-events:none}
.combo.bump{animation:combobump .25s}
.combo.h1{color:#b8860b}
.combo.h2{color:#c2184c;text-shadow:0 0 7px rgba(194,24,76,.45)}
.wash{position:absolute;inset:0;z-index:4;border-radius:12px;pointer-events:none;animation:washout .6s ease-out forwards}
@keyframes washout{from{opacity:.9}to{opacity:0}}
.chasering{position:absolute;inset:0;z-index:7;pointer-events:none}
.chasering b{position:absolute;overflow:hidden}
.chasering b.t{top:0;left:0;right:0;height:3px}.chasering b.b{bottom:0;left:0;right:0;height:3px}
.chasering b.l{left:0;top:0;bottom:0;width:3px}.chasering b.r{right:0;top:0;bottom:0;width:3px}
.chasering i{position:absolute;left:0;top:0;width:100%;height:100%;transform:translateX(-100%);will-change:transform}
.chasering b.t i,.chasering b.b i{background:linear-gradient(90deg,transparent,#d4a017 70%,transparent)}
.chasering b.b i{background:linear-gradient(270deg,transparent,#d4a017 70%,transparent)}
.chasering b.r i{background:linear-gradient(180deg,transparent,#d4a017 70%,transparent)}
.chasering b.l i{background:linear-gradient(0deg,transparent,#d4a017 70%,transparent)}
@property --ca{syntax:'<angle>';initial-value:0deg;inherits:false}
@keyframes chasespin{to{--ca:720deg}}
@media (prefers-reduced-motion:reduce){.wash,.chasering{animation:none;opacity:0}}
@keyframes combobump{40%{transform:scale(1.35) rotate(-4deg)}}
.stage.bighit{animation:bighit .28s}
@keyframes bighit{30%{transform:scale(1.015)}}
.over .pctv{opacity:0;transition:opacity .4s,transform .4s;transform:translateY(4px)}
.over .pctv.show{opacity:1;transform:none}
.over .stamp{display:inline-block;margin-left:8px;padding:1px 8px;border-radius:4px;background:var(--acc);color:#fff;font-weight:700;font-size:13px;animation:stampin .35s cubic-bezier(.2,1.6,.4,1)}
@keyframes stampin{from{transform:scale(2.2) rotate(-8deg);opacity:0}}
@media (prefers-reduced-motion:reduce){.spop,.confetti,.combo.bump,.stage.bighit,.over .stamp{animation:none}}

/* casino layer: pity meter, banners, gold rush glow, combo loss, big win, collection strip, streak */
.comboloss{position:absolute;top:40px;right:12px;z-index:4;font-weight:700;font-size:20px;color:var(--bad);animation:spop .9s ease-out forwards;pointer-events:none}
.over:has(.bwbanner){padding-top:76px}
.over .bwbanner{position:absolute;top:14px;left:50%;transform:translateX(-50%);z-index:2;font-weight:700;font-size:clamp(34px,7vw,56px);color:#b8860b;letter-spacing:-.02em;white-space:nowrap;pointer-events:none;animation:bwin .45s cubic-bezier(.2,1.6,.4,1) both;text-shadow:0 2px 0 rgba(255,255,255,.7)}
@keyframes bwin{from{transform:translateX(-50%) scale(2.2) rotate(-8deg);opacity:0}to{transform:translateX(-50%) scale(1) rotate(0)}}
.streakline{font-size:14px;color:var(--mute);margin:0}
.streakline b{color:var(--ink)}
@media (prefers-reduced-motion:reduce){.comboloss,.over .bwbanner{animation:none;opacity:1}}

/* each game owns one fixed colour; it shows in the floor line, nothing washes the stage */
.rain .puddle{background:color-mix(in srgb,var(--rain) 55%,var(--cap-edge));height:3px}
.invade .ground{background:color-mix(in srgb,var(--invade) 55%,var(--cap-edge));height:3px}
.wordis .floorline{background:color-mix(in srgb,var(--wordis) 55%,var(--cap-edge));height:3px}
.strip .name{color:var(--acc)}

/* mini vignette of each game on its card */
.gprev{position:relative;width:100%;height:120px;background:var(--case);border-radius:8px;overflow:hidden}
.gprev .pit{position:absolute;transform:translate(-50%,-50%)}
.gprev .pcaps{display:flex;gap:2px}
.gprev .pk{display:grid;place-items:center;min-width:18px;height:20px;padding:0 3px;background:var(--cap);border:1px solid var(--cap-line);border-bottom:2px solid var(--cap-edge);border-radius:4px;font-size:11.5px;font-weight:500;color:var(--ink);line-height:1}
.gprev .pcaps.acc .pk{background:var(--acc);color:#fff;border-color:var(--acc);border-bottom-color:color-mix(in srgb,var(--acc) 70%,#000)}
.gprev .pcaps.rainbow .pk{color:#fff;font-weight:700;text-shadow:0 1px 2px rgba(0,0,0,.7);border-color:transparent;border-bottom-color:rgba(0,0,0,.4);background:var(--holo-grad);background-size:400% 100%;animation:holo 2.2s linear infinite}
.gprev .pcaps.mys .pk{background:#2b2b33;color:#fffdf8;border-color:#2b2b33;border-bottom-color:#0d0d10}
.gprev .pcaps.mys::after{content:'?';position:absolute;top:-7px;right:-8px;width:13px;height:13px;border-radius:50%;display:grid;place-items:center;font-size:9px;line-height:1;font-weight:700;color:#fff;background:var(--holo-grad);background-size:400% 100%;animation:holo 1.4s linear infinite}
.gprev .pline{position:absolute;left:8%;right:8%;height:2px;background:var(--cap-edge)}
/* downpour: words fall into the puddle */
.pv-rain .pline{background:color-mix(in srgb,var(--rain) 55%,var(--cap-edge))}
.pv-rain .pfall{animation:pvfall 3.6s linear infinite}
@keyframes pvfall{0%{top:-12%;opacity:0}10%{opacity:1}84%{top:80%;opacity:1}92%,100%{top:84%;opacity:0}}
/* invasion: the fleet bobs and marches, the ship lasers one word */
.pv-invade .pform{position:absolute;inset:0;animation:pvmarch 4.8s ease-in-out infinite}
@keyframes pvmarch{0%,100%{transform:translate(-4px,0)}50%{transform:translate(4px,7px)}}
.pv-invade .pzap{animation:pvzap 2.4s ease-out infinite}
@keyframes pvzap{0%,46%{opacity:1;transform:translate(-50%,-50%) scale(1)}56%{opacity:0;transform:translate(-50%,-50%) scale(1.6)}86%{opacity:0;transform:translate(-50%,-50%) scale(.5)}100%{opacity:1;transform:translate(-50%,-50%) scale(1)}}
.pv-invade .plaser{position:absolute;left:calc(50% - 1px);top:56%;bottom:16%;width:2px;border-radius:1px;background:var(--invade);box-shadow:0 0 6px var(--invade);opacity:0;animation:pvlaser 2.4s linear infinite}
@keyframes pvlaser{0%,40%{opacity:0}42%,50%{opacity:1}54%,100%{opacity:0}}
.pv-invade .pship{width:0;height:0;border-left:8px solid transparent;border-right:8px solid transparent;border-bottom:12px solid var(--invade)}
/* wordis: the rainbow bottom brick slides out and the pile drops */
.pv-wordis .pstack{position:absolute;inset:0;animation:pvstack 3.4s linear infinite}
@keyframes pvstack{0%{opacity:0}6%,88%{opacity:1}100%{opacity:0}}
.pv-wordis .pdrop{position:absolute;inset:0;animation:pvdrop 3.4s cubic-bezier(.5,0,1,.5) infinite}
@keyframes pvdrop{0%,56%{transform:none}68%,100%{transform:translateY(22%)}} /* one row: the bricks sit 22% apart */
.pv-wordis .ppull{animation:pvpull 3.4s ease-in infinite}
@keyframes pvpull{0%,38%{transform:translate(-50%,-50%);opacity:1}54%{transform:translate(70%,-50%) rotate(8deg);opacity:0}100%{transform:translate(70%,-50%);opacity:0}}
.pv-wordis .pline{height:3px;border-radius:2px;background:var(--cap-edge)} /* the pile's floor, in plain gray */
/* dopamine: words pop in with draining bars and burst; the fever meter fills */
.pv-dopamine .ppop{animation:pvpop 3.6s ease-out infinite}
@keyframes pvpop{0%{transform:translate(-50%,-50%) scale(0);opacity:0}7%{transform:translate(-50%,-50%) scale(1.1);opacity:1}11%,64%{transform:translate(-50%,-50%) scale(1);opacity:1}72%{transform:translate(-50%,-50%) scale(1.5);opacity:0}100%{opacity:0}}
.pv-dopamine .plife{position:absolute;left:4%;right:4%;bottom:-6px;height:3px;border-radius:2px;background:var(--dopamine);transform-origin:left;animation:pvlife 3.6s linear infinite}
.pv-dopamine .ppop.acc .plife{background:var(--acc)}
.pv-dopamine .ppop.mys .plife{background:#2b2b33}
@keyframes pvlife{0%,8%{transform:scaleX(1)}64%{transform:scaleX(.18)}100%{transform:scaleX(.18)}}
.pv-dopamine .pplus{position:absolute;transform:translate(-50%,-50%);font-size:12px;font-weight:700;color:#d4a017;opacity:0;animation:pvplus 3.6s ease-out infinite}
@keyframes pvplus{0%,64%{opacity:0;transform:translate(-50%,-50%)}70%{opacity:1}92%,100%{opacity:0;transform:translate(-50%,calc(-50% - 16px))}}
/* the fever meter as the game draws it: ten separate rounded cells across the top, lighting one by one */
.pv-dopamine .pmeter{--off:color-mix(in srgb,var(--cap-edge) 60%,var(--case));position:absolute;top:8%;left:50%;transform:translateX(-50%);display:flex;gap:calc(1.6 * var(--u))}
.pv-dopamine .pmeter i{width:calc(6.6 * var(--u));height:calc(3.6 * var(--u));border-radius:calc(1.2 * var(--u));background:var(--off);animation:3.6s steps(1,end) infinite}
.pv-dopamine .pmeter i:nth-child(1){animation-name:pvc0}
@keyframes pvc0{0%,6%,94%,100%{background:var(--off)}6.01%,93.99%{background:var(--dopamine);box-shadow:0 0 calc(1.2 * var(--u)) color-mix(in srgb,var(--dopamine) 60%,transparent)}}
.pv-dopamine .pmeter i:nth-child(2){animation-name:pvc1}
@keyframes pvc1{0%,14%,94%,100%{background:var(--off)}14.01%,93.99%{background:var(--dopamine);box-shadow:0 0 calc(1.2 * var(--u)) color-mix(in srgb,var(--dopamine) 60%,transparent)}}
.pv-dopamine .pmeter i:nth-child(3){animation-name:pvc2}
@keyframes pvc2{0%,22%,94%,100%{background:var(--off)}22.01%,93.99%{background:var(--dopamine);box-shadow:0 0 calc(1.2 * var(--u)) color-mix(in srgb,var(--dopamine) 60%,transparent)}}
.pv-dopamine .pmeter i:nth-child(4){animation-name:pvc3}
@keyframes pvc3{0%,30%,94%,100%{background:var(--off)}30.01%,93.99%{background:var(--dopamine);box-shadow:0 0 calc(1.2 * var(--u)) color-mix(in srgb,var(--dopamine) 60%,transparent)}}
.pv-dopamine .pmeter i:nth-child(5){animation-name:pvc4}
@keyframes pvc4{0%,38%,94%,100%{background:var(--off)}38.01%,93.99%{background:var(--dopamine);box-shadow:0 0 calc(1.2 * var(--u)) color-mix(in srgb,var(--dopamine) 60%,transparent)}}
.pv-dopamine .pmeter i:nth-child(6){animation-name:pvc5}
@keyframes pvc5{0%,46%,94%,100%{background:var(--off)}46.01%,93.99%{background:var(--dopamine);box-shadow:0 0 calc(1.2 * var(--u)) color-mix(in srgb,var(--dopamine) 60%,transparent)}}
.pv-dopamine .pmeter i:nth-child(7){animation-name:pvc6}
@keyframes pvc6{0%,54%,94%,100%{background:var(--off)}54.01%,93.99%{background:var(--dopamine);box-shadow:0 0 calc(1.2 * var(--u)) color-mix(in srgb,var(--dopamine) 60%,transparent)}}
.pv-dopamine .pmeter i:nth-child(8){animation-name:pvc7}
@keyframes pvc7{0%,62%,94%,100%{background:var(--off)}62.01%,93.99%{background:var(--dopamine);box-shadow:0 0 calc(1.2 * var(--u)) color-mix(in srgb,var(--dopamine) 60%,transparent)}}
.pv-dopamine .pmeter i:nth-child(9){animation-name:pvc8}
@keyframes pvc8{0%,70%,94%,100%{background:var(--off)}70.01%,93.99%{background:var(--dopamine);box-shadow:0 0 calc(1.2 * var(--u)) color-mix(in srgb,var(--dopamine) 60%,transparent)}}
.pv-dopamine .pmeter i:nth-child(10){animation-name:pvc9}
@keyframes pvc9{0%,78%,94%,100%{background:var(--off)}78.01%,93.99%{background:var(--dopamine);box-shadow:0 0 calc(1.2 * var(--u)) color-mix(in srgb,var(--dopamine) 60%,transparent)}}
@media (prefers-reduced-motion:reduce){.gprev *,.gprev *::after{animation:none!important}}
.price{display:flex;flex-direction:column;align-items:center;gap:var(--s4)}
.price .amt{font-size:clamp(64px,10vw,104px);font-weight:700;line-height:1;letter-spacing:-.03em}
.price .amt small{display:inline;font-size:24px;font-weight:500;color:var(--mute);letter-spacing:0;margin-left:10px}
.price p{margin:0;color:var(--mute);font-size:18px}
.price .note{font-size:14px;color:var(--mute);margin-top:var(--s2)}
a.btn{text-decoration:none;color:var(--ink)} /* a key, not a link: ink, never the browser's link blue (primary and quiet keys set their own) */
.buybtn{--acc:var(--blue);height:52px;padding:0 28px;font-size:18px}
/* the yearly subscription, under the monthly buy button (subscribers see neither) */
.buyyear{color:var(--mute);font-size:14px;text-decoration:underline;text-underline-offset:3px}
.buyyear:hover{color:var(--ink)}
.ch-in.ch-own .buyyear{display:none}
.price .note,section .note{font-size:14px;color:var(--mute);min-height:1.5em}
.faq{display:flex;flex-direction:column;gap:var(--s1);margin:0;max-width:62ch;text-align:left}
.faq dt{font-weight:700;margin-top:var(--s4)}
.faq dt:first-child{margin-top:0}
.faq dd{margin:2px 0 0;color:var(--mute);max-width:60ch}
.how{display:flex;flex-direction:column;gap:var(--s4);margin:0;padding:0;list-style:none;counter-reset:s;max-width:52ch;text-align:left}
.how li{display:grid;grid-template-columns:40px 1fr;gap:var(--s3);align-items:start}
.how li::before{counter-increment:s;content:counter(s);display:grid;place-items:center;width:40px;height:40px;background:var(--cap);border:1px solid var(--cap-line);border-radius:5px;border-bottom:4px solid var(--cap-edge);box-shadow:inset 0 1px 0 var(--hi);font-weight:700;grid-row:1/3}
.how b{grid-column:2;font-weight:700}
.how span{grid-column:2;color:var(--mute)}
.how kbd{font:inherit;font-size:13px;display:inline-grid;place-items:center;min-width:1.6em;height:22px;padding:0 6px;background:var(--cap);border-radius:3px;border-bottom:2px solid var(--cap-edge);color:var(--ink);vertical-align:middle}
/* leaderboards under each game, reached by scrolling: all-time and last-24h */
.lbzone{margin-top:var(--s6);text-align:center}
.lbtitle{font-size:clamp(24px,3vw,32px);font-weight:700;letter-spacing:-.02em;margin:0 0 var(--s4)}
.lbrow{display:flex;gap:var(--s5);justify-content:center;align-items:flex-start;flex-wrap:wrap}
.lb .tm{font-size:11px;color:var(--mute);min-width:32px;text-align:right}
.lbrow .lb{width:min(340px,100%)}
.lb{display:flex;flex-direction:column;gap:var(--s3);text-align:left}
.lb h4{margin:0 0 6px;font-size:12px;font-weight:700;letter-spacing:.05em;color:var(--mute)}
.lb ol{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:5px;counter-reset:lb}
.lb li{display:flex;align-items:center;gap:8px;font-size:14px;line-height:1.2}
.lb li::before{counter-increment:lb;content:counter(lb);display:grid;place-items:center;width:20px;height:20px;background:var(--cap);border:1px solid var(--cap-line);border-bottom:2px solid var(--cap-edge);border-radius:4px;font-size:11px;font-weight:700;flex:none}
/* rainbow name and rank number, like the buy button, is reserved for lifetime owners (li.pro), wherever they rank */
.lb li.pro::before{background:var(--holo-grad);background-size:400% 100%;animation:holo 2.4s linear infinite;color:#fff;text-shadow:0 1px 2px rgba(0,0,0,.6);border-color:transparent;border-bottom-color:rgba(0,0,0,.4)}
.lb li.pro .nm{font-weight:700;background:var(--holo-grad);background-size:400% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-fill-color:transparent;animation:holo 2.4s linear infinite}
/* everyone who hasn't bought Chorana is gray: name and rank keycap, like gray-tier words; owners (li.pro) shimmer rainbow above */
.lb li:not(.mute):not(.pro) .nm{color:#6d7078}
.lb li:not(.mute):not(.pro)::before{background:#6d7078;color:#fff;border-color:transparent;border-bottom-color:#4f525a}
@media (prefers-reduced-motion:reduce){.lb li.pro .nm,.lb li.pro::before{animation:none}}
.lb li.mute{color:var(--mute);font-size:13px}
.lb li.mute::before{content:none}
.lb .nm{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--mute)}
.lb li b{font-weight:700;flex:none}

/* account page: auth panel and score history */
.authbox{display:flex;flex-direction:column;gap:var(--s3);width:min(360px,100%);margin-top:var(--s2)}
.authbox input{font:inherit;font-size:16px;height:44px;padding:0 14px;background:var(--field);color:var(--ink);border:1px solid var(--cap-line);border-bottom:4px solid var(--cap-edge);border-radius:5px;box-shadow:inset 0 1px 0 var(--hi)}
.authbox input:focus{outline:2px solid var(--blue);outline-offset:1px}
.authbox .btn{height:44px;font-size:16px;cursor:pointer}
.authbox .btn.primary{--acc:var(--blue)} /* --acc is unset outside nav/buy scopes; without this the button paints white */
.authbox .row{display:flex;gap:var(--s2)}
.authbox .row .btn{flex:1}
.authbox .note{font-size:14px;color:var(--mute);margin:0;min-height:1.5em}
.ubox .row input{flex:1;min-width:0}
.ubox .row .btn{flex:0 0 auto;padding:0 18px}
section.danger{border-top:1px solid var(--cap-edge);padding-top:var(--s5);width:100%;max-width:720px;margin-left:auto;margin-right:auto;gap:var(--s4)}
.danger .btn.danger{background:var(--bad);color:#fff;border-color:var(--bad);border-bottom:4px solid color-mix(in srgb,var(--bad) 60%,#000);cursor:pointer}
.danger .btn.danger:hover{background:color-mix(in srgb,var(--bad) 85%,#000)}
.danger .note{font-size:13px;color:var(--mute);max-width:44ch;margin:var(--s2) auto 0}
/* account hero: username spelled in keycaps over a quiet email line */
.acct .acctname .caps{font-size:clamp(22px,3.4vw,32px);font-weight:700}
.acctmail{margin:0;font-size:14px;color:var(--mute)}
/* account and player pages: "equipped <tag>" over the scores, left-aligned, the tag's name in its own lettering */
.equipped{margin:0 0 var(--s2);font-size:14px;font-weight:500;color:var(--mute);text-align:left}
.equipped .ntext,.equipped b{font-size:15px;font-weight:700}
.acctstats{display:flex;gap:var(--s4);justify-content:center;flex-wrap:wrap;margin:var(--s4) 0 var(--s2)}
.stile{display:flex;flex-direction:column;align-items:flex-start;gap:6px;min-width:120px}
.stile b{font-size:26px;font-weight:700;line-height:1}
.stile span{font-size:12px;color:var(--mute)}
.shist{display:flex;flex-direction:column;gap:var(--s4);width:100%;max-width:720px;margin-top:var(--s4)}
.sgame{display:flex;flex-direction:column;gap:var(--s3);padding:var(--s4) 0;text-align:left}
.sgame + .sgame{border-top:1px solid color-mix(in srgb,var(--cap-edge) 45%,transparent)} /* no cards: a hairline between games */
.sghead{display:flex;justify-content:space-between;align-items:center;gap:var(--s3);flex-wrap:wrap}
.sgame h3{margin:0;font-size:16px;font-weight:700;line-height:1}
.sgame .note{font-size:14px;color:var(--mute);margin:0}
.sstats{display:flex;gap:var(--s4);font-size:12px;color:var(--mute);align-items:baseline}
.sstats b{color:var(--ink);font-weight:700;font-size:17px;margin-right:4px}
.stile.ghost b,.stile.ghost span{visibility:hidden}
.sghost{aspect-ratio:640/185;border-radius:8px;background:color-mix(in srgb,var(--cap-edge) 22%,transparent)}
.chartwrap{position:relative;width:100%}
.schart{display:block;width:100%;height:auto}
.ctip{position:absolute;pointer-events:none;background:var(--ink);color:var(--desk);font-size:12.5px;line-height:1.35;padding:7px 11px;border-radius:7px;transform:translate(-50%,calc(-100% - 12px));white-space:nowrap;z-index:5;opacity:0;transition:opacity .12s}
.ctip.on{opacity:1}
.ctip b{color:var(--desk);font-size:14px}
.ctip::after{content:'';position:absolute;top:100%;left:50%;transform:translateX(-50%);border:5px solid transparent;border-top-color:var(--ink)}

footer{margin-top:var(--s7);padding-top:var(--s3);text-align:center;color:var(--mute);font-size:14px;display:flex;justify-content:center;gap:var(--s3);flex-wrap:wrap}

/* opener, same as the post-download page */
body{text-transform:lowercase}
button,a{text-transform:inherit}
textarea,input{text-transform:none}
.brand,.ch-name{text-transform:none}
[hidden]{display:none!important}
.open{display:flex;flex-direction:column;align-items:center;text-align:center;gap:var(--s4);padding-block:var(--s5) var(--s5)}
.open h1{font-size:clamp(32px,6vw,58px);font-weight:700;line-height:1.05;letter-spacing:-.025em;margin:0;text-wrap:balance}
/* no big page titles: each page's h1 stays for screen readers and search, but never shows. .keep is for an
   h1 that is the whole message (a player who doesn't exist) */
.open h1:not(.keep){position:absolute!important;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}
/* nor the grey line that used to sit under it: the page shows what it is. .keep marks one that is the
   message itself (a missing page, a missing player, the setup screen) */
.open > .lead:not(.keep){display:none}
/* a title block with nothing left to show takes no room, so the page starts right under the header */
.open:not(:has(> :not(h1:not(.keep)):not(.lead:not(.keep)))){padding:0!important;gap:0;margin:0}
/* log in, create an account, new password: the form sits in the middle of the window */
.wrap > main.auth{display:flex;flex-direction:column;justify-content:center}
main.auth > .open:first-child{padding-block:0 var(--s6)}
/* the log-in button: brushed silver, a cool shine sweeping across it on hover */
.btn.silver{background:linear-gradient(180deg,#fbfbfd 0%,#e3e5ea 38%,#c4c8cf 62%,#dfe2e7 100%);color:#1c1e23;font-weight:700;border:1px solid #b3b7bf;border-bottom:4px solid #8b9099;box-shadow:inset 0 1px 0 #fff,0 1px 3px rgba(0,0,0,.12);text-shadow:0 1px 0 rgba(255,255,255,.7);position:relative;overflow:hidden}
.btn.silver::after{content:'';position:absolute;inset:0;background:linear-gradient(105deg,transparent 35%,rgba(255,255,255,.75) 50%,transparent 65%);transform:translateX(-120%);transition:transform .6s ease;pointer-events:none}
.btn.silver:hover::after{transform:translateX(120%)}
.btn.silver:focus-visible{outline:2px solid #6f747d;outline-offset:2px}
@media (prefers-reduced-motion:reduce){.btn.silver::after{display:none}}
.open .lead{font-size:20px;line-height:1.5;margin:0;color:var(--mute);max-width:44ch;min-height:3em;text-wrap:balance}
/* wide screens: header and subtext each on one line, and the keyboard spreads out to match */
@media (min-width:1000px){.open h1,.open .lead{white-space:nowrap;max-width:none}.open .lead{min-height:1.5em}}
.open .setsWrap{display:inline-flex;flex-direction:column;gap:var(--s3);align-self:center;max-width:100%;margin-top:var(--s2)}
/* the hero is one object: a keyboard. function row (sets + volume) on top, live board below */
.deck{background:var(--case);border-radius:14px;padding:var(--s4);display:flex;flex-direction:column;align-items:center;gap:var(--s4);max-width:100%;margin-top:var(--s2);box-shadow:inset 0 2px 0 var(--sheen),inset 0 -3px 0 rgba(0,0,0,.06)}
.fnrow{display:flex;justify-content:space-between;align-items:center;gap:var(--s3);width:100%;flex-wrap:wrap}
.fnrow .sets{gap:6px}
.open .deck .sets > .key{height:30px;padding:0 10px;font-size:13px;cursor:pointer}
.board{display:flex;flex-direction:column;gap:.4em;font-size:clamp(12px,2.9vw,21px)}
.desk{display:flex;align-items:stretch;justify-content:center;gap:var(--s4);max-width:100%;margin-top:var(--s2)}
.desk .deck{margin-top:0}
@media (min-width:1000px){.desk .board{font-size:clamp(21px,2.2vw,28px)}}
/* the mousepad: the same case surface as the keyboard, as tall as it, so they read as a set */
.mousepad{display:flex;align-items:center;justify-content:center;padding:0 var(--s4);background:var(--case);border-radius:14px;box-shadow:inset 0 2px 0 var(--sheen),inset 0 -3px 0 rgba(0,0,0,.06)}
/* the mouse: one cream keycap body, two buttons split down the middle, a wheel between */
.mouse{position:relative;flex:none;width:4.6em;height:7.4em;font-size:clamp(12px,2.2vw,28px);background:var(--cap);border:2px solid var(--cap-edge);border-bottom-width:.3em;border-radius:2.3em 2.3em 2.5em 2.5em/2.7em 2.7em 3.6em 3.6em;box-shadow:inset 0 .08em 0 #fff,0 .2em .5em rgba(0,0,0,.06);overflow:hidden}
.mouse .mb{position:absolute;top:0;width:50%;height:44%;padding:0;margin:0;background:transparent;border:0;border-bottom:2px solid var(--cap-edge);cursor:pointer;transition:background .07s}
.mouse .mb.l{left:0;border-right:2px solid var(--cap-edge)}
.mouse .mb.r{right:0}
.mouse .mb:hover{background:color-mix(in srgb,var(--cap-edge) 18%,var(--cap))}
.mouse .mb.down{background:var(--ink)}
.mouse .wheel{position:absolute;left:50%;top:13%;width:.66em;height:1.35em;margin-left:-.33em;border-radius:.33em;background-color:var(--case);background-image:repeating-linear-gradient(180deg,var(--cap-edge) 0 2px,transparent 2px 5px);border:2px solid var(--cap-edge);pointer-events:none;z-index:1}
@media (max-width:999px){.mousepad{display:none}}
.brow{display:flex;gap:.4em;justify-content:center}
.brow .key{width:2.1em;height:2.1em;min-width:0;padding:0;font-weight:500;font-size:inherit;cursor:pointer;border-radius:.24em;border-bottom-width:.18em;transition:transform .07s,border-bottom-width .07s}
.brow .key.space{width:13.5em}
/* sound packs, all around the keyboard: a row above, a row below and a column of them down each side, each
   name drawn in a nametag; the one playing is ringed. The extra names (.shown) are only there to see */
.deck.ringed{align-items:stretch;gap:8px}
.dmid{display:flex;align-items:center;gap:14px;flex:1 1 auto;min-height:0}
.dmid > .board{flex:none}
.pring{display:flex;flex-wrap:wrap;gap:4px 8px;justify-content:center;align-content:center;overflow:hidden;min-width:0}
.pring.top,.pring.bot{flex:none;height:calc(var(--pf,12px) * 1.8 + 12px)}
.pring.side{flex:1 1 0;align-self:stretch}
.pack{display:flex;align-items:center;padding:3px;border:0;border-radius:6px;background:none;font:inherit;color:var(--ink)}
button.pack{cursor:pointer;transition:transform .15s cubic-bezier(.3,1.4,.5,1)}
button.pack:hover{transform:translateY(-1px)}
.pack .ntag .caps{font-size:var(--pf,12px)}
.pack.on{box-shadow:0 0 0 2px var(--ink)}
button.pack:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
.pack.shown{cursor:default}
/* the typing box: big and gray in the middle of the packs, the text large; empty, a blinking cursor and the
   prompt. It fills the space the keyboard had */
.typebox{flex:0 1 58%;align-self:stretch;min-width:0;display:block;overflow:hidden;box-sizing:border-box;padding:.55em .7em;border-radius:12px;background:#e4e4e8;box-shadow:inset 0 2px 5px rgba(0,0,0,.12),inset 0 0 0 1px rgba(0,0,0,.06);font-size:clamp(18px,3vh,34px);font-weight:500;line-height:1.3;color:var(--ink);text-align:left;cursor:text;overflow-wrap:anywhere;white-space:pre-wrap}
.typebox .caret{display:inline-block;width:.09em;height:1.15em;margin:0 .04em;vertical-align:-.2em;border-radius:1px;background:var(--ink);animation:caretblink 1.05s steps(1) infinite}
.typebox .ph{color:#8a8a93}
.typebox:not(.empty) .ph{display:none}
@keyframes caretblink{0%,50%{opacity:1}50.01%,100%{opacity:0}}
.ch-dark .typebox{background:#1b1b24;box-shadow:inset 0 2px 6px rgba(0,0,0,.5),inset 0 0 0 1px rgba(255,255,255,.06)}
.ch-dark .typebox .ph{color:#7c7c8c}
@media (prefers-reduced-motion:reduce){.typebox .caret{animation:none}}
/* the homepage's one big box: the whole width, the keyboard's old height, the text larger still. Empty, only
   the prompt's own cursor (after its first e) blinks; the prompt's e is dark, as if just typed */
.typebox.big{flex:none;width:100%;height:100%;padding:0;border-radius:0;background:none;box-shadow:none;display:grid;place-items:center;text-align:center;font-size:clamp(34px,7.6vh,86px);font-weight:500;letter-spacing:-.012em;word-spacing:.12em;line-height:1.12;min-height:2.4em;text-transform:none} /* no box: big free-standing text, centred */
/* the prompt in a soft warm gray that sits in the page's beige, its first letter in ink as if just typed */
.typebox.big .ph{color:color-mix(in srgb,var(--ink) 26%,var(--desk));white-space:nowrap} /* "Start typing..." always on one line */
.ch-dark .typebox.big .ph{color:color-mix(in srgb,var(--ink) 32%,var(--desk))}
.ch-dark .typebox.big{background:none;box-shadow:none}
.typebox.empty .tline > .caret{display:none}
/* the big text's cursor is a blinking underscore: after what you've typed, and under the prompt's next letter */
.typebox.big .caret{width:.5em;height:.1em;margin:0 0 0 .06em;vertical-align:-.14em;border-radius:.05em}
.typebox.big .ucur{position:relative}
.typebox.big .ucur::after{content:'';position:absolute;left:-.02em;right:-.02em;bottom:-.14em;height:.1em;border-radius:.05em;background:var(--ink);animation:caretblink 1.05s steps(1) infinite}
@media (prefers-reduced-motion:reduce){.typebox.big .ucur::after{animation:none}}
.typebox .tline{display:block;max-width:100%}
main.home .typebox.big .tline{translate:0 -.3em} /* sits a little higher, a breath above the games' line */
.typebox .ph b{color:var(--ink);font-weight:inherit}
.typebox .ph .ch-name{color:inherit}
/* the homepage's games: a plain grey line over them, and both wait unseen (holding their space) until you type.
   Then the line rises in and the three games flip up into place one after another, like keys pressed in */
main.home #games > h2.gsub{font-size:clamp(17px,2.3vh,24px);font-weight:400;letter-spacing:0;word-spacing:.32em;letter-spacing:.01em;color:var(--mute)}
main.home #games > h2.gsub .ch-name{color:inherit;font-weight:inherit}
main.home #games > h2.gsub .tch{opacity:0}
main.home #games > h2.gsub .tch.on{opacity:1}
/* the typing cursor: a blinking underscore under the next letter, then resting after the last */
main.home #games > h2.gsub .tch{position:relative}
main.home #games > h2.gsub .tch.tend{display:inline-block;width:.5em;opacity:1}
main.home #games > h2.gsub .tch.next::after{content:'';position:absolute;left:0;bottom:-.08em;width:.52em;height:.09em;border-radius:1px;background:var(--mute);opacity:1;animation:caretblink 1.05s steps(1) infinite}
main.home #games > h2.gsub .tch.next:not(.on):not(.tend){opacity:1;color:transparent}
main.home #games.await .gcard{opacity:0}
/* the games arrive one after another: each opens out from a small keycap in its middle, overshoots a touch and
   settles like a key pressed home, then its name, scene and words rise in, in that order */
main.home #games.arrive .gcard{animation:gcardin .85s cubic-bezier(.2,.8,.2,1) both;animation-delay:calc(.25s + var(--ci,0) * .16s)}
@keyframes gcardin{0%{opacity:0;clip-path:inset(40% 36% 40% 36% round 18px);transform:translateY(22px) scale(.94)}35%{opacity:1}50%{clip-path:inset(0 0 0 0 round 12px);transform:translateY(-5px) scale(1.012)}72%{transform:translateY(1px) scale(.996)}100%{opacity:1;clip-path:inset(0 0 0 0 round 0);transform:none}}
main.home #games .gcard > *{transition:opacity .45s ease-out,translate .55s cubic-bezier(.2,.9,.3,1.2)}
main.home #games.await .gcard > *{opacity:0;translate:0 10px}
main.home #games .gcard > :nth-child(1){transition-delay:calc(.62s + var(--ci,0) * .16s)}
main.home #games .gcard > :nth-child(2){transition-delay:calc(.5s + var(--ci,0) * .16s)}
main.home #games .gcard > :nth-child(3){transition-delay:calc(.74s + var(--ci,0) * .16s)}
main.home #games .gcard > :nth-child(4){transition-delay:calc(.84s + var(--ci,0) * .16s)}
@media (prefers-reduced-motion:reduce){main.home #games.arrive .gcard{animation:none}main.home #games .gcard > *{transition:none}}
.brow .key.down{transform:translateY(.1em);border-bottom-width:.06em;background:var(--ink);color:var(--cap);border-bottom-color:#000}
.vctl{display:flex;gap:8px;align-items:center;font-size:13px;color:var(--mute)}
.vctl .key{height:26px;min-width:26px;padding:0 8px;font-size:14px;cursor:pointer}
.vctl b{font-weight:500;color:var(--ink);min-width:1.4em;text-align:center;font-variant-numeric:tabular-nums}
@media (max-width:640px){.deck{padding:var(--s3)}.fnrow{justify-content:center}}
.open .sets > .key{height:44px;padding:0 18px;font-size:16px;cursor:pointer;--acc:var(--ink)}
.open .sets > .key.acc{background:var(--blue);color:#fff;border-color:var(--blue);border-bottom-color:color-mix(in srgb,var(--blue) 70%,#000)}
footer a{color:var(--mute)}
footer a:hover{color:var(--ink)}

/* ---------- Dopamine (was the Frenzy test build) ---------- */
.dopamine .stage{overflow:hidden}
.dopamine .strip b{display:inline-block}
@keyframes dpulse{50%{opacity:.4;transform:scaleY(.75)}}
/* floating words: keycaps that pop in, glow by tier, explode on clear */
.dopamine .fw{position:absolute;display:flex;gap:.12em;font-size:clamp(28px,3.3vw,46px);animation:fwin .22s cubic-bezier(.2,1.6,.4,1);z-index:1}
.dopamine .fw .key{display:grid;place-items:center;min-width:1.45em;height:1.5em;padding:0 .26em;border-radius:.13em}
.dopamine .fw:not(.tskin) .key{background:var(--cap);border:1px solid var(--cap-line);border-bottom:.13em solid var(--cap-edge);font-weight:500;color:var(--ink);box-shadow:inset 0 1px 0 var(--hi)} /* a word in a nametag skin keeps the skin's look */
.dopamine .fw .key.hit{background:var(--acc);color:#fff;border-color:var(--acc);border-bottom-color:color-mix(in srgb,var(--acc) 70%,#000);animation:hitpop .14s}
.dopamine .fw .life{position:absolute;left:2%;right:2%;bottom:-9px;height:4px;border-radius:2px;background:color-mix(in srgb,var(--acc) 70%,#fff);transform-origin:left}
.dopamine .fw.low .life{background:var(--bad)}
.dopamine .fw.pop{animation:fwpop .3s forwards;pointer-events:none}
.dopamine .fw.burn{animation:fwburn .4s forwards}
/* words arrive by fading and rising, never by scaling: after a scale animation the browser can keep
   the letters drawn at an in-between size, which reads as blur */
@keyframes fwin{from{transform:translateY(10px);opacity:0}}
@keyframes hitpop{50%{transform:translateY(-.16em)}}
@keyframes fwpop{to{transform:scale(1.55);opacity:0}}
/* the pop's flash: a white layer fading out (a brightness filter forced a repaint per pop) */
.dopamine .fw.pop::after{content:'';position:absolute;inset:-3px;border-radius:.2em;background:#fff;opacity:0;pointer-events:none;animation:popflash .3s ease-out}
@keyframes popflash{0%{opacity:.55}100%{opacity:0}}
@keyframes fwburn{to{transform:scale(.7) rotate(5deg);opacity:0}}
/* fever: ten cells across the top; full = the whole stage goes rainbow */
.dopamine .fmeter{position:absolute;top:28px;left:50%;transform:translateX(-50%);display:flex;gap:4px;z-index:3}
.dopamine .fmeter i{width:22px;height:8px;border-radius:3px;background:color-mix(in srgb,var(--cap-edge) 60%,var(--case))}
.dopamine .fmeter i.on{background:var(--acc);box-shadow:0 0 6px color-mix(in srgb,var(--acc) 60%,transparent)}
.dopamine .fmeter.full i{background:var(--holo-grad);background-size:400% 100%;animation:holo 1.2s linear infinite}
.dopamine .fword{position:absolute;top:24px;left:50%;transform:translateX(-50%);font-weight:700;font-size:13px;color:var(--acc);letter-spacing:.14em;z-index:3;opacity:0;transition:opacity .2s}
.dopamine .fword.on{opacity:1}
/* the fever tint: a strip four stages wide that slides (a transform, so the GPU moves it without
   repainting; sliding the background repainted the whole stage every frame of the fever) */
.dopamine .stage.fever::before{content:'';position:absolute;top:0;bottom:0;left:0;width:400%;background:var(--holo-grad);background-size:50% 100%;animation:feverslide 1.4s linear infinite;opacity:.1;pointer-events:none;z-index:0;will-change:transform}
@keyframes feverslide{to{transform:translateX(-50%)}}
.dopamine .stage.fever{box-shadow:0 0 0 3px color-mix(in srgb,#8a16a8 35%,transparent),0 0 34px color-mix(in srgb,#c2184c 25%,transparent)}
/* words about to die tremble */
/* a word about to burn: its bar pulses red. The word itself stays still, because moving or tilting
   text by fractions of a screen pixel (any scaling but 100%) is what made letters blurry */
.dopamine .fw.low .life{height:5px;animation:lowpulse .5s ease-in-out infinite alternate}
@keyframes lowpulse{from{opacity:1}to{opacity:.35}}
/* coins fly from the kill to the score, which bumps as they land */
.orb{position:fixed;width:11px;height:11px;border-radius:50%;background:var(--oc,#d4a017);box-shadow:inset 0 2px 0 var(--sheen),0 1px 2px rgba(0,0,0,.25);z-index:60;pointer-events:none;transition:transform .55s cubic-bezier(.25,.6,.3,1),opacity .55s;opacity:1}
.orb.rb{background:var(--holo-grad);background-size:400% 100%;animation:holo 1.2s linear infinite}
.strip b.bump{display:inline-block;animation:scorebump .2s} /* the points number, as each orb lands (every game) */
@keyframes scorebump{40%{transform:scale(1.4);color:#b8860b}}
/* fever's final seconds: everything strobes, clutch pops pay extra */
.dopamine .fmeter.clutch i{background:var(--bad);animation:dpulse .22s ease-in-out infinite}
/* embers off every keystroke */
.dopamine .fspark{position:absolute;width:5px;height:5px;border-radius:50%;z-index:4;pointer-events:none;animation:fsparkup .6s ease-out forwards}
@keyframes fsparkup{to{transform:translate(var(--dx,0),-46px) scale(.3);opacity:0}}
/* milestone callouts: the game narrates your streak */
.dopamine .fcall{position:absolute;left:50%;top:38%;transform:translateX(-50%);z-index:5;font-weight:700;font-size:clamp(30px,5.4vw,52px);color:var(--acc);letter-spacing:-.02em;white-space:nowrap;pointer-events:none;animation:fcallin 1.1s cubic-bezier(.2,1.6,.4,1) forwards;text-shadow:0 2px 0 rgba(255,255,255,.7)}
.dopamine .fcall.gold{color:#b8860b}
@keyframes fcallin{0%{transform:translateX(-50%) scale(2.1) rotate(-5deg);opacity:0}14%{transform:translateX(-50%) scale(1) rotate(0);opacity:1}78%{opacity:1}100%{opacity:0}}
/* two cells from fever: the meter can feel it coming */
.dopamine .fmeter.near i.on{animation:dpulse .5s ease-in-out infinite}
/* mystery words: dark caps with a holo "?", rarity hidden until the last letter */
.dopamine .fw.mys .key{background:#2b2b33;color:#fffdf8;border-color:#2b2b33;border-bottom-color:#0d0d10;box-shadow:inset 0 1px 0 rgba(255,255,255,.12)}
.dopamine .fw.mys .key.hit{background:var(--holo-grad);background-size:400% 100%;animation:holo 1.2s linear infinite;border-color:transparent;border-bottom-color:rgba(0,0,0,.4);color:#fff}
.dopamine .fw.mys .life{background:#2b2b33}
.dopamine .fw.mys::before{content:'?';position:absolute;top:-.55em;right:-.45em;width:1.3em;height:1.3em;border-radius:50%;display:grid;place-items:center;font-size:.6em;line-height:1;text-align:center;font-weight:700;color:#fff;background:var(--holo-grad);background-size:400% 100%;animation:holo 1.4s linear infinite;z-index:2}
.dopamine .fw.revealing{z-index:3}
@media (prefers-reduced-motion:reduce){.dopamine .fw,.dopamine .fw.pop,.dopamine .fw.burn,.dopamine .fmeter.full i,.dopamine .fmeter.near i.on,.dopamine .stage.fever::before,.dopamine .fw .key.hit,.dopamine .fspark,.dopamine .fcall,.dopamine .fw.mys::before{animation:none}}

/* the hub card preview: a mystery cap */

/* admin roster editing on /games: the pencil, tiles with controls, the add shelf */
.gwrap{position:relative;width:100%;display:flex;flex-direction:column;align-items:center;gap:var(--s4)}
.gpen{position:absolute;top:-52px;right:0;display:grid;place-items:center;min-width:40px;height:40px;padding:0 12px;font:inherit;font-size:14px;font-weight:700;color:var(--ink);background:var(--cap);border:1px solid var(--cap-line);border-bottom:4px solid var(--cap-edge);border-radius:8px;box-shadow:inset 0 1px 0 var(--hi);cursor:pointer}
.gpen:hover{background:var(--field)}
.gpen.on{background:var(--ink);color:var(--desk);border-color:var(--ink);border-bottom-color:#000}
.gtile{position:relative;display:flex;flex-direction:column;align-items:center;gap:var(--s3);padding:var(--s3);border:2px dashed transparent;border-radius:12px}
.gtile.off{border-color:var(--cap-edge)}
.gtile.off .gcard{opacity:.4}
.gtile.add{border-color:var(--cap-edge);background:color-mix(in srgb,var(--case) 50%,transparent)}
.gtile .gcard{cursor:default}
.gtag{position:absolute;top:8px;left:8px;font-size:11px;font-weight:700;letter-spacing:.12em;color:var(--mute);background:var(--case);padding:2px 8px;border-radius:5px}
.gtools{display:flex;gap:var(--s2);justify-content:center}
.gtools .btn{cursor:pointer}
.gtools .btn.primary{--acc:var(--blue)}
.gshelf{display:flex;flex-direction:column;align-items:center;gap:var(--s3);width:100%;margin-top:var(--s4);padding-top:var(--s4);border-top:1px solid var(--cap-edge)}
.gshelf h3{margin:0;font-size:15px;color:var(--mute);font-weight:700}
.gstatus{position:absolute;left:0;right:0;bottom:-1.9em;margin:0;min-height:1.5em;font-size:14px;color:var(--mute);text-align:center;pointer-events:none} /* admins only: out of the layout, so it never shrinks the games when it appears */

/* admin tools: a section at the bottom of the sound pack dropdown */
.tmsec{display:flex;flex-direction:column;gap:8px;width:240px;margin-top:4px;padding:10px 8px 4px;border-top:1px solid var(--cap-line)}
.tmpanel{display:flex;flex-direction:column;gap:8px}
.tmhead{font-weight:700;font-size:14px;color:var(--ink)}
.tmrow{display:flex;align-items:center;gap:8px;font-size:14px;color:var(--mute)}
.tmrow input{width:96px;font:inherit;font-size:15px;height:34px;padding:0 10px;border:1px solid var(--cap-line);border-bottom:3px solid var(--cap-edge);border-radius:6px;background:var(--field);color:var(--ink)}
.tmchips{display:flex;flex-wrap:wrap;gap:5px}
.tmchip{font:inherit;font-size:12px;padding:3px 8px;border:1px solid var(--cap-line);border-bottom:2px solid var(--cap-edge);border-radius:5px;background:var(--cap);color:var(--ink);cursor:pointer}
.tmchip:hover{background:var(--case)}
.tmnote{margin:0;font-size:12px;color:var(--mute);line-height:1.45}
.tmsec .btn{height:34px;cursor:pointer}
.tmsec .btn.primary{--acc:var(--blue)}
.tmsec .tmitem.live{color:var(--bad);font-weight:700}

/* performance slider, low end: hold the costliest animations still */
:root[data-perf="1"] .dopamine .stage.fever::before,:root[data-perf="2"] .dopamine .stage.fever::before,
:root[data-perf="1"] .tier-rainbow .key,:root[data-perf="1"] .tier-rainbow .key.hit,
:root[data-perf="1"] .dopamine .fw.mys .key.hit,:root[data-perf="1"] .dopamine .fw.mys::before,
:root[data-perf="1"] .dopamine .fmeter.full i,:root[data-perf="1"] .dopamine .fw.low .life,
:root[data-perf="1"] .dopamine .fw.pop::after{animation:none}
.tmrange{flex:1;min-width:0;accent-color:var(--blue);cursor:pointer}
/* about page: plain reading text */
.prose{max-width:56ch;text-align:left;display:flex;flex-direction:column;gap:var(--s3)}
.prose p{margin:0;font-size:18px;line-height:1.55}
/* games: signed-out players see this on the stage instead of the game */
.gate{display:flex;flex-direction:column;align-items:center;gap:var(--s3);text-align:center}
.gate h2{font-size:clamp(28px,3.8vw,38px);font-weight:700;margin:0;letter-spacing:-.02em}
.gate p{margin:0;color:var(--mute);font-size:18px}
.gatebtns{display:flex;gap:var(--s3);flex-wrap:wrap;justify-content:center;margin-top:var(--s2)}
.gatebtns .btn{height:44px;padding:0 22px;font-size:16px}
.gate .btn.primary{--acc:var(--blue)} /* the site's blue, not the game colour */
/* leaderboard names open the player's page; hovering bolds them */
.lb a.nm{color:inherit;text-decoration:none}
.lb a.nm:hover{font-weight:700;color:var(--ink)}
/* account page: shown when the account's email has bought Chorana, in rainbow lettering */
/* the account labels hold still: the gradient is spread once across the words, never moving */
.lifetag{display:inline-block;margin-left:10px;font-size:13px;font-weight:700;color:transparent;background:var(--holo-grad);background-size:100% 100%;-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}
/* owners: your name on /account is rainbow, like the buy button (Buy stays in the nav for everyone: it sells nametags too) */
.ch-own .acctname.mine .key,.acctname.own .key{color:#fff;font-weight:700;text-shadow:0 1px 2px rgba(0,0,0,.7),0 0 4px rgba(0,0,0,.45);border-color:transparent;background:var(--holo-grad);background-size:400% 100%;animation:holo 2.2s linear infinite;border-bottom-color:rgba(0,0,0,.4)}
@media (prefers-reduced-motion:reduce){.ch-own .acctname.mine .key,.acctname.own .key{animation:none}}
/* owners: the buy button becomes a plain thank-you */
.buythanks{display:none;margin:0;font-size:22px;font-weight:700}
/* rainbow lettering, shimmering like the buy button it replaces (beats the grey .price p) */
.ch-in.ch-own .buythanks{display:block;color:transparent;background:var(--holo-grad);background-size:400% 100%;-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;animation:holo 2.4s linear infinite}
@media (prefers-reduced-motion:reduce){.ch-in.ch-own .buythanks{animation:none}}
.ch-in.ch-own .buybtn{display:none}
.lifetag.solo{margin-left:0}
/* browser autofill paints saved logins pale blue: keep the cream input instead (a wide inset
   shadow covers the browser's own background, which pages can't set directly) */
input:-webkit-autofill,input:-webkit-autofill:hover,input:-webkit-autofill:focus{-webkit-box-shadow:inset 0 0 0 1000px var(--field);box-shadow:inset 0 0 0 1000px var(--field);-webkit-text-fill-color:var(--ink);caret-color:var(--ink)}
input:autofill{-webkit-box-shadow:inset 0 0 0 1000px var(--field);box-shadow:inset 0 0 0 1000px var(--field);-webkit-text-fill-color:var(--ink);caret-color:var(--ink)} /* separate rule: one unknown selector would drop the whole list */
/* account page: a quiet gray link under the email */
.forgot{background:none;border:0;padding:0;margin:-6px 0 0;font:inherit;font-size:13px;color:var(--mute);cursor:pointer}
.forgot:hover:not(:disabled){color:var(--ink);text-decoration:underline}
.forgot:disabled{cursor:default}
/* the restart hint under each game: big enough to notice */
.shell .hint{font-size:18px;color:var(--ink);align-items:center}
.shell .hint kbd{font-size:16px;height:1.8em;min-width:2em;padding:0 .55em;border-bottom-width:3px;margin-right:4px}
/* games: a bigger game name and start button, and under the stage how it works, then the hotkeys */
.strip .name{font-size:34px;letter-spacing:-.02em}
.stage .row-actions .btn{height:56px;padding:0 40px;font-size:22px;border-radius:8px;border-bottom-width:5px}
/* the start button (Start, Play again): a rare tag of a button. A holographic shimmer through the game's own
   colour and pink, cyan and gold, a bright sheen sweeping across, a soft glow breathing around it, and it
   still presses down like a keycap. Dark mode turns the glow to neon */
.stage .row-actions .btn.primary.go{position:relative;overflow:hidden;isolation:isolate;color:#fff;font-weight:700;letter-spacing:.02em;text-shadow:0 1px 2px rgba(0,0,0,.45),0 0 6px rgba(0,0,0,.2);border:0;border-bottom:5px solid color-mix(in srgb,var(--acc) 55%,#000);
  background:linear-gradient(100deg,var(--acc) 0%,color-mix(in srgb,var(--acc) 55%,#ff5fd2) 25%,color-mix(in srgb,var(--acc) 55%,#38d9ff) 50%,color-mix(in srgb,var(--acc) 55%,#ffcf3f) 75%,var(--acc) 100%);background-size:400% 100%;
  box-shadow:0 6px 18px color-mix(in srgb,var(--acc) 35%,transparent);animation:holo 5s linear infinite,gobreathe 2.4s ease-in-out infinite;transition:transform .06s,border-bottom-width .06s,filter .2s}
.stage .row-actions .btn.primary.go::after{content:'';position:absolute;top:-30%;bottom:-30%;left:-45%;width:32%;z-index:-1;transform:skewX(-20deg);background:linear-gradient(90deg,transparent,rgba(255,255,255,.7),transparent);animation:gosheen 2.6s ease-in-out infinite;pointer-events:none}
@keyframes gosheen{0%,50%{left:-45%}82%,100%{left:125%}}
@keyframes gobreathe{50%{box-shadow:0 8px 30px color-mix(in srgb,var(--acc) 60%,transparent),0 0 16px color-mix(in srgb,var(--acc) 40%,transparent)}}
.stage .row-actions .btn.primary.go:hover{filter:brightness(1.08) saturate(1.2)}
.stage .row-actions .btn.primary.go:hover::after{animation-duration:1.3s}
.stage .row-actions .btn.primary.go:active{transform:translateY(3px);border-bottom-width:2px}
.ch-dark .stage .row-actions .btn.primary.go{animation:holo 5s linear infinite,gobreathedark 2.4s ease-in-out infinite}
@keyframes gobreathedark{0%,100%{box-shadow:0 0 14px color-mix(in srgb,var(--acc) 55%,transparent),0 0 2px var(--acc)}50%{box-shadow:0 0 34px var(--acc),0 0 10px var(--acc)}}
@media (prefers-reduced-motion:reduce){.stage .row-actions .btn.primary.go,.stage .row-actions .btn.primary.go::after{animation:none}}
.shell .hint{flex-direction:column;align-items:flex-start;gap:var(--s3)}
.shell .hint .gabout{margin:0;font-size:17px;line-height:1.5;color:var(--mute);max-width:72ch}
.shell .hint .gkeys{display:flex;align-items:center}
/* end screen: the count, stats sized to sit with it, play again + share */
.over{justify-content:safe center} /* too tall for a short screen: start at the top and scroll, never clip */
.over .big{font-size:clamp(56px,9vw,84px);letter-spacing:-.03em;font-variant-numeric:tabular-nums}
.over .unit{font-size:17px;margin-top:-10px}
.over .tbl{font-size:17px;gap:6px var(--s4)}
.over .row-actions{margin-top:var(--s2)}
.stage .row-actions .btn.sharebtn{background:var(--cap);color:var(--ink);border:1px solid var(--cap-line);border-bottom:5px solid var(--cap-edge)}
/* inside the stage everything is centred: the start screen, the end screen and the log-in gate.
   Under the stage, the leaderboards read from the left */
.front{display:flex;flex-direction:column;align-items:center;gap:14px;z-index:2}
.lbzone{text-align:center} /* the leaderboards under a game: centred, three boards in one row */
.lbzone .lbrow{justify-content:center;flex-wrap:nowrap;gap:var(--s4)}
@media (max-width:800px){.lbzone .lbrow{flex-wrap:wrap}}
/* the leaderboards under each game: bigger, to sit with the game above them */
.lbzone .lbrow .lb{flex:1 1 0;width:auto;min-width:0;max-width:340px} /* three boards side by side, sharing the row: your friends, all-time, 24 hours */
@media (max-width:800px){.lbzone .lbrow .lb{flex:1 1 100%;max-width:none}}
/* a board's empty message: one line of text, its link a quiet underline in the ink colour */
.lb li.mute .lbmsg{display:block}
.lb li.mute a{color:var(--ink);text-decoration:underline;text-decoration-color:var(--cap-edge);text-underline-offset:3px}
.lb li.mute a:hover{text-decoration-color:currentColor}
.lbzone .lb h4{font-size:14px}
.lbzone .lb ol{gap:9px}
.lbzone .lb li{font-size:18px;gap:11px}
.lbzone .lb li::before{width:27px;height:27px;font-size:14px;border-radius:5px;border-bottom-width:3px}
.lbzone .lb .tm{font-size:13px;min-width:38px}
.lbzone .lb li.mute{font-size:16px}
/* developers get rights (the admin tools), not a look: no red, no tag, no hidden Buy */
/* the nav reads at the size of the page, not smaller: 17px links and a matching Buy (phones keep 15px so it fits) */
.nav{font-size:17px}
.nav a.btn{height:40px}
@media (max-width:640px){.nav{font-size:15px}.nav a.btn{height:36px}}
/* hovering a leaderboard name always answers: gray names darken (beating the gray rule above),
   owners' rainbow and developers' red brighten and shimmer faster, since their letters are see-through */
.lb li:not(.mute) a.nm:hover{color:var(--ink)}
.lb li.pro a.nm:hover{filter:brightness(1.25) saturate(1.3);animation-duration:.8s}
/* ---------- Dopamine on the dark site (html.ch-dark, toggled on /account) ----------
   The stage goes darker than the rest of the site: a black machine where every colour glows.
   Words wear their tier's neon twin (NEON in chorana.js) as --acc. */
.ch-dark .dopamine .stage{--case:#000;--ink:#f5f7ff;--mute:#a3a9c2;--cap:#12121c;--cap-line:#2b2b40;--cap-edge:#000;background:var(--case);color:var(--ink);box-shadow:inset 0 0 0 1px #16161f}
/* keycaps: dark glass, lit from inside by the tier colour */
.ch-dark .dopamine .fw:not(.tskin) .key{background:#0d0d16;color:#fff;font-weight:700;border-color:color-mix(in srgb,var(--acc) 75%,transparent);border-bottom-color:var(--acc);box-shadow:0 0 12px color-mix(in srgb,var(--acc) 55%,transparent),inset 0 0 8px color-mix(in srgb,var(--acc) 30%,transparent);text-shadow:0 0 8px var(--acc),0 0 2px var(--acc)}
.ch-dark .dopamine .fw:not(.tskin) .key.hit{background:var(--acc);color:#05050a;text-shadow:none;border-color:var(--acc);border-bottom-color:color-mix(in srgb,var(--acc) 60%,#fff);box-shadow:0 0 22px var(--acc),0 0 6px var(--acc)}
.ch-dark .dopamine .fw .life{background:var(--acc);box-shadow:0 0 8px var(--acc)}
.ch-dark .dopamine .fw.low .life{background:#ff2a4a;box-shadow:0 0 10px #ff2a4a}
/* mystery words: dark violet caps with a white glow, still keeping their secret */
.ch-dark .dopamine .fw:not(.tskin).mys .key{background:#1a1030;color:#fff;border-color:#b98cff;border-bottom-color:#7a4dff;box-shadow:0 0 14px rgba(160,110,255,.55);text-shadow:0 0 8px #c9a8ff}
.ch-dark .dopamine .fw.mys .life{background:#b98cff;box-shadow:0 0 8px #b98cff}
/* the rainbow tier: brighter still, with a halo */
.ch-dark .dopamine .fw:not(.tskin).tier-rainbow .key{filter:brightness(1.35) saturate(1.4);box-shadow:0 0 22px rgba(255,80,200,.7)}
/* fever on the dark site: a celebration you can still read through. The rainbow lives only at the
   stage's rim, faint and slow, so the black middle stays black and the words stay the brightest thing
   on screen; the stage gets a thin magenta edge with a soft glow, the meter lights without halos */
.ch-dark .dopamine .fmeter i{background:#2a2a42}
.ch-dark .dopamine .fmeter i.on{background:#ff3dff;box-shadow:0 0 5px rgba(255,61,255,.55)}
.ch-dark .dopamine .fmeter.full i{filter:none;box-shadow:none}
.ch-dark .dopamine .fword{color:#ff3dff;text-shadow:0 0 6px rgba(255,61,255,.55)}
.ch-dark .dopamine .stage.fever::before{opacity:.16;filter:none;animation-duration:6s;-webkit-mask:radial-gradient(ellipse 80% 75% at 50% 50%,transparent 55%,#000 100%);mask:radial-gradient(ellipse 80% 75% at 50% 50%,transparent 55%,#000 100%)}
.ch-dark .dopamine .stage.fever{box-shadow:0 0 0 1px rgba(255,61,255,.45),0 0 22px rgba(255,61,110,.18)}
.ch-dark .dopamine .stage.fever .confetti{filter:none} /* the fever drizzle, at its own colours rather than boosted */
/* callouts, score pops, combo and cues glow instead of sitting on paper */
.ch-dark .dopamine .fcall{text-shadow:0 0 18px var(--acc),0 0 4px var(--acc)}
.ch-dark .dopamine .fcall.gold{color:#ffe11a;text-shadow:0 0 18px #ffe11a}
.ch-dark .dopamine .spop{text-shadow:0 0 12px currentColor,0 0 3px currentColor}
.ch-dark .dopamine .combo.h1{color:#ffe11a;text-shadow:0 0 10px #ffe11a}
.ch-dark .dopamine .combo.h2{color:#ff3d6e;text-shadow:0 0 12px #ff3d6e}
.ch-dark .dopamine .confetti{filter:brightness(1.3) saturate(1.4)}
.ch-dark .dopamine .fspark{box-shadow:0 0 8px rgba(255,255,255,.8)}
/* coins fly over the page itself, so they glow wherever they are */
.ch-dark .orb{background:var(--oc,#ffe11a);box-shadow:0 0 10px var(--oc,#ffe11a),0 0 3px var(--oc,#ffe11a)}
@media (prefers-reduced-motion:reduce){.ch-dark .dopamine .stage.fever::before{animation:none}}
/* the lowest performance level keeps the neon colours but drops the per-word glow */
:root[data-perf="1"].ch-dark .dopamine .fw:not(.tskin) .key,:root[data-perf="1"].ch-dark .dopamine .fw:not(.tskin) .life{box-shadow:none;text-shadow:none}
:root[data-perf="1"].ch-dark .dopamine .fw:not(.tskin).tier-rainbow .key{filter:none}
/* account settings: one row per setting, its control on the right */
section.acctset{width:100%;max-width:720px;margin-left:auto;margin-right:auto}
.setrow{width:100%;box-sizing:border-box;display:flex;align-items:center;justify-content:space-between;gap:var(--s4);padding:var(--s3) 0;text-align:left}
.settxt{display:flex;flex-direction:column;gap:4px}
.settxt b{font-size:17px;font-weight:700}
.settxt span{font-size:14px;color:var(--mute)}
.ch-dark .dopamine .over .big{color:#ff3da0;text-shadow:0 0 22px #ff3da0,0 0 6px #ff3da0}
.ch-dark .dopamine .over .tbl b.new{color:#ffe11a;text-shadow:0 0 10px #ffe11a}
/* ---------- dark mode: the whole site (html.ch-dark, set before the first paint) ----------
   The three surfaces go night-black, ink turns bright, and every accent is pushed to neon */
:root.ch-dark{
  --desk:#07070d; --case:#121220; --cap:#1c1c2e; --cap-edge:#05050a; --cap-line:#2e2e4a; --blue:#3d8bff;
  --ink:#f4f6ff; --mute:#a5abc6; --bad:#ff3b5c;
  --rain:#2eb0ff; --invade:#a970ff; --wordis:#16f2c6; --dopamine:#ff3da0;
  --holo-grad:linear-gradient(100deg,#ff2d75,#ffb81a,#2bff88,#1fe0ff,#4d7dff,#d43dff,#ff2d75);
  --hi:rgba(255,255,255,.07); --sheen:rgba(255,255,255,.05); --field:#141424;
  color-scheme:dark;
}
/* chart grid lines and the floor: drawn in the cap edge, which is black here */
.ch-dark .schart line{stroke:var(--cap-line)}
/* game titles and rainbow lettering shine */
/* gray names and caps (non-owners) are lifted so they read on black */
.ch-dark .lb li:not(.mute):not(.pro):not(.dev) .nm{color:#9aa1b8}
.ch-dark .lb li:not(.mute):not(.pro):not(.dev)::before{background:#3a3e4c;border-bottom-color:#22252f}
.ch-dark .lb li:not(.mute):not(.pro):not(.dev) a.nm:hover{color:var(--ink)}
.ch-dark .pv-dopamine .pplus{color:#ffe11a;text-shadow:0 0 8px #ffe11a}
.ch-dark .pv-dopamine .ppop.mys .plife{background:#b98cff}
/* ---------- dark mode readability ----------
   The glow lives around the keycaps, never on the letters: coloured text-glow bloomed into
   the light neons (green, yellow, white) and blurred them. Letters stay crisp. */
/* words waiting to be typed: crisp white letters on dark glass, a soft rim of the tier colour */
.ch-dark .dopamine .fw:not(.tskin) .key{text-shadow:0 1px 2px rgba(0,0,0,.85);box-shadow:0 0 8px color-mix(in srgb,var(--acc) 32%,transparent),inset 0 0 5px color-mix(in srgb,var(--acc) 14%,transparent)}
/* typed letters light up: the whole key fills solid neon in the word's colour, dark letter on top,
   like a pressed key on the home keyboard. Same in every game; rainbow and unrevealed mystery
   words keep their shimmer */
.ch-dark .dopamine .fw:not(.tskin):not(.tier-rainbow):not(.mys):not(.burn) .key.hit,
.ch-dark .rain .drop:not(.tskin):not(.tier-rainbow) .key.hit,
.ch-dark .invade .inv:not(.tskin):not(.tier-rainbow) .key.hit,
.ch-dark .wordis .brick:not(.tskin):not(.tier-rainbow) .key.hit{background:var(--acc);color:#07070d;font-weight:700;text-shadow:none;border-color:var(--acc);border-bottom-color:color-mix(in srgb,var(--acc) 60%,#000);box-shadow:0 0 16px color-mix(in srgb,var(--acc) 75%,transparent),0 0 4px var(--acc)}
.ch-dark .dopamine .fw:not(.tskin).mys .key{text-shadow:0 1px 2px rgba(0,0,0,.85);box-shadow:0 0 9px rgba(160,110,255,.4)}
.ch-dark .dopamine .fw:not(.tskin).tier-rainbow .key{filter:brightness(1.15) saturate(1.3);box-shadow:0 0 14px rgba(255,80,200,.5)}
/* accent keycaps and primary buttons: dark letters on the bright fill (white vanished on green and cyan). The name keycaps on /account and player pages keep their own colours */
.ch-dark .key.acc:not(.acctname .key){color:#07070d}
.ch-dark .btn.primary:not(.holo):not(.buybtn){color:#07070d}
.ch-dark .open .sets > .key.acc{color:#07070d}
/* white lettering on the brighter rainbow gets a firmer shadow */
.ch-dark .btn.primary.buybtn,.ch-dark .btn.primary.holo,.ch-dark .nav a.btn.primary,.ch-dark .tier-rainbow .key,.ch-dark .ch-own .acctname.mine .key,.ch-dark .acctname.own .key{text-shadow:0 1px 2px rgba(0,0,0,.95),0 0 5px rgba(0,0,0,.7)}
/* glowing headline numbers and words: a tighter, dimmer halo so the shapes stay sharp */
.ch-dark .dopamine .fcall{text-shadow:0 0 10px color-mix(in srgb,var(--acc) 60%,transparent)}
.ch-dark .dopamine .fcall.gold,.ch-dark .dopamine .combo.h1{text-shadow:0 0 8px rgba(255,225,26,.5)}
.ch-dark .dopamine .spop{text-shadow:0 1px 2px rgba(0,0,0,.9),0 0 8px color-mix(in srgb,currentColor 45%,transparent)}
.ch-dark .dopamine .over .big{text-shadow:0 0 14px rgba(255,61,160,.55)}
.ch-dark .dopamine .over .tbl b.new{text-shadow:0 0 6px rgba(255,225,26,.45)}
.ch-dark .dopamine .fword{text-shadow:0 0 6px rgba(255,61,255,.6)}
/* rainbow and mystery typed letters keep their shimmer on dark (the neon-hit rules above would flatten them) */
.ch-dark .dopamine .fw:not(.tskin).tier-rainbow .key.hit,.ch-dark .dopamine .fw:not(.tskin).mys .key.hit{background:var(--holo-grad);background-size:400% 100%;animation:holo 1s linear infinite;color:#fff;font-weight:700;border-color:transparent;border-bottom-color:rgba(0,0,0,.5);text-shadow:0 1px 2px rgba(0,0,0,.95),0 0 5px rgba(0,0,0,.7);box-shadow:0 0 12px rgba(255,80,200,.45)}
@media (prefers-reduced-motion:reduce){.ch-dark .dopamine .fw:not(.tskin).tier-rainbow .key.hit,.ch-dark .dopamine .fw:not(.tskin).mys .key.hit{animation:none}}
/* the logo's black keycap: a plain edge so it doesn't vanish into the dark page */
.ch-dark .brand::before{box-shadow:0 0 0 1px #34345a}
/* dark mode keeps the site plain; only what you press and the words you type are neon.
   The home keyboard's pressed keys and the mouse buttons light up in a neon colour */
.ch-dark .brow .key.down{background:var(--press,#1fc8ff);color:#07070d;border-color:var(--press,#1fc8ff);border-bottom-color:var(--press,#1fc8ff)}
.ch-dark .mouse .mb.down{background:#ff3dff}
/* every game gets Dopamine's dark stage: a black machine with dark-glass keycaps and bold white
   letters, rimmed in the word's colour (Dopamine's own rules above add its fever and meters) */
.ch-dark .shell .stage{--case:#000;--cap:#12121c;--cap-line:#2b2b40;--cap-edge:#000;background:var(--case);box-shadow:inset 0 0 0 1px #16161f}
.ch-dark :is(.rain .drop:not(.tskin):not(.tier-rainbow),.invade .inv:not(.tskin):not(.tier-rainbow),.wordis .brick:not(.tskin):not(.tier-rainbow)) .key{background:#0d0d16;color:#fff;font-weight:700;border-color:color-mix(in srgb,var(--acc) 75%,transparent);border-bottom-color:var(--acc);text-shadow:0 1px 2px rgba(0,0,0,.85)}
/* every word in every game carries a slight glow of its colour on the dark site: around the keycaps,
   never on the letters (typed keys glow a touch more, set above). The lowest performance level drops it */
.ch-dark :is(.rain .drop:not(.tskin),.invade .inv:not(.tskin),.wordis .brick:not(.tskin),.dopamine .fw:not(.tskin)) .key{box-shadow:0 0 12px color-mix(in srgb,var(--acc) 60%,transparent),0 0 3px color-mix(in srgb,var(--acc) 50%,transparent)}
:root[data-perf="1"].ch-dark :is(.rain .drop:not(.tskin),.invade .inv:not(.tskin),.wordis .brick:not(.tskin),.dopamine .fw:not(.tskin)) :is(.key,.key.hit){box-shadow:none}
/* phones: the nav wraps onto a second line instead of pushing the page wider than the screen */
@media (max-width:640px){.nav{flex-wrap:wrap;row-gap:2px;column-gap:var(--s3)}}
/* the nav's rainbow Buy keeps white letters in dark mode (the dark-letters rule for bright buttons skips it) */
.ch-dark #navbuy.btn.primary{color:#fff}
/* leaderboards: the box and its preset rows are simply there from the first paint; when the scores
   arrive, the names flick up very quickly from #1 down (movement only, never scaling, so text stays sharp) */
@keyframes lbin{from{opacity:0;transform:translateY(6px)}}
.lb li:not(.ghost):not(.blank){animation:lbin .18s cubic-bezier(.2,.8,.3,1) both}
.lb li:nth-child(2){animation-delay:.015s}.lb li:nth-child(3){animation-delay:.03s}.lb li:nth-child(4){animation-delay:.045s}
.lb li:nth-child(5){animation-delay:.06s}.lb li:nth-child(6){animation-delay:.075s}.lb li:nth-child(7){animation-delay:.09s}
.lb li:nth-child(8){animation-delay:.105s}.lb li:nth-child(9){animation-delay:.12s}.lb li:nth-child(10){animation-delay:.135s}
@media (prefers-reduced-motion:reduce){.lb li{animation:none!important}}
/* one footer on every page (siteFooter in chorana.js): the wrap fills the window, main takes the slack,
   so the footer is always the same distance from the bottom, and its links are evenly spaced */
.wrap{min-height:100dvh;box-sizing:border-box;display:flex;flex-direction:column;padding-bottom:0}
.wrap > main{flex:1 0 auto}
/* the footer is a band in the case colour, full window width (a shadow paints it past the wrap
   without widening the page) and running down to the bottom edge */
footer.sitefoot{margin-top:var(--s7);padding:var(--s4) 0;gap:var(--s4);justify-content:center;align-items:center;background:var(--case);box-shadow:0 0 0 100vmax var(--case);clip-path:inset(0 -100vmax -100vmax)}
footer.sitefoot a{text-decoration:none}
/* one-screen pages (main.fit: leaderboards, nametags): on desktops and laptops the page is exactly the
   window and never scrolls; main gives up the slack, so the footer sits at the very bottom, in the same
   place as on every other page that fits */
@media (min-width:1000px){
  html:has(main.fit){overflow:hidden}
  .wrap:has(> main.fit){height:100dvh}
  main.fit{flex:1 1 0;min-height:0}
  main.fit + footer.sitefoot{margin-top:2.5vh}
}
/* the homepage scrolls now (it sells the app, top to bottom): its own sizing is in index.html */
/* every page opens the same way, so moving between them is seamless: the title block starts at the
   same height (overriding each page's own padding or centring), the title is the same size, and the
   subtext sits under it in the same grey */
main > .open:first-child{padding-top:var(--s4);justify-content:flex-start}
.open > .lead{font-size:20px;color:var(--mute);margin:0}
/* short windows (a 150%-scaled laptop is about 680px tall inside the browser): every page tightens the
   same shared chrome the same way, so titles still line up from page to page and the one-screen pages fit */
@media (max-height:760px){
  .wrap{padding-top:var(--s3)}
  main > .open:first-child{padding-top:var(--s3)}
  .open{gap:var(--s3)}
  footer.sitefoot{padding:var(--s3) 0}
}
/* ---- game previews keep their proportions at any size: the scenes place things by percent, so the
   keycaps, ship, bars and meter scale with the box too, by --u: 1% of the height, capped by the width near the
   design's shape (120px tall, ~280px wide), so a taller box only adds room, never bigger keys. The home page shrinks the boxes to fit short windows, and with
   fixed-size keys the rows ran into each other ---- */
.gprev{container-type:size;--u:min(1cqh,.7cqw)} /* one unit: 1% of the height, but never more than the width allows, so a tall scene keeps three-letter words clear of each other */
.gprev .pcaps{gap:calc(1.7 * var(--u))}
.gprev .ntag{font-size:calc(10 * var(--u))} /* the words in nametags: big enough to read at a glance */
/* a short scene (the homepage's): the words at their old size, and the extra words (.pextra) left out, so nothing crowds */
@container (max-height:180px){.gprev .ntag{font-size:calc(8.5 * var(--u))}.gprev .pextra{display:none}}
/* a very tall scene (the games tab, stretched to the footer): more words, the same size, filling the gaps.
   Shorter scenes leave them out; in wordis the pile then sits 11% a row, so the drop is one row too */
@container (max-height:399px){.gprev .pextra2{display:none}}
@container (max-height:239px){.gprev .pmore{display:none}} /* a couple more words (.pmore) from a medium-tall scene up: the homepage's */
/* wordis's extra bricks stack 11% apart, so they need a scene tall enough that bricks that close don't overlap */
@container (max-height:469px){.pv-wordis .pmore{display:none}}
@container (min-height:470px){.pv-wordis .pdrop{animation-name:pvdrop2}}
@keyframes pvdrop2{0%,56%{transform:none}68%,100%{transform:translateY(11%)}}
.gprev .ntag .key{box-shadow:none} /* preview words drawn in nametags */
.gprev .ptag{width:max-content} /* hug the tag, so a word's bar and its centre match the word */
/* the games tab with singleplayer only (multiplayer off): the row of games sits halfway between the title
   and the footer, as wide as the navbar above it (never past its edges), with tall scenes */
@media (min-width:1000px){
  /* the row fills the space between the subtitle and the footer: the scenes grow tall (up to about square),
     and anything left over is shared above and below, so the games sit centred in it */
  main.ghub.solo > .gwrap{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;justify-content:stretch;margin:2.5vh 0 0}
  main.ghub.solo > .gwrap > .games{flex:1 1 auto;min-height:0;align-items:stretch} /* the games run all the way down to the footer */
  main.ghub.solo .gcard{min-height:0;justify-content:center}
  main.ghub.solo .gcard .gprev{flex:1 1 auto;height:auto;min-height:130px}
}
.gprev .pk{min-width:calc(15 * var(--u));height:calc(16.7 * var(--u));padding:0 calc(2.5 * var(--u));font-size:calc(9.6 * var(--u));border-radius:calc(3.3 * var(--u));border-bottom-width:calc(1.7 * var(--u))}
.gprev .pcaps.mys::after{top:calc(-5.8 * var(--u));right:calc(-6.7 * var(--u));width:calc(10.8 * var(--u));height:calc(10.8 * var(--u));font-size:calc(7.5 * var(--u))}
.pv-dopamine .plife{bottom:calc(-5 * var(--u));height:calc(2.5 * var(--u))}
.pv-invade .pship{border-left-width:calc(6.7 * var(--u));border-right-width:calc(6.7 * var(--u));border-bottom-width:calc(10 * var(--u))}
.pv-dopamine .pplus{font-size:calc(10 * var(--u))}
/* ---- leaderboard boxes: equal lengths and pages ----
   Every row is the same height whatever is in it (a tag's rank key, wave lettering, a plain name), and
   short lists are padded with blank rows, so all the boxes on a page come out the same length. Your own
   row is marked, and under each list a small navigator moves ten at a time (‹ 1 2 … 48 ›) */
.lb li{height:22px;flex:none}
.lbzone .lb li{height:30px}
.lb li.blank::before{content:none}
.lb li.me{margin:0 -6px;padding:0 6px;border-radius:6px;background:color-mix(in srgb,var(--acc,#1a56e8) 13%,transparent)}
.lb li.me .nm::after{content:' · you';font-weight:500;color:var(--mute);-webkit-text-fill-color:var(--mute)}
.lbpages{display:flex;justify-content:center;align-items:center;gap:4px;margin-top:4px}
.lbpages.one{visibility:hidden}
.lbpg{display:grid;place-items:center;min-width:22px;height:22px;padding:0 6px;border-radius:5px;background:var(--cap);border:1px solid var(--cap-line);border-bottom:2px solid var(--cap-edge);font:inherit;font-size:11px;font-weight:700;color:var(--ink);cursor:pointer;transition:transform .06s}
.lbpg:active:not(:disabled){transform:translateY(1px)}
.lbpg.on{background:var(--acc,#1b1b1f);color:#fff;border-color:transparent;border-bottom-color:color-mix(in srgb,var(--acc,#1b1b1f) 65%,#000);cursor:default}
.lbpg.mine:not(.on){box-shadow:inset 0 0 0 1.5px var(--acc,#1a56e8)}
.lbpg.step{font-size:13px}
.lbpg:disabled{opacity:.35;cursor:default}
.lbgap{font-size:11px;color:var(--mute);padding:0 1px}
.lbzone .lbpg{min-width:28px;height:28px;font-size:13px}
/* the one-screen leaderboards page sizes everything from the window height, rows and navigator included */
@media (min-width:1000px){
  #boards .lb li{height:clamp(14px,2.05vh,22px)}
  #boards .lbpages{margin-top:.3vh;gap:.4vh}
  #boards .lbpg{min-width:clamp(14px,2vh,22px);height:clamp(14px,2vh,22px);font-size:clamp(8px,1.1vh,11px);padding:0 .5vh}
}
/* the preset rows a board shows before it arrives: its rank keys, faint, and two quiet bars */
.lb li.ghost::before{opacity:.35}
.lbpages.ghost .lbpg{opacity:.35;cursor:default} /* and the navigator under them, just as faint */
.lb li.ghost .gbar{flex:1;height:7px;border-radius:4px;background:color-mix(in srgb,var(--cap-edge) 55%,transparent)}
.lb li.ghost .gbar.gsc{flex:none;width:34px}
.lb li.ghost:nth-child(3n) .gbar:first-of-type{flex:.7}
.lb li.ghost:nth-child(3n+1) .gbar:first-of-type{flex:.85}

/* ---- every word wears the nametag of its rarity (applyTier in chorana.js adds .tskin and the tag's
   classes; nametags.css draws the skin). A word arrives as an outline: plain keys ringed in its colour,
   its letters in that colour. Each letter you type fills in with the skin itself, shimmering, and pops,
   so the word colours in as you go ---- */
.tskin .key{position:relative}
.tskin.tskin.tskin .key:not(.hit){background:var(--cap);background-image:none;color:color-mix(in srgb,var(--acc) 75%,#000);font-weight:700;text-shadow:none;filter:none;border:1px solid var(--cap-line);border-bottom:.12em solid var(--cap-edge);box-shadow:inset 0 0 0 2px var(--acc);animation:none}
.tskin.tskin.tskin .key:not(.hit)::after,.tskin.tskin.tskin .key.hit::after{display:none}
/* typed: the skin shows through, and the key springs up and lands with a ring in its colour bursting out */
.tskin.tskin.tskin .key.hit{transform:translateY(.1em);box-shadow:0 0 14px color-mix(in srgb,var(--acc) 55%,transparent);animation:keyhit .26s cubic-bezier(.2,1.6,.4,1),holo var(--sp,3s) linear infinite}
.tskin.tskin.tskin .key.hit::before{content:'';position:absolute;inset:-3px;border-radius:.32em;border:2px solid var(--acc);opacity:0;pointer-events:none;animation:keyring .4s ease-out}
@keyframes keyhit{0%{transform:translateY(.1em) scale(1)}35%{transform:translateY(-.24em) scale(1.24);filter:brightness(1.55)}100%{transform:translateY(.1em) scale(1);filter:none}}
@keyframes keyring{0%{opacity:.95;transform:scale(.85)}100%{opacity:0;transform:scale(1.75)}}
/* rainbow and mystery words: outlined in hot pink; each typed letter fills with a clean neon rainbow (never
   the muddy prism) and its glow runs through the spectrum */
.tskin.tskin.tskin:is(.tier-rainbow,.mys) .key:not(.hit){color:#b0186f;box-shadow:inset 0 0 0 2px #e0379a,0 0 10px rgba(224,55,154,.35)}
.tskin.tskin.tskin:is(.tier-rainbow,.mys) .key.hit{background:linear-gradient(100deg,#ff2d75,#ffb81a,#2bff88,#1fe0ff,#4d7dff,#d43dff,#ff2d75);background-size:400% 100%;border:0;border-bottom:.14em solid rgba(0,0,0,.45);color:#fff;font-weight:700;text-shadow:0 1px 2px rgba(0,0,0,.9),0 0 5px rgba(0,0,0,.55);animation:keyhit .26s cubic-bezier(.2,1.6,.4,1),holo 1.1s linear infinite,rbglow 1.6s linear infinite}
.tskin.tskin.tskin:is(.tier-rainbow,.mys) .key.hit::before{border-color:#ff3dff}
@keyframes rbglow{0%,100%{box-shadow:0 0 18px rgba(255,45,117,.9),0 0 42px rgba(255,45,117,.35)}25%{box-shadow:0 0 18px rgba(255,184,26,.9),0 0 42px rgba(255,184,26,.35)}50%{box-shadow:0 0 18px rgba(43,255,136,.9),0 0 42px rgba(43,255,136,.35)}75%{box-shadow:0 0 18px rgba(31,224,255,.9),0 0 42px rgba(212,61,255,.4)}}
/* dark mode: the outlines are dark keys with neon rings and neon letters, and a typed letter blazes */
.ch-dark .tskin.tskin.tskin .key:not(.hit){background:#0d0d16;color:var(--acc);border-color:color-mix(in srgb,var(--acc) 35%,transparent);border-bottom-color:#000;box-shadow:inset 0 0 0 2px var(--acc),0 0 10px color-mix(in srgb,var(--acc) 45%,transparent)}
.ch-dark .tskin.tskin.tskin .key.hit{box-shadow:0 0 22px var(--acc),0 0 6px var(--acc)}
.ch-dark .tskin.tskin.tskin .key.hit::before{box-shadow:0 0 10px var(--acc)}
.ch-dark .tskin.tskin.tskin:is(.tier-rainbow,.mys) .key:not(.hit){color:#ff6ad5;box-shadow:inset 0 0 0 2px #ff3dff,0 0 14px rgba(255,61,255,.5)}
@media (prefers-reduced-motion:reduce){.tskin.tskin.tskin .key.hit,.tskin.tskin.tskin:is(.tier-rainbow,.mys) .key.hit{animation:none}.tskin.tskin.tskin .key.hit::before{display:none}}
/* the performance slider's lowest level holds the glow and the rainbow still */
:root[data-perf="1"] .tskin.tskin.tskin :is(.key,.key.hit){box-shadow:none}
:root[data-perf="1"] .tskin.tskin.tskin:is(.tier-rainbow,.mys) .key.hit{animation:none}
/* the flash when a word is cleared: on the dark site a full white flash is blinding, so it is a faint glow in the word's colour */
.ch-dark .flash{background:color-mix(in srgb,var(--acc,#fff) 40%,#fff)}
.ch-dark .flash.on{animation:flashdim .22s}
@keyframes flashdim{0%{opacity:.12}100%{opacity:0}}
/* under every game's Start button: skip to wave 1-6, six small keycaps, the chosen one pressed in */
.wavepick{display:flex;flex-direction:column;align-items:center;gap:8px;margin-top:var(--s3)}
.wavepick .wplabel{font-size:13px;color:var(--mute)}
.wavepick .wpkeys{display:flex;gap:6px}
.wavepick .key{width:34px;height:34px;padding:0;font-size:15px;font-weight:700;cursor:pointer}
.wavepick .key:hover{transform:translateY(1px)}
.wavepick .key.on{background:var(--ink);color:var(--cap);border-color:var(--ink);border-bottom-color:#000;transform:translateY(2px);border-bottom-width:2px}
.ch-dark .wavepick .key.on{background:var(--acc,#fff);color:#07070d}

/* ---- competitive (performance level 1, html[data-perf="1"]): next to no animation, for competitive typists.
   Inside the game nothing moves except the words themselves (their movement is driven by the game, not
   CSS): no shakes, flashes, pops, glows or wobble; floating extras never appear; a cleared word vanishes
   at once instead of popping. Sounds are unchanged ---- */
:root[data-perf="1"] .shell *,:root[data-perf="1"] .shell *::before,:root[data-perf="1"] .shell *::after,:root[data-perf="1"] .shell{animation:none!important;transition:none!important}
:root[data-perf="1"] :is(.spop,.confetti,.fspark,.orb,.fcall,.comboloss,.flash){display:none!important}
:root[data-perf="1"] .shell :is(.pop,.burn,.pull){visibility:hidden!important}

/* the game's full screen button: a square keycap like the rest, its four-corner icon in ink */
.strip .fsbtn{width:36px;height:36px;padding:0;display:inline-grid;place-items:center}
.strip .fsbtn svg{width:18px;height:18px}
/* full screen: the frame fills the screen on the desk colour and centres the game, which keeps its size and
   is magnified as a whole (zoom set in games.html), so it plays exactly as it does in the page */
.fswrap:fullscreen{display:grid;place-items:center;background:var(--desk);overflow:hidden}

/* no hitches: the stage lives on its own GPU layer, so the shake, the swell and the jackpot move it without
   redrawing it; the warm-up box draws one of every effect where nobody can see it (warmFx in chorana.js) */
.shell .stage{will-change:transform}
.fxwarm{position:absolute;left:0;top:0;width:200px;height:120px;opacity:.01;pointer-events:none;z-index:0;overflow:hidden}
.fxwarm>*{animation-play-state:paused!important}
.fxwarm .fw{position:absolute;left:0;top:0}
.fxwarm .fxmini{position:absolute;left:0;top:0;width:180px;height:100px;min-height:0;padding:0}
.wavepick .wpkeys{flex-wrap:wrap;justify-content:center;max-width:100%}
.wavepick .key{width:32px;height:32px;font-size:14px}
/* developer tool, reels / shorts (html.ch-vert, set from the game's tools menu): the game exactly as usual,
   without the line under it (how to play, and the Ctrl Enter keys) */
.ch-vert main:not(.ghub) .shell .hint{display:none}
/* and no edge: the game takes the page's own colour, with no corners, ring or fever glow, so it sits straight
   on the page (the wave tint, painted inline on the stage, gives way too) */
.ch-vert main:not(.ghub) .shell .stage{--case:var(--desk);background:var(--desk)!important;border-radius:0;box-shadow:none!important}
.ch-vert main:not(.ghub) .shell .stage :is(.over,.flash,.wash){border-radius:0}
.ch-vert main:not(.ghub) .shell .stage .flash{display:none} /* no flash on a lost life: it lit the game's box */
/* nothing else may show the box either: the jackpot's chase lights run its edge, the colour wash and the fever's
   rainbow fill it; and in Dopamine nothing is cut off at its edge (confetti, score pops), since every word sits well inside */
.ch-vert main:not(.ghub) .shell .stage :is(.wash,.chasering){display:none}
.ch-vert main:not(.ghub) .dopamine .stage.fever::before{display:none}
.ch-vert main:not(.ghub) .dopamine .stage{overflow:visible}
/* the reels effects (reelFx in chorana.js): round, fading, floating over the page so no edge can clip them */
.rfx{position:fixed;left:0;top:0;pointer-events:none;border-radius:50%;z-index:59;will-change:transform,opacity;opacity:0}
.rfx.ripple{width:110px;height:110px;margin:-55px 0 0 -55px;border:3px solid var(--rc,#fff);animation:rfxripple .5s ease-out forwards}
.rfx.bloom{width:340px;height:340px;margin:-170px 0 0 -170px;background:radial-gradient(circle,var(--rc,#fff) 0,transparent 66%);animation:rfxbloom .7s ease-out forwards}
.rfx.shock{width:640px;height:640px;margin:-320px 0 0 -320px;border:5px solid var(--rc,#fff);animation:rfxshock .8s cubic-bezier(.15,.7,.3,1) forwards}
.rfx.shock.late{border-width:3px;animation-duration:.9s}
.rfx.burst{width:560px;height:560px;margin:-280px 0 0 -280px;background:repeating-conic-gradient(#ffd23f 0 5deg,transparent 5deg 15deg,#fff1b8 15deg 20deg,transparent 20deg 30deg);-webkit-mask:radial-gradient(circle,#000 0,#000 18%,transparent 70%);mask:radial-gradient(circle,#000 0,#000 18%,transparent 70%);animation:rfxburst 1.1s ease-out forwards}
.rfx.bubble{width:var(--sz,16px);height:var(--sz,16px);margin:calc(var(--sz,16px) / -2) 0 0 calc(var(--sz,16px) / -2);background:radial-gradient(circle at 35% 35%,#fff 0,var(--rc,#fff) 40%,transparent 72%);animation:rfxbub 1.6s ease-in-out forwards}
@keyframes rfxripple{from{transform:scale(.2);opacity:.95}to{transform:scale(1);opacity:0}}
@keyframes rfxbloom{from{transform:scale(.3);opacity:.28}to{transform:scale(1.25);opacity:0}} /* a faint glow, not a flare */
@keyframes rfxshock{from{transform:scale(.05);opacity:1}to{transform:scale(1);opacity:0}}
@keyframes rfxburst{0%{transform:scale(.3) rotate(0);opacity:0}15%{opacity:.75}100%{transform:scale(1.15) rotate(50deg);opacity:0}}
@keyframes rfxbub{0%{transform:scale(.3);opacity:0}25%{opacity:.85}100%{transform:translateY(-80px) scale(1);opacity:0}}
/* reels fever: every word glows in its own colour, breathing */
.ch-vert main:not(.ghub) .dopamine .stage.fever .fw:not(.pop):not(.burn)::after{content:'';position:absolute;left:-30%;right:-30%;top:-60%;bottom:-60%;background:radial-gradient(closest-side,color-mix(in srgb,var(--acc) 50%,transparent),transparent);z-index:-1;pointer-events:none;animation:rfxaura .55s ease-in-out infinite alternate;will-change:transform,opacity}
@keyframes rfxaura{from{transform:scale(.85);opacity:.15}to{transform:scale(1.05);opacity:.3}} /* the fever glow behind each word, kept faint */
.fxwarm .rfx{position:absolute;opacity:1;animation:none} /* drawn for real (the box is all but invisible), so the GPU learns them */
.fxwarm .fw.fxglow::after{content:'';position:absolute;inset:-40%;background:radial-gradient(closest-side,color-mix(in srgb,var(--acc,#c2184c) 50%,transparent),transparent);transform:scale(.9)}
:root[data-perf="1"] .rfx,:root[data-perf="1"] .ch-vert .dopamine .stage.fever .fw::after{display:none}
@media (prefers-reduced-motion:reduce){.rfx,.ch-vert .dopamine .stage.fever .fw::after{display:none}}
/* the wave picker's own square: type any wave, lit like the keys when it holds the choice */
/* an input laid out as a grid (like the other keys) parks its text at the left, so this one stays a block */
.wavepick .key.wpown{display:block;box-sizing:border-box;padding:0;text-align:center;font:inherit;font-size:15px;font-weight:700;color:var(--ink);cursor:text;outline:none;caret-color:var(--ink)}
.wavepick .key.wpown::placeholder{color:var(--mute);font-weight:500;display:block;width:100%;text-align:center} /* Chrome sizes the placeholder to its text and parks it left unless it fills the box */
.wavepick .key.wpown:focus{box-shadow:0 0 0 2px var(--ink)}
.wavepick .key.wpown.on{background:var(--ink);color:var(--cap);border-color:var(--ink);border-bottom-color:#000;transform:translateY(2px);border-bottom-width:2px}
.ch-dark .wavepick .key.wpown.on{background:var(--acc,#fff);color:#07070d}
.tmcheck{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:500}
.tmcheck input{width:16px;height:16px;accent-color:var(--ink)}
/* Dopamine: no words pop up when words are cleared, the combo is the only thing said: top right, "combo!" over
   the ×n, lettered in a premium nametag (.ntext, nametags.css), growing a little with each step (--c) */
.dopamine .stage .combo{display:none} /* the shared corner badge gives way to Dopamine's own (it still plays the sounds) */
.dopamine .stage .dcombo{position:absolute;top:12px;right:16px;z-index:4;display:flex;flex-direction:column;align-items:flex-end;gap:2px;line-height:1;pointer-events:none;transform-origin:100% 0;text-align:right}
.dopamine .stage .dcombo[hidden]{display:none}
.dopamine .stage .dcombo .ntext:first-child{font-size:calc(17px + var(--c,2) * .6px);letter-spacing:.02em}
.dopamine .stage .dcombo .ntext:last-child{font-size:calc(29px + var(--c,2) * 2.4px);letter-spacing:-.02em}
.dopamine .stage .dcombo.bump{animation:dcbump .28s cubic-bezier(.2,1.6,.4,1)}
@keyframes dcbump{35%{transform:translateY(-9px) scale(1.16)}65%{transform:translateY(2px) scale(.97)}} /* the combo's words jump on each step: the only thing in Dopamine that does */
/* the praise for a long combo (good, great, amazing, incredible, unbelievable): just above the word that
   reached it, in the combo's nametag, a size bigger at each step; it rises a touch and fades out quickly */
.dopamine .stage .dpraise{position:absolute;z-index:5;pointer-events:none;line-height:1;white-space:nowrap;animation:dpfloat .7s ease-out forwards} /* placed by its top-left corner (praiseSpot) */
.dopamine .stage .dpraise .ntext{letter-spacing:-.01em}
.dopamine .stage .dpraise.l4{font-size:30px}
.dopamine .stage .dpraise.l6{font-size:33px}
.dopamine .stage .dpraise.l8{font-size:36px}
.dopamine .stage .dpraise.l10{font-size:38px}
.dopamine .stage .dpraise.l12{font-size:40px}
@keyframes dpfloat{0%,55%{opacity:1;transform:none}100%{opacity:0;transform:translateY(-16px)}}
@media (prefers-reduced-motion:reduce){.dopamine .stage .dpraise{animation:none}}
@media (prefers-reduced-motion:reduce){.dopamine .stage .dcombo.bump{animation:none}}
.dopamine .stage .fword{display:none}
/* the wave picker under Play again and Share: exactly their width (set when the results are drawn), its
   eleven keys sharing it evenly, square */
.over .wavepick.fit .wpkeys{display:grid;grid-template-columns:repeat(11,minmax(0,1fr));gap:4px;width:100%;max-width:none}
.over .wavepick.fit .key{width:auto;height:auto;aspect-ratio:1;min-width:0;font-size:12px}
.over .wavepick.fit .key.wpown{width:auto;height:auto;aspect-ratio:1}
.dopamine .fmeter{display:none!important} /* no fever meter in Dopamine */

/* everything to do with buying is one purple: the nav's Buy, every buy button, the Stripe checkout and its
   hints, the price on /buy, and the owners' thank-you. Flat, no holo: the rainbow stays for rare things */
:root{--buy:#7c3aed;--buy-edge:#5b21b6}
.ch-dark{--buy:#8b5cf6;--buy-edge:#6d28d9}
.btn.primary.buybtn,.nav a.btn.primary,.ch-dark #navbuy.btn.primary{background:var(--buy);color:#fff;animation:none;text-shadow:none;border-color:var(--buy);border-bottom:4px solid var(--buy-edge)}
.btn.primary.buybtn:hover,.nav a.btn.primary:hover{background:var(--buy);filter:brightness(1.1)}
.btn.primary.buybtn:active,.nav a.btn.primary:active{border-bottom-width:2px}
.ch-dark .btn.primary.buybtn,.ch-dark .nav a.btn.primary{text-shadow:none;box-shadow:0 0 14px color-mix(in srgb,var(--buy) 45%,transparent)}
.ch-in.ch-own .buythanks{background:none;color:var(--buy);-webkit-text-fill-color:var(--buy);animation:none}
#navbuy{white-space:nowrap} /* "buy & download" stays on one line */
/* the nav stays on one line beside the brand on desktops (Home and About live in the footer: the brand is the
   home link). Logged out, log in and create account both show, so the links sit a little closer */
@media (min-width:1040px){
  header{flex-wrap:nowrap}
  html:not(.ch-in) .nav{gap:18px}
}
/* your scores, typing test: a key for each test taken, its best in bold, the one shown pressed in */
.ttkinds{display:flex;flex-wrap:nowrap;gap:6px;margin:2px 0 12px;padding:2px 2px 8px;overflow-x:auto;overscroll-behavior-x:contain;scrollbar-width:thin;scrollbar-color:var(--cap-edge) transparent} /* one line: more tests than fit scroll along it */
.ttkinds::-webkit-scrollbar{height:6px}
.ttkinds::-webkit-scrollbar-thumb{background:var(--cap-edge);border-radius:3px}
.ttkinds .key{flex:none;white-space:nowrap}
.ttkinds .key{height:30px;padding:0 11px;font-size:13px;font-weight:500;cursor:pointer;gap:6px;display:inline-flex;align-items:center}
.ttkinds .key b{font-weight:700}
.ttkinds .key:hover{transform:translateY(1px)}
.ttkinds .key.on{background:var(--acc);color:#fff;border-color:var(--acc);border-bottom-color:color-mix(in srgb,var(--acc) 70%,#000);transform:translateY(2px);border-bottom-width:2px}
.ch-dark .ttkinds .key.on{color:#07070d}
/* a folding score panel is never wider than its row: a long line inside it (the typing test keys) scrolls instead */
.sgame.fold .sgbody{grid-template-columns:minmax(0,1fr)}
/* admins: the nav's Tools menu (supabase-auth.js), styled like the nav links, opening a small keycap panel */
.navtools{position:relative;display:inline-flex;margin-right:-10px} /* the little arrow is lighter than the words: pull the next link in so the words look evenly spaced */
.navtoolsbtn{font:inherit;color:var(--ink);background:none;border:0;border-bottom:2px solid transparent;padding:8px 2px;cursor:pointer;display:inline-flex;align-items:center;gap:4px}
.navtoolsbtn::after{content:'\25BE';display:inline-block;font-size:.75em;opacity:.7;transition:transform .15s}
.navtoolsbtn[aria-expanded=true]::after{transform:rotate(180deg)} /* open, the arrow points back up */
.navtoolsbtn:hover{border-bottom-color:var(--cap-edge)}
.navtools.on .navtoolsbtn{border-bottom-color:var(--ink)}
.navmenu{position:absolute;top:calc(100% + 8px);left:-8px;z-index:40;min-width:170px;display:flex;flex-direction:column;gap:2px;padding:6px;background:var(--cap);border:1px solid var(--cap-line);border-bottom:4px solid var(--cap-edge);border-radius:8px;box-shadow:0 10px 24px rgba(0,0,0,.12)}
.nav .navmenu a{padding:8px 12px;border:0;border-radius:5px;white-space:nowrap;font-size:15px}
.nav .navmenu a:hover,.nav .navmenu a.on{background:var(--case)}
.ch-dark .navmenu{box-shadow:0 10px 24px rgba(0,0,0,.6)}
/* admins: their tools at the bottom of the Typing menu, under a thin rule */
.nav .navmenu a.navadmin.first{position:relative;margin-top:7px}
.nav .navmenu a.navadmin.first::before{content:'';position:absolute;left:8px;right:8px;top:-5px;border-top:1px solid var(--cap-line)}
/* the founders invite (games.html): a keycap card over a dimmed page, the red founders nametag at its heart */
.fdback{position:fixed;inset:0;z-index:80;display:grid;place-items:center;padding:16px;background:rgba(20,16,10,.42);backdrop-filter:blur(3px);animation:fdfade .25s ease-out}
.fdcard{position:relative;box-sizing:border-box;width:min(440px,100%);max-width:100%;padding:26px 26px 22px;text-align:center;background:var(--cap);border:1px solid var(--cap-line);border-bottom:5px solid var(--cap-edge);border-radius:16px;box-shadow:0 24px 60px rgba(60,10,20,.28),0 0 0 1px rgba(200,16,46,.12);animation:fdrise .35s cubic-bezier(.2,1.3,.4,1)}
.fdcard .fdkick{margin:0 0 14px;font-size:15px;font-weight:700;color:#c8102e}
.fdcard .fdtag{display:flex;justify-content:center;margin:4px 0 16px}
.fdcard .fdtag .caps{font-size:min(30px,6.6vw)} /* the whole name fits a phone's width */
.fdcard .fdtext{margin:0 0 10px;font-size:16px;line-height:1.45;color:var(--ink)}
.fdcard .fdleft{margin:0 0 18px;font-size:13px;color:var(--mute)}
.fdcard .fdleft b{color:#c8102e}
.fdcard .fdgo{display:flex;flex-wrap:wrap;gap:10px;justify-content:center}
.fdcard .btn.primary{--acc:#c8102e;font-weight:700} /* --acc is unset outside the game scopes: the founders red */
.fdcard .fdx{position:absolute;top:6px;right:6px;width:40px;height:40px;border:0;background:none;color:var(--mute);font-size:20px;line-height:1;cursor:pointer;border-radius:6px}
.fdcard .fdx:hover{background:var(--case);color:var(--ink)}
.ch-dark .fdback{background:rgba(0,0,0,.6)}
.ch-dark .fdcard .fdkick,.ch-dark .fdcard .fdleft b{color:#ff4d6a}
@keyframes fdfade{from{opacity:0}}
@keyframes fdrise{from{opacity:0;transform:translateY(14px) scale(.97)}}
@media (prefers-reduced-motion:reduce){.fdback,.fdcard{animation:none}}
/* ---- phones and tablets: made for thumbs ---- */
/* the hidden typing input (what a phone's keyboard types into): fixed, so focusing it never jumps the page to
   the top, and 16px, so an iPhone doesn't zoom in when a game or test starts. chorana.js brings whatever is
   being typed into up above the on-screen keyboard */
#kin{position:fixed;font-size:16px}
/* no double-tap zoom, and no tap delay, on anything you press or type into */
a,button,.btn,.key,.stage,.typebox,[role=button]{touch-action:manipulation}
@media (pointer:coarse){
  /* every box you type into is at least 16px: smaller, and an iPhone zooms the page in when you tap it */
  input:not([type=checkbox]):not([type=radio]):not([type=range]),textarea,select{font-size:max(16px,1em)}
  /* the footer links and the leaderboard page keys, big enough for a thumb */
  footer.sitefoot{row-gap:0}
  footer.sitefoot a{padding:10px 6px}
  .lbpages{gap:6px}
  .lbpg,.lbzone .lbpg{min-width:36px;height:36px;font-size:14px}
  .lbpg.step{font-size:18px}
  .frlist .frname{min-height:36px;display:inline-flex;align-items:center}
}
/* a phone's game board (and typing test) fits above its on-screen keyboard, which takes about the bottom 40% */
@media (max-width:640px) and (pointer:coarse){.stage{min-height:clamp(320px,54dvh,1400px)}}
/* the homepage's "Start typing..." always fits a phone's width */
@media (max-width:640px){.typebox.big{font-size:min(clamp(34px,7.6vh,86px),9.4vw)}main.home #games > h2.gsub{word-spacing:.08em}}
/* the name on /account and a profile, with a founder's number before it ("#6", nametags.js founderMark): small,
   in the founders lettering */
.acctnamerow{display:flex;align-items:center;flex-wrap:wrap;gap:6px 12px;max-width:100%}
.acctnamerow > .acctname{min-width:0}
.acctnamerow .fnum{display:inline-flex;flex:none}
.acctnamerow .fnum .ntext{font-size:clamp(15px,1.9vw,19px);font-weight:700;letter-spacing:.02em;opacity:.9}
.acctnamerow .fnum .ntext span{animation:holo 3.4s linear infinite} /* the shimmer only: the number never jumps */
/* ---- the nav on phones: one row, the brand and a menu key (chorana.js); the key opens every link as a panel
   of big rows under the header. Desktop is untouched ---- */
.navbtn{display:none;flex:none;width:46px;height:46px;padding:0;place-items:center;cursor:pointer;color:var(--ink);background:var(--cap);border:1px solid var(--cap-line);border-bottom:4px solid var(--cap-edge);border-radius:9px;touch-action:manipulation}
.navbtn:active{border-bottom-width:2px;transform:translateY(2px)}
.navbtn svg{width:22px;height:22px;overflow:visible}
.navbtn path{fill:none;stroke:currentColor;stroke-width:2.4;stroke-linecap:round;transition:transform .2s,opacity .15s;transform-origin:12px 12px}
header.navopen .navbtn .l1{transform:translateY(5px) rotate(45deg)}
header.navopen .navbtn .l2{opacity:0}
header.navopen .navbtn .l3{transform:translateY(-5px) rotate(-45deg)}
@media (max-width:640px){
  header{flex-wrap:nowrap;position:relative}
  .navbtn{display:inline-grid}
  header:not(.navopen) .nav{display:none}
  header.navopen .nav{position:absolute;top:calc(100% + 8px);left:0;right:0;z-index:70;flex-direction:column;flex-wrap:nowrap;align-items:stretch;gap:2px;padding:8px;font-size:17px;background:var(--cap);border:1px solid var(--cap-line);border-bottom:4px solid var(--cap-edge);border-radius:12px;box-shadow:0 16px 34px rgba(0,0,0,.14);animation:navdrop .16s ease-out}
  header.navopen .nav a:not(.btn){display:block;padding:12px 14px;border:0;border-radius:8px}
  header.navopen .nav a:not(.btn):active,header.navopen .nav a.on{background:var(--case)}
  header.navopen .nav a.btn{top:0;display:flex;align-items:center;justify-content:center;height:48px;margin-top:6px;font-size:17px}
  header.navopen .nav .navtools{display:flex;flex-direction:column;align-items:stretch;margin-right:0}
  header.navopen .nav .navtoolsbtn{padding:12px 14px;border:0;font-size:17px;text-align:left;justify-content:flex-start}
  header.navopen .nav .navmenu{position:static;min-width:0;padding:0 0 4px 14px;background:none;border:0;box-shadow:none}
  .ch-dark header.navopen .nav{box-shadow:0 16px 34px rgba(0,0,0,.6)}
}
@keyframes navdrop{from{opacity:0;transform:translateY(-6px)}}
@media (prefers-reduced-motion:reduce){header.navopen .nav{animation:none}.navbtn path{transition:none}}
/* the scrollbar's room is kept from the start, so a page growing long enough to scroll never nudges everything sideways */
html{overflow-y:auto;scrollbar-gutter:stable} /* the gutter only holds with overflow set */
/* "lifetime ownership" in the singularity lettering (nametags.js lifeOwnership): its letters bob in the wave; the
   spaces keep their width (each letter is its own little box) */
.lifeown{display:inline-block;font-size:14px;font-weight:700;line-height:1.5}
.lifeown .ntext span{white-space:pre}
/* the homepage on a phone: a short note where "Start typing..." would be, in italics, a little larger than the
   games line under it (index.html) */
/* in the Chorana app (html.ch-app, set by the window): its sidebar is the nav, so the site's header and footer go */
html.ch-app .wrap > header:has(#nav),html.ch-app footer.sitefoot{display:none}
html.ch-app .wrap{padding-top:var(--s5);padding-left:max(40px,env(safe-area-inset-left,0px));padding-right:max(40px,env(safe-area-inset-right,0px))}
