:root{
    --bg:#08070a;
    --bg-soft:#100e13;
    --panel:#161219;
    --panel-2:#1d1820;
    --border:#2c2530;
    /* derived from --border rather than a separate hardcoded color —
       theme switching (app.js applyTheme) only updates the base tokens
       above, so anything that doesn't derive from one of them goes stale
       and mismatched the moment a different theme is picked */
    --border-soft:color-mix(in srgb, var(--border), transparent 40%);
    --text:#f3eef7;
    --text-dim:#96899f;
    --text-faint:#493f4e;
    --accent:#ff6b5c;
    --accent-rgb:255,107,92;
    --accent-soft:rgba(255,107,92,0.16);
    --accent-2:#7b6bff;
    --accent-2-rgb:123,107,255;
    --error:#ff5c5c;
    --error-rgb:255,92,92;
    --caret-color:#ff6b5c;
    --caret-rgb:255,107,92;
    --radius:12px;
    --line-height:56px;
    --test-font-size:28px;
    --test-font-family:'JetBrains Mono', 'SFMono-Regular', Consolas, monospace;
    --font-display:'Space Grotesk', sans-serif;
    --font-mono:'JetBrains Mono', 'SFMono-Regular', Consolas, monospace;
    /* shared elevation + surface treatment, used across cards/panels/buttons
       so depth reads consistently instead of every component inventing its
       own shadow stack */
    --shadow-sm:0 1px 2px rgba(0,0,0,0.35);
    --shadow-md:0 8px 24px rgba(0,0,0,0.35), 0 2px 6px rgba(0,0,0,0.3);
    --shadow-lg:0 24px 60px rgba(0,0,0,0.5), 0 6px 18px rgba(0,0,0,0.35);
    --glow-accent:0 0 0 1px rgba(var(--accent-rgb),0.14), 0 6px 18px -8px rgba(var(--accent-rgb),0.32);
    --inset-hi:inset 0 1px 0 rgba(255,255,255,0.05);
    --ease-out:cubic-bezier(.16,1,.3,1);
  }

  *{box-sizing:border-box;}
  html,body{height:100%;}
  body{
    margin:0;
    background:
      radial-gradient(ellipse 80% 50% at 50% -10%, rgba(var(--accent-2-rgb),0.045), transparent 60%),
      var(--bg);
    color:var(--text);
    font-family:var(--font-display);
    -webkit-font-smoothing:antialiased;
    overflow-x:hidden;
    min-height:100vh;
    display:flex;
    flex-direction:column;
    /* Mobile Safari/Chrome show a gray flash on tap by default and allow
       double-tap-to-zoom on interactive elements — both read as laggy on
       a site meant to feel instant. Buttons and the typing zone opt back
       into normal text selection/zoom individually where it's wanted
       (e.g. results text, input fields). */
    -webkit-tap-highlight-color:transparent;
  }
  button, .cfg-btn, .icon-btn, .keycap, a{
    touch-action:manipulation;
  }

  ::selection{ background:var(--accent-soft); color:var(--accent-2); }

  /* Respect the OS-level "reduce motion" setting: keep every themed
     surface visually present (colors, layout, static positions) but
     freeze anything that moves continuously. Lightning strikes and the
     restart-ring/caret still animate on purpose — those are short,
     state-driven feedback (a strike, a keystroke) rather than ambient
     looping motion, so they're left alone. This also meaningfully cuts
     battery/CPU use on lower-powered phones even for people who didn't
     explicitly ask for reduced motion but whose OS defaults to it. */
  @media (prefers-reduced-motion: reduce){
    .space-stars, .star, .filament, .hero-star,
    .camo-blob,
    .anim-spectrum .beam,
    .rain-drop, .storm-clouds,
    .ambient-glow::before, .ambient-glow::after{
      animation:none !important;
    }
  }

  /* ---------- ambient background ---------- */
  .bg-fx{
    position:fixed; inset:0; z-index:0; pointer-events:none;
  }
  .bg-fx::before{
    content:"";
    position:absolute; inset:0;
    background-image:
      linear-gradient(rgba(255,255,255,0.025) 1px, transparent 1px),
      linear-gradient(90deg, rgba(255,255,255,0.025) 1px, transparent 1px);
    background-size:42px 42px;
    mask-image:radial-gradient(ellipse 70% 60% at 50% 20%, black 0%, rgba(0,0,0,0.5) 60%, transparent 100%);
  }
  /* a faint edge vignette pulls the eye back toward the typing canvas
     instead of the far corners of a wide viewport — kept low-opacity
     enough to read as focus rather than a visible dark frame, and works
     the same regardless of which theme (light or dark) is active */
  .bg-fx::after{
    content:"";
    position:absolute; inset:0;
    /* extra middle stop gives the falloff a gentler curve than a plain
       two-stop gradient — reads as a soft graduated dim rather than a
       ring with an edge to it */
    background:radial-gradient(ellipse 85% 70% at 50% 40%, transparent 45%, rgba(0,0,0,0.05) 75%, rgba(0,0,0,0.1) 100%);
  }

  /* Two soft diagonal light sweeps drifting on independent, very slow
     cycles — the background is technically never static, but the motion
     is slow and small enough that nobody watches it happen; it just
     quietly keeps the page from feeling inert. Two different cycle
     lengths means they're never in the same relative position twice in
     a normal session, so it doesn't read as a repeating "pulse".

     Built the same way the spectrum theme's light beams are, not as a
     large radial-gradient circle — a large radial gradient's color steps
     are rings radiating from a center point, and at a large radius each
     ring becomes a wide, obvious arc sweeping across the screen. A
     linear-gradient's bands are thin and evenly spaced along one straight
     axis instead, which is far less visible, and a real blur filter —
     genuine optical smoothing at the pixel level — irons out what's
     left. */
  .ambient-glow{
    position:fixed; inset:0; z-index:0; pointer-events:none; overflow:hidden;
  }
  .ambient-glow::before,
  .ambient-glow::after{
    content:"";
    position:absolute;
    width:260%; height:600px;
    left:-80%; top:-190px;
    /* wider blur than the band's own gradient taper needs on its own —
       the extra softness is what keeps this reading as ambient light
       rather than a visible bar sweeping past */
    filter:blur(95px);
    will-change:transform, opacity;
  }
  .ambient-glow::before{
    background:linear-gradient(100deg, transparent, transparent 30%, rgba(var(--accent-rgb),0.08) 50%, transparent 70%, transparent);
    animation:ambient-drift-a 95s ease-in-out infinite;
  }
  .ambient-glow::after{
    top:auto; bottom:-190px;
    background:linear-gradient(100deg, transparent, transparent 30%, rgba(var(--accent-rgb),0.055) 50%, transparent 70%, transparent);
    animation:ambient-drift-b 130s ease-in-out infinite;
  }
  @keyframes ambient-drift-a{
    0%, 100%{ transform:rotate(-8deg) translateX(0); opacity:0.7; }
    50%{ transform:rotate(-8deg) translateX(8vw); opacity:1; }
  }
  @keyframes ambient-drift-b{
    0%, 100%{ transform:rotate(-8deg) translateX(0); opacity:0.6; }
    50%{ transform:rotate(-8deg) translateX(-7vw); opacity:0.9; }
  }

  /* A large, low-opacity gradient over a dark background is the classic
     case for visible color-step banding: the display only has 256 shades
     per channel to represent a smooth fade, and spread across a big area
     those individual steps become visible bands/edges. A single, very
     faint grain layer breaks up flat color steps by dithering — the same
     technique film grain and photo editing tools use for this — cheap to
     render since it's a static tiled image, not a per-frame recompute. */
  .grain-overlay{
    position:fixed; inset:0; z-index:0; pointer-events:none;
    opacity:0.035;
    mix-blend-mode:overlay;
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='128' height='128'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    background-repeat:repeat;
  }

  .theme-anim-layer{
    position:fixed; inset:0; z-index:0; pointer-events:none; overflow:hidden;
    opacity:0; transition:opacity .5s ease;
  }
  body.anim-voltage .theme-anim-layer,
  body.anim-monsoon .theme-anim-layer,
  body.anim-spectrum .theme-anim-layer,
  body.anim-camo .theme-anim-layer,
  body.anim-space .theme-anim-layer{ opacity:1; }
  .theme-anim-layer > div{ position:absolute; inset:0; display:none; }
  body.anim-voltage .anim-voltage{ display:block; }
  body.anim-monsoon .anim-monsoon{ display:block; }
  body.anim-spectrum .anim-spectrum{ display:block; }
  body.anim-camo .anim-camo{ display:block; }
  body.anim-space .anim-space{ display:block; }

  /* voltage: randomized lightning strikes, driven entirely by JS (see
     strikeLightning), over a slow-drifting storm-cloud wash so the sky
     reads as overcast and heavy even in the gaps between strikes rather
     than plain black. */
  .anim-voltage .storm-clouds{
    position:absolute; inset:-15%;
    background:
      radial-gradient(ellipse 50% 35% at 20% 10%, rgba(70,85,120,0.35), transparent 60%),
      radial-gradient(ellipse 45% 30% at 75% 5%, rgba(50,65,100,0.3), transparent 60%),
      radial-gradient(ellipse 60% 40% at 50% 0%, rgba(35,45,70,0.4), transparent 65%),
      linear-gradient(180deg, #0c0f16 0%, #08090d 55%, #050609 100%);
    /* A touch of blur here does the same job it does for the ambient
       spotlights and the spectrum beams: irons out the ring edges a
       radial gradient produces at this size, rather than trusting the
       overlap between three of them to hide it. The extra inset margin
       (-15% instead of -10%) gives that blur room to fade out before it
       reaches the element's own edge. */
    filter:blur(2px);
    animation:storm-drift 34s ease-in-out infinite;
    will-change:transform, opacity;
  }
  @keyframes storm-drift{
    0%, 100%{ transform:translateX(0) scale(1); opacity:0.9; }
    50%{ transform:translateX(-2%) scale(1.03); opacity:1; }
  }
  .anim-voltage .flash{
    position:absolute; inset:-10%;
    background:radial-gradient(ellipse 55% 45% at 28% 15%, rgba(180,205,255,0.16), transparent 62%);
    filter:blur(3px);
    opacity:0;
    transition:opacity 0.09s ease-out;
  }
  .anim-voltage .flash.strike{ opacity:1; transition:opacity 0.025s ease-out; }
  .anim-voltage .flash.distant{
    background:radial-gradient(ellipse 75% 55% at 50% 100%, rgba(150,170,220,0.1), transparent 68%);
    transition:opacity 0.25s ease-out;
  }
  .anim-voltage .flash.distant.strike{ opacity:1; transition:opacity 0.15s ease-out; }
  .anim-voltage .bolt{
    position:absolute; opacity:0;
    filter:drop-shadow(0 0 4px rgba(200,220,255,0.9)) drop-shadow(0 0 12px rgba(180,205,255,0.6));
    transition:opacity 0.07s ease-out;
  }
  .anim-voltage .bolt.strike{ opacity:1; transition:opacity 0.02s ease-out; }
  .anim-voltage .bolt polyline{
    fill:none; stroke:rgba(215,228,255,0.95); stroke-width:2.2;
    stroke-linejoin:round; stroke-linecap:round;
  }

  /* monsoon: gentle continuous rain, elements generated in JS */
  .rain-drop{
    position:absolute; top:-8%; width:1px; height:70px;
    background:linear-gradient(to bottom, transparent, rgba(150,195,225,0.32));
    animation:rain-fall linear infinite;
  }
  @keyframes rain-fall{
    0% { transform:translateY(-8vh); }
    100% { transform:translateY(112vh); }
  }

  /* spectrum: prismatic light beams sweeping past, dark-matter style shimmer.
     Each beam fades in, sweeps, fades out — opacity and position share the
     same keyframe so the loop point is invisible (nothing snaps). A slow
     hue-rotate on the whole layer gives it that shifting, iridescent quality. */
  .anim-spectrum{
    /* hue-rotate() used to live here, cycling the whole element's colors
       continuously — removed because it forced a full repaint of this
       element and every blurred beam inside it on every single frame,
       the single most expensive animation on the site. But the beams
       were leaning on --accent-rgb/--accent-2-rgb for their color,
       meaning without the hue-rotate they only ever showed two hues
       plus white — not much of a "spectrum" on its own. Fixed by giving
       each beam its own distinct hardcoded color instead of pulling
       from theme variables, so the variety comes from five genuinely
       different colors defined once, not from continuously recomputing
       a filter across the whole frame forever. */
  }
  .anim-spectrum .beam{
    position:absolute; width:150%; height:90px; left:-25%;
    filter:blur(16px);
    opacity:0;
    will-change:opacity, transform;
  }
  .anim-spectrum .beam1{ top:8%;  background:linear-gradient(90deg, transparent, rgba(199,105,255,0.32), transparent); animation:spectrum-sweep 10s ease-in-out infinite; }
  .anim-spectrum .beam2{ top:30%; background:linear-gradient(90deg, transparent, rgba(24,218,248,0.30), transparent); animation:spectrum-sweep 13s ease-in-out infinite -4s; }
  .anim-spectrum .beam3{ top:52%; background:linear-gradient(90deg, transparent, rgba(255,255,255,0.22), transparent); animation:spectrum-sweep 8s ease-in-out infinite -6.5s; }
  .anim-spectrum .beam4{ top:74%; background:linear-gradient(90deg, transparent, rgba(255,110,168,0.28), transparent); animation:spectrum-sweep 15s ease-in-out infinite -2s; }
  .anim-spectrum .beam5{ top:92%; background:linear-gradient(90deg, transparent, rgba(94,234,212,0.26), transparent); animation:spectrum-sweep 11.5s ease-in-out infinite -8s; }
  @keyframes spectrum-sweep{
    0%   { transform:translateX(-40%) rotate(-7deg); opacity:0; }
    18%  { opacity:1; }
    82%  { opacity:1; }
    100% { transform:translateX(40%) rotate(-7deg); opacity:0; }
  }

  /* camo: shifting digital-camo pattern — irregular organic blobs slowly
     drifting and morphing shape, over a base wash that cycles hue, with a
     faint scanline sweep for a tactical/tech edge. Blob positions/sizes
     are randomly generated per load (see buildCamoLayer()). */
  .anim-camo .camo-shift{
    position:absolute; inset:0;
    background:linear-gradient(135deg, #182d1e 0%, #0f2028 50%, #1b1f30 100%);
    /* No longer hue-rotate/saturate animated — like spectrum's old
       filter animation, this forced a repaint of the base layer on
       every frame for as long as the theme was active, for a subtle
       effect the morphing/drifting blobs on top already carry most of
       the visual interest for. The base gradient's own colors were
       nudged slightly richer to compensate for losing the mid-cycle
       saturation boost. */
  }
  .camo-blobs{ position:absolute; inset:0; overflow:hidden; }
  .camo-blob{
    position:absolute;
    animation:camo-morph ease-in-out infinite, camo-drift linear infinite;
  }
  @keyframes camo-morph{
    0%, 100%{ border-radius:42% 58% 65% 35% / 45% 40% 60% 55%; }
    33%{ border-radius:65% 35% 40% 60% / 55% 65% 35% 45%; }
    66%{ border-radius:35% 65% 55% 45% / 40% 55% 45% 60%; }
  }
  @keyframes camo-drift{
    0%   { transform:translate(0,0) scale(1); }
    50%  { transform:translate(4%,-3%) scale(1.06); }
    100% { transform:translate(0,0) scale(1); }
  }

  /* stardust: a dense field of small twinkling stars carries the "space"
     feeling — matching a real deep-sky photo, which is mostly pinprick
     stars with only faint wisps of nebula haze, not bold colored blobs.
     The wash below is intentionally subtle: it should read as texture in
     the blackness, not as a colorful background. */
  .anim-space .space-wash{
    position:absolute; inset:0;
    /* Previously bottomed out at pure #000000 at the edges — meaningfully
       darker than every other theme's background, including its closest
       siblings (voltage, spectrum), which sit around the same base
       lightness as the rest of the site. Since this wash is what shows
       through in the empty space around the UI on this theme, that gap
       was the actual source of space feeling disconnected from
       everything else, not the panel colors themselves (which were
       already in line). Brought the floor up to match. */
    background:
      radial-gradient(ellipse 45% 35% at 20% 15%, rgba(70,55,120,0.22), transparent 60%),
      radial-gradient(ellipse 40% 38% at 82% 70%, rgba(35,45,95,0.24), transparent 62%),
      radial-gradient(ellipse 90% 70% at 50% 50%, #0c0c1a 0%, #080814 60%, #050510 100%);
  }
  .space-filaments{ position:absolute; inset:0; overflow:hidden; mix-blend-mode:screen; }
  .filament{
    position:absolute; border-radius:50%;
    filter:blur(55px);
    opacity:0.16;
    /* The blur itself never changes, only opacity/transform — hinting
       will-change up front commits this to its own GPU-composited layer
       from the first frame instead of the browser deciding mid-animation,
       which is what causes a visible hitch on the first cycle. Without
       this, some browsers re-run the (expensive) blur filter on every
       animated frame instead of blurring once and just moving the
       result. */
    will-change:opacity, transform;
  }
  .filament.f1{ width:50vw; height:28vw; top:-10%; left:3%; background:radial-gradient(ellipse at center, rgba(120,95,190,0.6), transparent 74%); animation:filament-breathe 17s ease-in-out infinite; }
  .filament.f2{ width:42vw; height:32vw; top:40%; left:56%; background:radial-gradient(ellipse at center, rgba(70,100,180,0.55), transparent 74%); animation:filament-breathe 21s ease-in-out infinite -7s; }
  .filament.f3{ width:36vw; height:22vw; top:60%; left:-8%; background:radial-gradient(ellipse at center, rgba(150,80,170,0.45), transparent 74%); animation:filament-breathe 19s ease-in-out infinite -3s; }
  .filament.f4{ width:44vw; height:24vw; top:0%; left:60%; background:radial-gradient(ellipse at center, rgba(90,65,160,0.5), transparent 74%); animation:filament-breathe 23s ease-in-out infinite -11s; }
  @keyframes filament-breathe{
    0%, 100%{ opacity:0.6; transform:scale(1) translate(0,0); }
    50%{ opacity:1; transform:scale(1.1) translate(2%,-2%); }
  }

  .space-stars{ position:absolute; inset:0; animation:starfield-drift 200s linear infinite; }
  .star{
    position:absolute; border-radius:50%;
    background:#ffffff;
    animation:star-twinkle ease-in-out infinite;
    will-change:opacity, transform;
  }
  .star.bright{ box-shadow:0 0 4px 1px rgba(255,255,255,0.75); }
  .star.tinted{ background:#cfe0ff; }
  @keyframes star-twinkle{
    0%, 100%{ opacity:var(--star-min, 0.12); transform:scale(0.75); }
    50%{ opacity:var(--star-max, 1); transform:scale(1); }
  }
  @keyframes starfield-drift{
    0%{ transform:translate(0,0); }
    50%{ transform:translate(-1.8%,-1.2%); }
    100%{ transform:translate(0,0); }
  }

  .hero-star{
    position:absolute; width:22px; height:22px;
    animation:hero-twinkle 4.5s ease-in-out infinite;
  }
  .hero-star svg{ width:100%; height:100%; fill:#ffffff; filter:drop-shadow(0 0 6px rgba(220,228,255,0.9)) drop-shadow(0 0 14px rgba(170,190,255,0.55)); }
  .hero-star.hs1{ top:14%; left:24%; }
  .hero-star.hs2{ top:66%; left:76%; width:16px; height:16px; animation-delay:-2.2s; }
  .hero-star.hs3{ top:42%; left:9%; width:13px; height:13px; animation-delay:-1.1s; }
  .hero-star.hs4{ top:78%; left:30%; width:15px; height:15px; animation-delay:-3.3s; }
  .hero-star.hs5{ top:24%; left:88%; width:18px; height:18px; animation-delay:-0.6s; }
  @keyframes hero-twinkle{
    0%, 100%{ opacity:0.55; transform:scale(0.8) rotate(0deg); }
    45%{ opacity:1; transform:scale(1.2) rotate(5deg); }
    50%{ opacity:1; transform:scale(1.35) rotate(8deg); }
    55%{ opacity:1; transform:scale(1.2) rotate(5deg); }
  }

  a{ color:inherit; }
  button{ font-family:inherit; }

  /* ---------- layout shell ---------- */
  .shell{
    position:relative; z-index:1;
    width:100%;
    max-width:1180px;
    margin:0 auto;
    padding:0 28px;
    flex:1;
    display:flex;
    flex-direction:column;
  }

  /* ---------- topbar ---------- */
  .topbar{
    display:grid;
    grid-template-columns: 1fr auto 1fr;
    align-items:center;
    padding:28px 0 20px;
  }
  .topbar-left{ display:flex; align-items:center; justify-self:start; }
  .brand{ display:flex; align-items:center; gap:12px; cursor:pointer; user-select:none; background:none; border:none; padding:0; }
  .brand-mark{
    display:flex; align-items:center; gap:4px; flex-shrink:0;
  }
  .keycap{
    display:flex; align-items:center; justify-content:center;
    width:34px; height:34px;
    border-radius:9px;
    border:1.5px solid var(--border);
    background:var(--panel);
    box-shadow:var(--inset-hi), var(--shadow-sm);
    transition:border-color .3s var(--ease-out), transform .3s var(--ease-out), background .3s ease, box-shadow .3s ease;
  }
  .keycap span{
    font-family:var(--font-mono); font-weight:600; font-size:16px;
    color:var(--text);
    transition:color .3s ease;
  }
  .brand:hover .keycap{ border-color:rgba(var(--accent-rgb),0.6); background:var(--accent-soft); transform:translateY(-1px); box-shadow:0 1px 0 rgba(255,255,255,0.06) inset, 0 3px 8px rgba(0,0,0,0.35); }
  .brand:hover .keycap span{ color:var(--accent); }
  .brand:hover .keycap-t{ transition-delay:.04s; }
  .brand:active .keycap{ transform:translateY(1px) scale(0.94); box-shadow:0 1px 1px rgba(0,0,0,0.3) inset; }
  .brand-text{ display:flex; flex-direction:column; line-height:1.1; text-align:left; }
  .brand-name{ font-size:22px; font-weight:700; letter-spacing:-0.03em; color:var(--text); }
  .brand-dot{ color:var(--accent); }
  .brand-tag{ font-size:10px; letter-spacing:0.16em; color:var(--text-dim); text-transform:uppercase; margin-top:3px; }

  .site-nav{ display:flex; align-items:center; gap:6px; margin-left:20px; padding-left:20px; border-left:1px solid var(--border); }
  .site-nav .icon-btn{ text-decoration:none; }

  .mode-pill{
    display:flex; align-items:center;
    font-size:12px; letter-spacing:0.1em; text-transform:uppercase;
    color:var(--text-dim);
    padding:7px 14px;
    border:1px solid var(--border);
    border-radius:999px;
    background:var(--bg-soft);
    box-shadow:var(--inset-hi);
    justify-self:center;
    white-space:nowrap;
  }

  .nav-right{ display:flex; align-items:center; gap:10px; justify-self:end; }
  .pb-chip{
    font-family:var(--font-mono);
    font-size:12px; color:var(--text-dim);
    padding:7px 12px;
    border:1px solid var(--border);
    border-radius:999px;
    background:var(--bg-soft);
    box-shadow:var(--inset-hi);
    white-space:nowrap;
  }
  .pb-chip b{ color:var(--accent); font-weight:600; }
  .icon-btn{
    width:36px; height:36px;
    display:flex; align-items:center; justify-content:center;
    border-radius:999px;
    border:1px solid var(--border);
    background:var(--bg-soft);
    box-shadow:var(--inset-hi);
    color:var(--text-dim);
    cursor:pointer;
    transition:color .15s ease, border-color .15s ease, background .15s ease, transform .15s var(--ease-out), box-shadow .15s ease;
  }
  .icon-btn:hover{ color:var(--accent); border-color:rgba(var(--accent-rgb),0.4); background:var(--accent-soft); transform:translateY(-1px); box-shadow:var(--inset-hi), 0 3px 10px -5px rgba(var(--accent-rgb),0.3); }
  .icon-btn:active{ transform:translateY(0) scale(0.92); }
  .icon-btn svg{ width:16px; height:16px; }

  /* ---------- config bar ---------- */
  .config-row{
    display:flex; align-items:center; justify-content:center;
    gap:10px; flex-wrap:wrap;
    margin:10px 0 8px;
  }
  .config-group{
    display:flex; align-items:center;
    background:var(--panel);
    border:1px solid var(--border);
    border-radius:999px;
    padding:4px;
    gap:2px;
    box-shadow:inset 0 1px 3px rgba(0,0,0,0.3);
  }
  .config-group .divider{ width:1px; height:18px; background:var(--border); margin:0 6px; }
  .cfg-btn{
    border:none; background:none; cursor:pointer;
    color:var(--text-dim);
    font-family:var(--font-mono);
    font-size:13px;
    padding:7px 13px;
    border-radius:999px;
    display:flex; align-items:center; gap:6px;
    transition:color .15s ease, background .15s ease, transform .15s var(--ease-out), box-shadow .15s ease;
    white-space:nowrap;
  }
  .cfg-btn:hover{ color:var(--text); }
  .cfg-btn:active{ transform:scale(0.94); }
  .cfg-btn.active{ color:var(--accent); background:var(--accent-soft); box-shadow:var(--inset-hi), 0 2px 6px -3px rgba(var(--accent-rgb),0.32); }
  .cfg-btn.amount-btn.active{ color:var(--accent-2); background:rgba(var(--accent-2-rgb),0.14); box-shadow:var(--inset-hi), 0 2px 6px -3px rgba(var(--accent-2-rgb),0.32); }
  .cfg-btn.toggle.active{ color:var(--accent); background:var(--accent-soft); }
  .cfg-btn svg{ width:13px; height:13px; opacity:0.9; }
  .cfg-icon{ font-family:var(--font-mono); font-weight:600; opacity:0.8; }

  .lang-wrap{ position:relative; display:flex; justify-content:center; margin-bottom:26px; }
  .meta-label{
    text-align:center;
    font-family:var(--font-mono);
    font-size:12px;
    color:var(--text-faint);
    letter-spacing:0.06em;
    min-height:16px;
    background:none; border:none; cursor:default;
    padding:5px 10px;
    border-radius:6px;
    transition:background .15s ease, color .15s ease;
  }
  .meta-label.lang-clickable{ cursor:pointer; color:var(--text-dim); }
  .meta-label.lang-clickable:hover{ background:var(--bg-soft); color:var(--text); }
  .lang-caret{ font-size:9px; margin-left:5px; opacity:0; display:inline-block; width:0; overflow:hidden; }
  .meta-label.lang-clickable .lang-caret{ opacity:0.7; width:auto; margin-left:5px; }

  .lang-panel{
    position:absolute; top:calc(100% + 6px); left:50%; transform:translateX(-50%);
    background:var(--panel); border:1px solid var(--border); border-radius:12px;
    padding:10px; z-index:30;
    display:flex; flex-direction:column; gap:8px;
    width:max-content; max-width:min(90vw, 640px);
    box-shadow:var(--inset-hi), var(--shadow-lg);
  }
  .lang-search{
    width:100%; background:var(--bg-soft); border:1px solid var(--border);
    border-radius:6px; padding:8px 10px; color:var(--text);
    font-family:var(--font-mono); font-size:12px; outline:none;
  }
  .lang-search:focus{ border-color:rgba(var(--accent-rgb),0.5); }
  .lang-panel-body{ display:flex; gap:16px; max-height:60vh; overflow-y:auto; }
  .lang-section{ display:flex; flex-direction:column; gap:4px; min-width:150px; }
  .lang-section-title{
    font-family:var(--font-mono); font-size:9px; letter-spacing:0.1em; text-transform:uppercase;
    color:var(--text-faint); padding:4px 8px 6px;
  }
  .lang-section-grid{ display:flex; flex-wrap:wrap; gap:3px; align-content:flex-start; }
  .lang-opt{
    border:none; background:none; cursor:pointer;
    color:var(--text-dim); font-family:var(--font-mono); font-size:12px;
    padding:7px 10px; border-radius:6px;
    transition:background .15s ease, color .15s ease;
    white-space:nowrap;
  }
  .lang-opt:hover{ background:var(--bg-soft); color:var(--text); }
  .lang-opt.active{ color:var(--accent); background:var(--accent-soft); }
  .lang-opt[hidden]{ display:none; }

  /* ---------- test area ---------- */
  .test-zone{
    position:relative;
    z-index:0;
    flex:1;
    display:flex;
    flex-direction:column;
    align-items:center;
    justify-content:flex-start;
    padding-top:28px;
    min-height:260px;
  }

  .live-stats{
    position:absolute;
    top:-2px; left:2px;
    display:flex; gap:22px;
    font-family:var(--font-mono);
    opacity:0;
    transition:opacity .25s ease;
    pointer-events:none;
  }
  .live-stats.show{ opacity:1; }
  .live-stats .stat{ display:flex; flex-direction:column; gap:2px; }
  .live-stats .stat-val{ font-size:22px; font-weight:600; color:var(--accent); }
  .live-stats .stat-key{ font-size:10px; letter-spacing:0.1em; text-transform:uppercase; color:var(--text-dim); }

  .momentum-bar{
    display:flex; align-items:center; justify-content:center;
    margin-bottom:16px;
  }
  .momentum-pips{ display:flex; gap:5px; justify-content:center; }
  .pip{
    width:22px; height:6px; border-radius:1px;
    background:var(--bg-soft);
    border:1px solid var(--border);
    transition:background .2s ease, border-color .2s ease, box-shadow .2s ease, transform .18s cubic-bezier(.16,1,.3,1);
  }
  .pip.filled{
    background:var(--caret-color);
    border-color:var(--caret-color);
  }
  .pip.lost{ animation:pip-lost .4s ease; }
  .pip.gained{ animation:pip-gained .5s ease; }
  @keyframes pip-lost{
    0%,100%{ transform:scaleY(1); }
    25%{ transform:scaleY(1.8); background:var(--error); border-color:var(--error); }
  }
  @keyframes pip-gained{
    0%{ transform:scale(0.4); opacity:0.3; }
    50%{ transform:scale(1.3); }
    100%{ transform:scale(1); }
  }

  /* Wraps the viewport purely for visual framing (padding/radius/spotlight).
     That framing used to live directly on .words-viewport, but its padding
     silently broke the caret: positionCaret() (app.js) computes the caret's
     x/y by walking offsetLeft/offsetTop from the typed letter up to
     #wordsContainer, then applies that as a transform on #caret — a
     calculation that only lines up if #wordsViewport's content box starts
     at (0,0) with no padding of its own. Padding here on the wrapper
     instead keeps that assumption true. */
  .type-frame{
    width:100%;
    max-width:1120px;
  }
  .words-viewport{
    position:relative;
    width:100%;
    height:calc(var(--line-height) * 3);
    overflow:hidden;
    cursor:text;
    /* Prevents mobile browsers from treating a tap-to-focus as a text
       selection, which would otherwise pop the iOS/Android Copy/Look Up
       callout menu over the typing area and interrupt the flow. */
    user-select:none;
    -webkit-user-select:none;
  }
  .words{
    position:relative;
    display:flex;
    flex-wrap:wrap;
    align-content:flex-start;
    font-family:var(--test-font-family);
    font-size:var(--test-font-size);
    line-height:var(--line-height);
    transition:transform .22s cubic-bezier(.16,1,.3,1), filter .18s ease;
    filter:blur(0px);
    transition-property:transform, filter;
  }
  .words.blurred{ filter:blur(4px); opacity:.55; }
  .words.rtl{ direction:rtl; }
  .words.rtl .word{ margin:0 0 0 14px; }
  .words.blind .letter.incorrect{ color:var(--text); }
  .words.blind .letter.extra{ color:var(--text); opacity:1; }

  .word{ display:inline-flex; margin:0 14px 0 0; position:relative; }
  .letter{ color:var(--text-dim); transition:color .05s ease; position:relative; }
  .letter.correct{ color:var(--text); }
  .letter.incorrect{ color:var(--error); }
  .letter.extra{ color:var(--error); opacity:0.65; }

  .caret{
    position:absolute; top:0; left:0;
    width:2px;
    background:var(--caret-color);
    border-radius:2px;
    box-shadow:0 0 6px rgba(var(--caret-rgb),0.45);
    animation:caret-blink 1s steps(1) infinite;
    /* Slides between letters rather than jumping — a duration this short
       resolves to essentially a snap at typing speed even though it's
       technically animated; long enough to actually read as a slide, and
       still comfortably faster than the gap between two keystrokes at a
       fast typing pace so it never trails behind. */
    transition:transform .08s linear, height .08s linear, background .2s ease;
    will-change:transform;
  }
  .caret.typing{ animation:none; opacity:1; }
  @keyframes caret-blink{ 0%,49%{opacity:1;} 50%,100%{opacity:0;} }

  .hidden-input{
    position:absolute;
    opacity:0;
    width:10px; height:10px;
    top:0; left:0;
    border:none; outline:none; background:none; color:transparent;
    caret-color:transparent;
    /* iOS Safari auto-zooms the page when a focused input's computed
       font-size is under 16px — with this element positioned off in the
       corner and effectively invisible, that zoom is jarring and has
       nothing to focus on. Fixing the size here (rather than relying on
       inherited body/mono sizing, which varies by screen width) avoids
       it regardless of viewport. */
    font-size:16px;
  }

  .focus-overlay{
    position:absolute; inset:0;
    display:flex; align-items:center; justify-content:center;
    background:rgba(13,15,20,0.7);
    backdrop-filter:blur(2px);
    font-family:var(--font-mono);
    font-size:14px;
    color:var(--text-dim);
    opacity:0; pointer-events:none;
    transition:opacity .15s ease;
    border-radius:var(--radius);
  }
  .focus-overlay.show{ opacity:1; pointer-events:auto; cursor:pointer; }

  .action-row{
    display:flex; align-items:center; justify-content:center;
    margin-top:44px;
  }
  .restart-ring-wrap{ position:relative; width:52px; height:52px; }
  .restart-ring{
    position:absolute; inset:0;
    transform:rotate(-90deg);
    pointer-events:none;
  }
  .restart-ring-track{
    fill:none; stroke:var(--border); stroke-width:2;
  }
  .restart-ring-fill{
    fill:none; stroke:var(--accent); stroke-width:2;
    stroke-linecap:round;
    stroke-dasharray:144.5;
    stroke-dashoffset:144.5;
    transition:stroke-dashoffset .15s linear, stroke .2s ease;
    opacity:0;
  }
  .restart-ring-fill.show{ opacity:0.85; }
  .restart-btn{
    position:absolute; top:5px; left:5px;
    width:42px; height:42px;
    border-radius:999px;
    border:1px solid rgba(var(--accent-rgb),0.28);
    background:var(--panel);
    box-shadow:var(--inset-hi), var(--shadow-sm);
    color:rgba(var(--accent-rgb),0.65);
    display:flex; align-items:center; justify-content:center;
    cursor:pointer;
    transition:color .18s ease, border-color .18s ease, transform .25s var(--ease-out), box-shadow .25s ease;
  }
  .restart-btn:hover{ color:var(--accent); border-color:rgba(var(--accent-rgb),0.55); transform:rotate(-35deg); box-shadow:var(--inset-hi), var(--glow-accent); }
  .restart-btn svg{ width:18px; height:18px; }

  .hint-row{
    text-align:center;
    font-family:var(--font-mono);
    font-size:11px;
    color:var(--text-faint);
    margin-top:14px;
    letter-spacing:0.02em;
  }
  .hint-row kbd{
    background:var(--panel);
    border:1px solid var(--border);
    border-radius:4px;
    padding:1px 6px;
    font-family:var(--font-mono);
    color:var(--text-dim);
    font-size:10px;
  }

  /* ---------- custom modal ---------- */
  .modal-backdrop{
    position:fixed; inset:0; background:rgba(5,6,9,0.72);
    display:flex; align-items:center; justify-content:center;
    z-index:50; opacity:0; pointer-events:none; transition:opacity .15s ease;
  }
  .modal-backdrop.show{ opacity:1; pointer-events:auto; }
  .modal{
    width:min(560px, 90vw);
    background:var(--panel);
    border:1px solid var(--border);
    border-radius:16px;
    padding:26px;
    box-shadow:var(--inset-hi), var(--shadow-lg);
  }
  .modal h3{ margin:0 0 6px; font-size:16px; }
  .modal p{ margin:0 0 16px; font-size:13px; color:var(--text-dim); }
  .modal textarea{
    width:100%; min-height:140px;
    background:var(--bg-soft);
    border:1px solid var(--border);
    border-radius:8px;
    color:var(--text);
    font-family:var(--font-mono);
    font-size:14px;
    padding:14px;
    resize:vertical;
    outline:none;
  }
  .modal textarea:focus{ border-color:rgba(var(--accent-rgb),0.5); }
  .modal-actions{ display:flex; justify-content:flex-end; gap:10px; margin-top:16px; }
  .btn{
    font-family:var(--font-display);
    font-size:13px; font-weight:600;
    padding:10px 18px;
    border-radius:9px;
    border:1px solid var(--border);
    background:var(--panel);
    color:var(--text-dim);
    cursor:pointer;
    box-shadow:var(--inset-hi), var(--shadow-sm);
    transition:color .15s ease, background .15s ease, border-color .15s ease, transform .15s var(--ease-out), box-shadow .15s ease;
  }
  .btn:hover{ color:var(--text); border-color:var(--text-faint); transform:translateY(-1px); box-shadow:var(--inset-hi), var(--shadow-md); }
  .btn:active{ transform:translateY(0) scale(0.96); box-shadow:0 1px 1px rgba(0,0,0,0.3) inset; }
  .btn.primary{ background:linear-gradient(135deg, var(--accent-2), var(--accent)); color:#1a0e04; border-color:var(--accent); box-shadow:0 1px 0 rgba(255,255,255,0.2) inset, 0 3px 12px -5px rgba(var(--accent-rgb),0.35); }
  .btn.primary:hover{ background:linear-gradient(135deg, var(--accent-2), var(--accent)); border-color:var(--accent-2); box-shadow:0 1px 0 rgba(255,255,255,0.2) inset, 0 6px 16px -6px rgba(var(--accent-rgb),0.45); }

  /* ---------- results ---------- */
  .results{
    width:100%;
    max-width:760px;
    margin:0 auto;
    padding:36px 0 20px;
    text-align:center;
  }
  /* staggered reveal — each block rises in slightly after the last, so
     the payoff moment reads as a sequence (badge, then the number, then
     supporting detail) instead of the whole report snapping in at once */
  .results-badge, .fail-badge{ animation:badge-pop .4s var(--ease-out) both; }
  .results-primary{ animation:result-rise .4s var(--ease-out) both .05s; }
  .results-secondary{ animation:result-rise .4s var(--ease-out) both .1s; }
  .wpm-graph{ animation:result-rise .4s var(--ease-out) both .15s; }
  .problem-keys, #historyToggle, .results-meta, .results-footer{ animation:result-rise .4s var(--ease-out) both .2s; }
  @keyframes result-rise{ from{ opacity:0; transform:translateY(10px); } to{ opacity:1; transform:translateY(0); } }
  .results-badge{
    display:inline-flex; align-items:center; gap:6px;
    font-family:var(--font-mono); font-size:11px; letter-spacing:0.1em;
    color:var(--accent-2); text-transform:uppercase;
    background:rgba(var(--accent-2-rgb),0.1);
    border:1px solid rgba(var(--accent-2-rgb),0.3);
    padding:5px 12px; border-radius:999px;
    margin-bottom:18px;
    box-shadow:0 3px 12px -6px rgba(var(--accent-2-rgb),0.35);
  }
  @keyframes badge-pop{ from{ opacity:0; transform:scale(0.85) translateY(-4px); } to{ opacity:1; transform:scale(1) translateY(0); } }
  .results-primary{
    display:flex; justify-content:center; gap:64px; margin-bottom:30px;
  }
  .results-primary .stat-key{ font-size:12px; letter-spacing:0.14em; text-transform:uppercase; color:var(--text-dim); margin-bottom:6px; }
  .results-primary .stat-val{ font-family:var(--font-mono); font-size:72px; font-weight:700; line-height:1; color:var(--text); letter-spacing:-0.02em; }
  .results-primary .stat-val.accent{
    background:linear-gradient(135deg, var(--accent-2), var(--accent));
    -webkit-background-clip:text; background-clip:text; color:transparent;
    filter:drop-shadow(0 2px 14px rgba(var(--accent-rgb),0.16));
  }

  .results-secondary{
    display:grid;
    grid-template-columns:repeat(4, 1fr);
    gap:10px;
    padding:0;
    margin-bottom:26px;
  }
  .results-secondary .stat{
    display:flex; flex-direction:column; gap:6px;
    padding:16px 10px;
    background:var(--panel);
    border:1px solid var(--border-soft);
    border-radius:12px;
    box-shadow:var(--inset-hi);
  }
  .results-secondary .stat-key{ font-size:10px; letter-spacing:0.1em; text-transform:uppercase; color:var(--text-faint); }
  .results-secondary .stat-val{ font-family:var(--font-mono); font-size:19px; color:var(--text); }

  .results-footer{ display:flex; justify-content:center; align-items:center; gap:16px; flex-wrap:wrap; }
  .results-meta{ font-family:var(--font-mono); font-size:11px; color:var(--text-faint); margin-bottom:20px; }

  .wpm-graph{
    width:100%; max-width:640px; margin:0 auto 12px;
    background:var(--panel);
    border:1px solid var(--border-soft);
    border-radius:14px;
    padding:14px 10px 4px;
    box-shadow:var(--inset-hi);
  }
  .wpm-graph svg{ display:block; width:100%; height:160px; }
  .graph-legend{
    display:flex; justify-content:center; gap:16px;
    font-family:var(--font-mono); font-size:10px; color:var(--text-faint);
    margin-bottom:22px; letter-spacing:0.04em;
  }
  .legend-dot{ display:inline-block; width:7px; height:7px; border-radius:50%; margin-right:5px; vertical-align:middle; }

  .problem-keys{
    display:flex; align-items:center; justify-content:center; gap:10px; flex-wrap:wrap;
    font-family:var(--font-mono); font-size:12px; color:var(--text-dim);
    margin-bottom:14px;
  }
  .pk-label{ color:var(--text-faint); letter-spacing:0.05em; text-transform:uppercase; font-size:10px; }
  .weak-chip{
    display:inline-flex; align-items:center; justify-content:center;
    min-width:22px; height:22px; padding:0 6px;
    border-radius:6px;
    background:rgba(var(--error-rgb),0.12);
    border:1px solid rgba(var(--error-rgb),0.3);
    color:var(--error);
    font-weight:600; text-transform:lowercase;
  }

  .input-history{
    max-width:680px; margin:14px auto 0;
    max-height:150px; overflow-y:auto;
    text-align:left;
    background:var(--bg-soft);
    border:1px solid var(--border);
    border-radius:10px;
    padding:14px 16px;
    font-family:var(--font-mono);
    font-size:13px;
    line-height:1.9;
  }
  .hist-word{ margin-right:9px; display:inline-block; }
  .hist-letter.correct{ color:var(--text); }
  .hist-letter.incorrect{ color:var(--error); }
  .hist-letter.extra{ opacity:0.6; text-decoration:line-through; }
  .hist-letter.missed{ color:var(--text-faint); text-decoration:underline; text-decoration-style:dotted; }

  /* ---------- settings panel: a right-edge drawer ---------- */
  .settings-panel{
    position:absolute;
    top:72px; right:28px;
    width:320px;
    background:var(--panel);
    border:1px solid var(--border);
    border-radius:14px;
    padding:16px;
    z-index:40;
    opacity:0; transform:translateY(-6px);
    pointer-events:none;
    transition:opacity .2s var(--ease-out), transform .2s var(--ease-out);
    box-shadow:var(--inset-hi), var(--shadow-lg);
    max-height:80vh; overflow-y:auto;
  }
  .settings-panel.show{ opacity:1; transform:translateY(0); pointer-events:auto; }
  .settings-panel h4{ margin:14px 0 6px; font-size:11px; letter-spacing:0.1em; text-transform:uppercase; color:var(--text-dim); }
  .settings-panel h4:first-child{ margin-top:0; }
  .settings-note{ font-size:11px; color:var(--text-faint); line-height:1.5; margin-top:2px; }
  .link-btn{
    background:none; border:none; color:var(--text-dim); font-family:var(--font-mono);
    font-size:11px; cursor:pointer; text-decoration:underline; padding:0; margin-top:10px;
  }
  .link-btn:hover{ color:var(--error); }

  .settings-tabs{ display:flex; gap:3px; background:var(--bg-soft); border-radius:8px; padding:3px; margin-bottom:14px; }
  .settings-tab{
    flex:1; border:none; background:none; cursor:pointer;
    color:var(--text-dim); font-family:var(--font-mono); font-size:11px;
    padding:7px 0; border-radius:6px;
    transition:color .15s ease, background .15s ease, transform .15s cubic-bezier(.16,1,.3,1);
  }
  .settings-tab:active{ transform:scale(0.95); }
  .settings-tab.active{ background:var(--panel-2); color:var(--text); }
  .settings-section[hidden]{ display:none; }

  .settings-row{ display:flex; align-items:center; justify-content:space-between; gap:14px; padding:10px 0; border-bottom:1px solid var(--border); }
  .settings-row:last-child{ border-bottom:none; }
  .settings-row .settings-note{ margin-top:2px; }
  .settings-label{ font-size:12px; color:var(--text); }

  .toggle-switch{
    position:relative; width:36px; height:21px; border-radius:999px;
    background:var(--border); border:none; cursor:pointer; flex-shrink:0; padding:0;
    transition:background .15s ease;
  }
  .toggle-switch::after{
    content:""; position:absolute; top:2px; left:2px;
    width:17px; height:17px; border-radius:50%;
    background:var(--text-dim);
    transition:transform .2s cubic-bezier(.34,1.4,.4,1), background .15s ease;
  }
  .toggle-switch.on{ background:var(--accent-soft); }
  .toggle-switch.on::after{ transform:translateX(15px); background:var(--accent); }

  .seg-control{ display:flex; background:var(--bg-soft); border-radius:8px; padding:3px; gap:3px; margin:6px 0 8px; }
  .seg-control button{
    flex:1; border:none; background:none; cursor:pointer;
    color:var(--text-dim); font-family:var(--font-mono); font-size:12px;
    padding:8px 0; border-radius:6px;
    transition:color .15s ease, background .15s ease, transform .15s cubic-bezier(.16,1,.3,1);
  }
  .seg-control button:active{ transform:scale(0.95); }
  .seg-control button.active{ background:var(--accent-soft); color:var(--accent); }

  .theme-swatches{ display:grid; grid-template-columns:repeat(5,1fr); gap:10px; margin:8px 0 4px; }
  .theme-swatch{
    width:100%; aspect-ratio:1; border-radius:9px; cursor:pointer;
    border:2px solid var(--border); position:relative; overflow:hidden;
    box-shadow:var(--shadow-sm);
    transition:border-color .15s ease, transform .15s var(--ease-out), box-shadow .15s ease;
  }
  .theme-swatch:hover{ transform:translateY(-2px); box-shadow:var(--shadow-md); }
  .theme-swatch:active{ transform:scale(0.92); }
  .theme-swatch.active{ border-color:var(--text); box-shadow:0 0 0 3px rgba(var(--accent-rgb),0.22); }
  .theme-swatch-dot{
    position:absolute; bottom:5px; right:5px; width:11px; height:11px;
    border-radius:50%;
  }

  .font-family-grid{ display:grid; grid-template-columns:1fr 1fr; gap:4px; margin:6px 0 8px; }
  .font-family-grid button{
    border:none; background:var(--bg-soft); cursor:pointer;
    color:var(--text-dim); font-family:var(--font-mono); font-size:11px;
    padding:9px 6px; border-radius:6px;
    transition:color .15s ease, background .15s ease, transform .15s cubic-bezier(.16,1,.3,1);
    text-align:center;
  }
  .font-family-grid button:active{ transform:scale(0.95); }
  .font-family-grid button.active{ background:var(--accent-soft); color:var(--accent); }

  .fail-badge{
    display:inline-flex; align-items:center; gap:6px;
    font-family:var(--font-mono); font-size:11px; letter-spacing:0.1em;
    color:var(--error); text-transform:uppercase;
    background:rgba(var(--error-rgb),0.1);
    border:1px solid rgba(var(--error-rgb),0.3);
    padding:5px 12px; border-radius:999px;
    margin-bottom:18px;
  }

  /* results dashboard: on wide screens the primary numbers and the graph
     sit side by side instead of stacking the whole way down — reads as a
     compact report rather than a long scroll of centered blocks. Mobile
     keeps the simpler stacked flow untouched. */
  @media (min-width:860px){
    .results{
      max-width:920px;
      text-align:left;
      display:grid;
      grid-template-columns:1fr 1fr;
      column-gap:48px;
      row-gap:4px;
      align-items:start;
      grid-template-areas:
        "badge   badge"
        "primary graph"
        "secondary graph"
        "keys    keys"
        "toggle  toggle"
        "history history"
        "meta    meta"
        "footer  footer";
    }
    .results-badge, .fail-badge{ grid-area:badge; justify-self:start; }
    .results-primary{ grid-area:primary; justify-content:flex-start; align-self:center; }
    .results-secondary{ grid-area:secondary; grid-template-columns:repeat(2,1fr); }
    .wpm-graph{ grid-area:graph; margin:0; align-self:stretch; display:flex; flex-direction:column; justify-content:center; }
    .problem-keys{ grid-area:keys; justify-content:flex-start; margin-top:10px; }
    #historyToggle{ grid-area:toggle; }
    .input-history{ grid-area:history; margin-left:0; }
    .results-meta{ grid-area:meta; text-align:left; }
    .results-footer{ grid-area:footer; justify-content:flex-start; }
  }

  /* ---------- footer ---------- */
  footer{
    position:relative; z-index:1;
    display:flex; align-items:center; justify-content:center;
    gap:18px; flex-wrap:wrap;
    padding:22px 0 30px;
    font-family:var(--font-mono);
    font-size:11px;
    color:var(--text-faint);
  }
  footer .sep{ color:var(--text-faint); opacity:0.5; }

  [hidden]{ display:none !important; }

  /* Focus mode, Monkeytype-style: while actively typing, everything
     except the words and a minimal indicator disappears — no hovering
     to peek at the HUD, it's just gone until the test ends. The brand
     mark stays as the one fixed anchor in the corner, and the restart
     ring (a literal circular timer) stays since it's the one piece of
     live feedback worth keeping visible. Config row, mode pill, nav
     icons, live wpm/time numbers, the language selector, and the
     tab/esc hint all fully hide rather than dim — dimming still shows
     a shape sitting there, which isn't the same as it being gone.
     The fade starts the instant typing begins (no delay before the
     transition kicks off) but runs over .28s with a small downward
     drift — long enough to read as a deliberate fade-away rather than
     an instant cut or a too-fast flicker, short enough to still feel
     immediate. visibility flips to hidden only once the fade finishes,
     so these elements stay out of tab order during focus mode without
     cutting the animation short. */
  .config-row, .meta-label, footer, .hint-row,
  .site-nav, .mode-pill, .nav-right, .live-stats{
    transition:opacity .28s cubic-bezier(.4,0,.2,1), transform .28s cubic-bezier(.4,0,.2,1), visibility 0s linear 0s;
    will-change:opacity, transform;
  }
  body.in-session .config-row,
  body.in-session .meta-label,
  body.in-session footer,
  body.in-session .hint-row,
  body.in-session .site-nav,
  body.in-session .mode-pill,
  body.in-session .nav-right,
  body.in-session .live-stats{
    opacity:0;
    transform:translateY(-4px);
    visibility:hidden;
    pointer-events:none;
    transition:opacity .28s cubic-bezier(.4,0,.2,1), transform .28s cubic-bezier(.4,0,.2,1), visibility 0s linear .28s;
  }


  @media (max-width:720px){
    .shell{ padding:0 16px; }
    :root{ --line-height:40px; --test-font-size:20px; }
    .test-zone{ padding-top:16px; min-height:200px; }
    .results-primary{ gap:32px; }
    .results-primary .stat-val{ font-size:44px; }
    .results-secondary{ grid-template-columns:repeat(2,1fr); row-gap:16px; }
    .topbar{ display:flex; flex-direction:column; gap:12px; padding-top:20px; }
    .brand, .nav-right{ justify-self:auto; }
    .topbar-left{ width:100%; justify-content:space-between; }
    .site-nav{ margin-left:0; padding-left:0; border-left:none; }
    .nav-right{ width:100%; justify-content:center; flex-wrap:wrap; }
    .icon-btn{ width:40px; height:40px; }
    .settings-panel{ right:16px; left:16px; top:auto; width:auto; }
    .wpm-graph svg{ height:110px; }
    .lang-panel{ max-width:88vw; }
    .lang-panel-body{ flex-direction:column; gap:10px; max-height:50vh; }
    .input-history{ font-size:11px; line-height:1.8; max-height:120px; }
    .config-row{ gap:6px; }
    .config-group{ padding:3px; }
    .cfg-btn{ padding:6px 9px; font-size:12px; }
  }
  @media (max-width:480px){
    .mode-pill{ display:none; }
    .streak-chip-hide{ display:none; }
    .results-footer{ gap:10px; }
    .results-footer .btn{ padding:9px 14px; font-size:12px; }
    .brand-tag{ display:none; }
  }

/* ---------- modals (account, leaderboard) ---------- */
.modal-overlay{
  position:fixed; inset:0; z-index:60;
  background:rgba(0,0,0,0.55);
  backdrop-filter:blur(3px);
  display:flex; align-items:center; justify-content:center;
  padding:20px;
  opacity:0; pointer-events:none;
  transition:opacity .2s cubic-bezier(.16,1,.3,1);
}
.modal-overlay.show{ opacity:1; pointer-events:auto; }
.modal{
  position:relative;
  width:100%; max-width:380px;
  background:var(--panel);
  border:1px solid var(--border);
  border-radius:16px;
  padding:28px;
  box-shadow:var(--inset-hi), var(--shadow-lg);
  transform:translateY(10px) scale(.98);
  transition:transform .22s cubic-bezier(.16,1,.3,1);
  max-height:85vh; overflow-y:auto;
}
.modal-overlay.show .modal{ transform:translateY(0) scale(1); }
.modal-wide{ max-width:640px; }
.modal-close{
  position:absolute; top:16px; right:16px;
  width:26px; height:26px; border-radius:999px;
  border:none; background:var(--bg-soft); color:var(--text-dim);
  font-size:16px; line-height:1; cursor:pointer;
  display:flex; align-items:center; justify-content:center;
  z-index:2;
  transition:color .15s ease, background .15s ease;
}
.modal-close:hover{ color:var(--text); background:var(--panel2); }
.modal-title{ font-size:16px; font-weight:600; margin:0 34px 18px 0; color:var(--text); }

.modal-tabs{ display:flex; gap:3px; background:var(--bg-soft); border-radius:8px; padding:3px; margin:6px 34px 20px 0; }
.modal-tab{
  flex:1; border:none; background:none; cursor:pointer;
  color:var(--text-dim); font-family:var(--font-mono); font-size:12px;
  padding:8px 0; border-radius:6px;
  transition:color .15s ease, background .15s ease, transform .15s cubic-bezier(.16,1,.3,1);
}
.modal-tab:active{ transform:scale(0.95); }
.modal-tab.active{ background:var(--panel); color:var(--accent); }

.oauth-row{ display:flex; flex-direction:column; gap:8px; margin-bottom:16px; }
.oauth-btn{
  display:flex; align-items:center; justify-content:center; gap:10px;
  background:var(--bg-soft); border:1px solid var(--border);
  font-size:13px; padding:11px 0;
}
.oauth-btn:hover{ border-color:rgba(var(--accent-rgb),0.4); }
.oauth-divider{ display:flex; align-items:center; gap:10px; margin:0 0 16px; color:var(--text-faint); font-size:11px; }
.oauth-divider::before, .oauth-divider::after{ content:""; flex:1; height:1px; background:var(--border); }

.auth-form{ display:flex; flex-direction:column; gap:14px; }
.auth-form label{
  display:flex; flex-direction:column; gap:6px;
  font-size:11px; letter-spacing:0.06em; text-transform:uppercase; color:var(--text-dim);
}
.auth-form input{
  font-family:var(--font-mono); font-size:14px;
  background:var(--bg-soft); border:1px solid var(--border); border-radius:8px;
  padding:10px 12px; color:var(--text);
  transition:border-color .15s ease;
}
.auth-form input:focus{ outline:none; border-color:rgba(var(--accent-rgb),0.55); }
.auth-error{
  font-size:12px; color:var(--error);
  background:rgba(var(--error-rgb),0.1); border:1px solid rgba(var(--error-rgb),0.3);
  border-radius:8px; padding:8px 10px;
}

.account-summary{ display:flex; align-items:center; gap:14px; margin-bottom:22px; }
.account-avatar{
  width:46px; height:46px; border-radius:999px; flex-shrink:0;
  background:linear-gradient(135deg, var(--accent-2), var(--accent));
  box-shadow:0 3px 12px -5px rgba(var(--accent-rgb),0.35);
  display:flex; align-items:center; justify-content:center;
  font-family:var(--font-mono); font-weight:700; font-size:18px; color:rgba(10,10,15,0.85);
}
.account-username{ font-size:16px; font-weight:600; color:var(--text); }
.account-status{ font-size:12px; color:var(--text-dim); margin-top:2px; }

.leaderboard-filters{ display:flex; gap:10px; margin-bottom:18px; flex-wrap:wrap; }
.leaderboard-list{ display:flex; flex-direction:column; gap:2px; min-height:120px; }
.leaderboard-row{
  display:flex; align-items:center; gap:14px;
  padding:10px 12px; border-radius:8px;
  font-family:var(--font-mono); font-size:13px;
  transition:background .15s ease;
}
.leaderboard-row:nth-child(odd){ background:var(--bg-soft); }
.leaderboard-row:hover{ background:var(--panel-2); }
.leaderboard-rank{ width:28px; color:var(--text-dim); text-align:right; }
.leaderboard-row:nth-child(1) .leaderboard-rank{ color:var(--accent); font-weight:700; }
.leaderboard-row:nth-child(1){ box-shadow:inset 2px 0 0 var(--accent); }
.leaderboard-username{ flex:1; color:var(--text); }
.leaderboard-wpm{ color:var(--accent); font-weight:600; }
.leaderboard-acc{ color:var(--text-dim); width:56px; text-align:right; }
.leaderboard-empty{ text-align:center; color:var(--text-dim); padding:40px 0; font-size:13px; }

@media (max-width:640px){
  .modal{ padding:20px; }
  .leaderboard-filters{ flex-direction:column; }
}
