  :root{
    --bg:#030509; --ink:#e3edff; --dim:#8295bf; --faint:#3a4a72;
    --plasma:#5fc6ff; --hot:#cfe6ff; --line:rgba(95,198,255,.22);
    /* platform backdrop: portrait/mobile by default; wide image swapped in via media query */
    --plat:url(../assets/platform-mobile.jpg);
    --plat-desktop:url(../assets/platform-desktop.jpg);
    /* typography — body is swappable via Settings → Typography (js/app.js); display stays Orbitron */
    --font-display:"Orbitron";
    --font-body:"Space Grotesk";
    --font-mono:"JetBrains Mono";
  }
  *{ margin:0; padding:0; box-sizing:border-box; }
  html,body{ height:100%; background:#030509; overflow:hidden; }
  /* Body = readable swappable face; display (.fx, headings) + HUD (.mono) lead with Orbitron. */
  body{ color:#e3edff; font-family:var(--font-body),ui-sans-serif,system-ui,sans-serif;
    -webkit-font-smoothing:antialiased; }
  .mono{ font-family:var(--font-display),var(--font-mono),ui-monospace,monospace; font-weight:500; }
  .fx{ font-family:var(--font-display),var(--font-body),ui-sans-serif,system-ui,sans-serif; }

  #platform{ position:fixed; inset:0; z-index:0;
    background:#030509 center 64% / cover no-repeat;
    background-image:var(--plat);
    opacity:0; transition:opacity 1.4s ease; }
  #platform.in{ opacity:1; }
  /* wide screens use a dedicated landscape image, covered (no letterbox);
     positioned so the dais core sits on the same line the plasma is anchored to */
  @media (min-aspect-ratio:1/1){
    /* desktop: show the whole dais, anchored to the bottom-center */
    #platform{ background-image:var(--plat-desktop); background-size:contain; background-position:bottom center; }
    /* desktop: content sits in the left half so it never covers the central plasma;
       left margin pulled in past the logo for breathing room (right edge unchanged → panel narrows) */
    .panel-wrap{ right:auto; width:52vw; justify-content:flex-start;
      padding-left:9vw; padding-right:2vw; perspective-origin:0% 45%; }
  }
  #platform::after{ content:""; position:absolute; inset:0;
    background:linear-gradient(180deg, rgba(3,5,9,.55) 0%, rgba(3,5,9,.12) 30%, rgba(3,5,9,0) 55%); }
  /* ===== scene backdrop (per-section "room") =====
     A room image crossfaded OVER the dais when a section sets bg:'<name>' (→ assets/<name>.webp).
     Full-bleed cover (rooms fill the frame, unlike the contained dais); the live plasma beam (#reactor,
     z1) still draws on top, rising from the room's dormant dais. Logic: setBackdrop() in js/app.js. */
  #platformRoom{ position:fixed; inset:0; z-index:0; pointer-events:none;
    background:#030509 center / cover no-repeat;
    opacity:0; transition:opacity .7s ease; }
  #platformRoom.in{ opacity:1; }
  #platformRoom::after{ content:""; position:absolute; inset:0;
    background:linear-gradient(180deg, rgba(3,5,9,.55) 0%, rgba(3,5,9,.12) 30%, rgba(3,5,9,0) 55%); }
  /* Cyan energy bloom fired by roomFlash() at a room swap (the camera-pan "arrival pulse").
     Above the scene (reactor z1) but below all UI (frame z8); screen-blends so it reads as light. */
  #roomFlash{ position:fixed; inset:0; z-index:3; pointer-events:none; opacity:0; mix-blend-mode:screen;
    background:radial-gradient(ellipse 75% 62% at 50% 60%, rgba(95,198,255,.6), rgba(95,198,255,.14) 46%, transparent 72%); }
  /* ===== CAMERA CUBE-TURN (screen→screen "rotating into a new room" warp) =====
     A real 3D stage: the perspective container holds a preserve-3d rig with two opaque FACES (the
     current screen + the next), arranged as adjacent cube faces (edge = 100vw → translateZ ±50vw). A
     quarter-turn of the rig rotates one out and the other in with true perspective warp — then app.js
     hands off to the flat #platform/#platformRoom layers and hides the stage. The faces are STATIC
     backgrounds (the live beam is hidden during the turn), so no snapshot is needed. Off = display:none.
     INSIDE-the-cube view: the rig sits BEHIND the screen plane (translateZ +50vw) and the faces are
     pushed AWAY from it (translateZ -50vw), so the camera is INSIDE the box. The active face still nets
     to translateZ 0 (fills exactly), but the OTHER faces pass in FRONT of the screen during the turn —
     so the incoming wall LOOMS in big and subsides to actual size (like turning your head in a room),
     instead of growing from a far/small outside view. Flip both signs back to invert (outside view).
     cubeTurn()/setBackdrop() in app.js drive it; perspective below is the loom-strength knob (bigger =
     gentler loom). */
  #warpStage{ position:fixed; inset:0; z-index:5; pointer-events:none; display:none;
    perspective:108vw; perspective-origin:50% 46%; background:#030509; }
  #warpStage.on{ display:block; }
  #warpStage .warp-rig{ position:absolute; inset:0; transform-style:preserve-3d;
    transform:translateZ(50vw) rotateY(0deg); will-change:transform; }
  #warpStage .warp-face{ position:absolute; inset:0; backface-visibility:hidden;
    background:#030509 center / cover no-repeat; }
  /* front face faces the viewer at rest (net translateZ 0 → fills exactly); the side face's rotateY
     sign (left vs right cube face) is set inline by cubeTurn() per turn direction. */
  #warpStage .warp-front{ transform:rotateY(0deg) translateZ(-50vw); }
  /* dais face mirrors #platform's background (mobile var(--plat) / desktop contained) so the hand-off
     to the real dais is seamless; room faces get an inline url() from app.js. */
  #warpStage .warp-face.is-dais{ background-image:var(--plat); background-position:center 64%; }
  @media (min-aspect-ratio:1/1){
    #warpStage .warp-face.is-dais{ background-image:var(--plat-desktop); background-size:contain; background-position:bottom center; }
  }
  /* In a room, hide the dais-tuned ring glow + ambient glow blobs — they're placed for the empty
     dais, not the room art (which carries its own baked dais glow). */
  /* Hide the dais-tuned glow quickly on ENTER (.4s) so the old rings/blobs don't hover over the
     incoming room; the graceful 1.4s fade-back lives on the base #daisGlow rule for the RETURN. */
  body.in-room #daisGlow, body.in-room #lightCalib{ opacity:0 !important; transition:opacity .4s ease; }
  #reactor{ position:fixed; inset:0; z-index:1; mix-blend-mode:screen; transition:opacity .45s ease; }
  /* PROTOTYPE — war room as the live background (enabled by ?warbg via app.js, toggling body.warbg).
     The iframe sits ABOVE the 2D background layers (z0–2) but BELOW all UI chrome (z8+), and is
     non-interactive so clicks/hover go to the real UI. The home's 2D backdrop layers are hidden so only
     the 3D room shows; app.js also pauses the reactor render to free the GPU for the war room. */
  #warBg{ position:fixed; inset:0; width:100%; height:100%; border:0; z-index:6; pointer-events:none;
    background:#05060a; }
  body.warbg #platform, body.warbg #platformRoom, body.warbg #reactor,
  body.warbg #daisGlow, body.warbg #lightCalib, body.warbg #pillars, body.warbg #fog{
    opacity:0 !important; pointer-events:none !important; }   /* hidden + transparent to pointers so empty-area drags reach the war-room orbit */
  /* The beam draws ABOVE the room (z1 > z0), so a room swap can't cover it — setBackdrop() hides it
     fast during the camera-pan, then reveals it (ignites in over .45s) once the room has landed. */
  body.beam-hidden #reactor{ opacity:0; transition:opacity .22s ease; }
  /* animated glows traced over the platform's actual neon rings */
  #daisGlow{ position:fixed; inset:0; z-index:1; pointer-events:none; mix-blend-mode:screen;
    opacity:0; transition:opacity 1.4s ease; }
  #daisGlow.in{ opacity:1; }
  #daisGlow .ring{ position:absolute; left:50%; transform:translate(-50%,-50%);
    border-radius:50%; }
  /* top projector ring (small, on the dais face) */
  #daisGlow .r1{ top:68.5%; width:min(34vw,210px); height:min(8vw,52px);
    background:radial-gradient(ellipse 50% 50% at 50% 50%, rgba(150,225,255,.95), rgba(95,198,255,.4) 55%, transparent 74%);
    animation:ringPulse 3.6s ease-in-out infinite; }
  /* mid neon band */
  #daisGlow .r2{ top:70%; width:min(74vw,470px); height:min(13vw,90px);
    background:radial-gradient(ellipse 50% 50% at 50% 50%, rgba(95,210,255,.85), rgba(95,198,255,.3) 52%, transparent 72%);
    animation:ringPulse 4.4s ease-in-out infinite .6s; }
  /* base ring */
  #daisGlow .r3{ top:98%; width:min(92vw,600px); height:min(15vw,20px);
    background:radial-gradient(ellipse 50% 50% at 50% 50%, rgba(95,210,255,.8), rgba(95,198,255,.28) 52%, transparent 72%);
    animation:ringPulse 5.2s ease-in-out infinite 1.2s; }
  @keyframes ringPulse{
    0%,100%{ filter:brightness(.95); opacity:.85; }
    50%{ filter:brightness(1.7); opacity:1; } }
  /* Portrait uses the mobile platform image (var(--plat)), whose dais sits lower in frame than
     the desktop image — drop the rings to land on it (matches the lowered beam anchor, uDaisY,
     set in setDaisAnchor). Mirrors the @media platform-image swap (aspect-, not width-, based). */
  @media (max-aspect-ratio:1/1){
    #daisGlow .r1{ top:75%; }
    #daisGlow .r2{ top:78%; }
    #daisGlow .r3{ top:96%; }
  }

  /* ===== CTA HOLOGRAM PREVIEW (reusable hover popover above the footer CTAs) =====
     One shared WebGL canvas, positioned by JS ABOVE whichever door-card is hovered (ARCADE →
     rotating ship for now). screen-blend so the additive glow melts into the backdrop; sits
     above the doors but below the panels/rail (z12). Fades + grows from the card on hover.
     Hover-only → hidden on phones. left/top are set inline by js/app.js (place()). */
  #ctaHolo{ position:fixed; left:0; top:0; width:260px; height:220px; z-index:12;
    pointer-events:none; opacity:0; transform:translateY(8px) scale(.96); transform-origin:50% 100%;
    transition:opacity .35s ease, transform .35s ease;
    mix-blend-mode:screen; filter:drop-shadow(0 0 16px rgba(95,198,255,.28)); }
  #ctaHolo.in{ opacity:1; transform:none; }
  /* canvas is the only interactive child — drag to rotate; host stays pointer-none so the caption
     never blocks. touch-action:none keeps a drag from scrolling the page. */
  #ctaHolo canvas{ display:block; width:100% !important; height:100% !important;
    pointer-events:auto; cursor:grab; touch-action:none; }
  #ctaHolo.dragging canvas{ cursor:grabbing; }
  /* tiny readout caption under the projection (set per-preview) */
  #ctaHolo .holo-cap{ position:absolute; left:50%; bottom:4px; transform:translateX(-50%);
    font-size:9px; letter-spacing:.28em; color:rgba(127,208,255,.7); white-space:nowrap;
    text-shadow:0 0 8px rgba(95,198,255,.5); opacity:.85; pointer-events:none; }
  @media (prefers-reduced-motion:reduce){ #ctaHolo{ transition:opacity .2s ease; transform:none; } }
  @media (max-width:760px){ #ctaHolo{ display:none; } }

  /* ===== background-platform GLOW lights =====
     Soft round blue glow blobs over the platform. Positions live in LIGHT_POINTS
     (js/app.js) and stay draggable for re-tuning; each pulses on a randomized
     cycle (--lc-dur/--lc-delay set per-dot in JS) so they never glow in unison.
     Hidden during the intro — #lightCalib gets .in once the portal/platform
     loads (mirrors #daisGlow). The screen-blend lives on .lc-dot (not the
     container) so the .lc-label HUD stays readable when shown for calibration. */
  /* z-index:2 keeps the lights just above the dais/platform but BEHIND all the
     UI panels (.frame z8 · #home z10 · .panel-wrap z14 · .menu z18 · toprow z22). */
  #lightCalib{ position:fixed; inset:0; z-index:2; pointer-events:none;
    opacity:0; transition:opacity 1.4s ease; }
  #lightCalib.in{ opacity:1; }
  #lightCalib .lc-pt{ position:absolute; transform:translate(-50%,-50%);
    pointer-events:auto; cursor:grab; touch-action:none; }
  #lightCalib .lc-pt.drag{ cursor:grabbing; }
  #lightCalib .lc-pt.drag .lc-dot{ filter:brightness(1.7) saturate(1.2); }
  /* soft, round, edge-less glow (no hard disc / square falloff).
     display:block is required — a <span> is inline, so width/height/margin:auto
     are ignored and the gradient collapses to nothing. */
  #lightCalib .lc-dot{ display:block; width:var(--lc-size,20px); height:var(--lc-size,20px);
    border-radius:50%; margin:0 auto;
    background:radial-gradient(circle at 50% 50%,
      rgba(207,230,255,.98) 0%, rgba(95,198,255,.7) 34%, rgba(95,198,255,0) 72%);
    mix-blend-mode:screen;
    animation:lcGlow var(--lc-dur,6s) ease-in-out var(--lc-delay,0s) infinite; }
  /* calm breathing pulse (tamer than before → less overall glow) */
  @keyframes lcGlow{
    0%,100%{ opacity:.28; transform:scale(.8);  }
    50%    { opacity:.85; transform:scale(1.12); } }
  /* alt style: irregular flicker — toggled via GLOW_STYLE in app.js (adds .lc-flicker) */
  #lightCalib.lc-flicker .lc-dot{ animation-name:lcFlicker; animation-timing-function:linear; }
  @keyframes lcFlicker{
    0%,100%{ opacity:.8; }  8%{ opacity:.25; }  12%{ opacity:.78; }
    26%{ opacity:.35; }    30%{ opacity:.85; }  41%{ opacity:.4; }
    47%{ opacity:.9; }     55%{ opacity:.3; }   60%{ opacity:.82; }
    73%{ opacity:.42; }    78%{ opacity:.88; }  90%{ opacity:.32; } }
  /* calibration labels — hidden (remove display:none to show for re-tuning) */
  #lightCalib .lc-label{ display:none;
    position:absolute; left:50%; bottom:calc(100% + 6px);
    transform:translateX(-50%); white-space:nowrap;
    font-family:var(--font-mono),ui-monospace,monospace; font-size:10px; line-height:1.25;
    color:#fff; text-align:center; text-shadow:0 1px 2px #000,0 0 4px #000;
    background:rgba(0,0,0,.55); border:1px solid rgba(255,60,60,.6);
    border-radius:4px; padding:2px 5px; }
  #lightCalib .lc-label b{ color:#ff7b7b; font-weight:600; }

  /* ===== room scene dressing — light PILLARS + drifting FOG =====
     Decorative layers for room backdrops (the war room). Positions live in PILLARS / FOG_POINTS
     (js/app.js), draggable for live tuning (dumpPillars()/dumpFog() log paste-ready coords). Shown
     only while a room is active (body.in-room); both sit at z2 (above the room image, below all UI).
     SHOW_PILLAR_LABELS / SHOW_FOG_LABELS in app.js add body.cal-tune-pillars / body.cal-tune-fog →
     reveal THAT layer's labels + grab handles and lift it above the panel; flip off for the clean view. */
  #pillars, #fog{ position:fixed; inset:0; z-index:2; pointer-events:none;
    opacity:0; transition:opacity .4s ease; }
  /* Enter: hold the dressing back (delay) so it MATERIALIZES only AFTER the camera-pan room has landed
     (setBackdrop, ~PAN_IN_MS) instead of fading in while the room is still mid-pan and motion-blurred.
     Keep this delay ≈ the pan-in duration. Leaving uses the quick, undelayed base fade above so it
     clears out with the pan-out. */
  body.in-room #pillars, body.in-room #fog{ opacity:1; transition:opacity .55s ease .8s; }
  /* Keep BOTH layers out from behind the panel content with a SOFT feathered mask (a hard clip left a
     visible vertical "wall"; the feather fades in over ~120px so there's no seam). --dress-clip = the
     panel's right edge in px, kept current by clipDressing() in app.js. */
  #pillars, #fog{
    -webkit-mask:linear-gradient(to right, transparent 0,
      transparent var(--dress-clip,0), #000 calc(var(--dress-clip,0) + 120px));
    mask:linear-gradient(to right, transparent 0,
      transparent var(--dress-clip,0), #000 calc(var(--dress-clip,0) + 120px)); }
  /* tuning only matters inside a room — lift the layer being tuned above the panel so handles reach */
  body.cal-tune-pillars.in-room #pillars, body.cal-tune-fog.in-room #fog{ z-index:25; }

  /* a calibration point is a 0×0 anchor at {x,y}; its visual + handle hang off the centre */
  .cal-pt{ position:absolute; width:0; height:0; transform:translate(-50%,-50%); pointer-events:none; }
  .cal-grab{ display:none; position:absolute; left:0; top:0; transform:translate(-50%,-50%);
    width:16px; height:16px; border-radius:50%; pointer-events:auto; cursor:grab; touch-action:none;
    z-index:3; background:rgba(127,208,255,.45); border:1px solid #7fd0ff;
    box-shadow:0 0 8px rgba(95,198,255,.7); }
  /* handles/labels are live ONLY for the layer in tuning mode, inside a room (else invisible handles
     would eat clicks) */
  body.cal-tune-pillars.in-room #pillars .cal-grab, body.cal-tune-fog.in-room #fog .cal-grab{ display:block; }
  .cal-pt.drag .cal-grab{ cursor:grabbing; background:rgba(127,208,255,.9); }
  .cal-label{ display:none; position:absolute; left:0; bottom:14px; transform:translateX(-50%);
    white-space:nowrap; font-family:var(--font-mono),ui-monospace,monospace; font-size:10px;
    line-height:1.25; color:#cfe6ff; text-align:center; text-shadow:0 1px 2px #000,0 0 4px #000;
    background:rgba(0,0,0,.6); border:1px solid rgba(95,198,255,.6); border-radius:4px; padding:2px 6px; }
  body.cal-tune-pillars.in-room #pillars .cal-label, body.cal-tune-fog.in-room #fog .cal-label{ display:block; }
  .cal-label b{ color:#7fd0ff; font-weight:600; }

  /* light PILLAR — soft glowing core over a wide diffuse halo, gentle synced pulse. Tiltable via
     --pl-rot (0=vertical, 90=flat). pl-core/pl-halo centre on the anchor; non-interactive. */
  .pillar .pl-core, .pillar .pl-halo{ position:absolute; left:0; top:0;
    transform:translate(-50%,-50%) rotate(var(--pl-rot,0deg)); mix-blend-mode:screen;
    pointer-events:none; animation:plPulse var(--pl-dur,4s) ease-in-out var(--pl-delay,0s) infinite; }
  .pillar .pl-core{ width:var(--pl-w,11px); height:var(--pl-h,24vh); border-radius:var(--pl-w,11px);
    background:linear-gradient(180deg, rgba(95,198,255,0) 0%, rgba(207,230,255,.9) 14%,
      rgba(228,244,255,1) 50%, rgba(207,230,255,.9) 86%, rgba(95,198,255,0) 100%);
    /* softer: gaussian-blur the bar itself + a deep layered bloom (white-hot → cyan → wide falloff) */
    filter:blur(2px) drop-shadow(0 0 6px rgba(200,238,255,1)) drop-shadow(0 0 16px rgba(130,212,255,.98))
           drop-shadow(0 0 38px rgba(95,198,255,.85)) drop-shadow(0 0 80px rgba(95,198,255,.5)); }
  /* hot base node where the column meets the floor */
  .pillar .pl-core::after{ content:""; position:absolute; left:50%; bottom:4%;
    width:calc(var(--pl-w,11px)*4.5); height:calc(var(--pl-w,11px)*4.5);
    transform:translate(-50%,40%); border-radius:50%; mix-blend-mode:screen;
    background:radial-gradient(circle at 50% 50%, rgba(228,244,255,.92),
      rgba(95,198,255,.5) 38%, transparent 72%); filter:blur(5px); }
  /* wide, heavily-blurred halo → diffuse glow that spreads outward, not a hard vertical line */
  .pillar .pl-halo{ width:calc(var(--pl-w,11px)*11); height:calc(var(--pl-h,24vh)*1.05);
    border-radius:50%;
    background:radial-gradient(ellipse 50% 50% at 50% 50%,
      rgba(135,216,255,.62), rgba(95,198,255,.26) 46%, transparent 74%);
    filter:blur(26px); }
  .cal-pt.pillar.drag .pl-core{ filter:blur(2px) drop-shadow(0 0 9px rgba(200,238,255,1))
    drop-shadow(0 0 30px rgba(95,198,255,1)) brightness(1.25); }
  @keyframes plPulse{ 0%,100%{ opacity:.62; } 50%{ opacity:1; } }

  /* FOG / smoke — soft drifting low cloud, centred on the anchor. Brighter + denser so it reads;
     drift now billows (horizontal + a little vertical lift + scale) for a smokier feel. */
  .fog-pt .fog-cloud{ position:absolute; left:0; top:0;
    width:var(--fg-size,360px); height:calc(var(--fg-size,360px) * .6);
    background:radial-gradient(ellipse 50% 50% at 50% 50%,
      rgba(190,222,255,.46), rgba(125,185,238,.24) 40%, rgba(95,160,220,.08) 64%, transparent 78%);
    filter:blur(22px); mix-blend-mode:screen; pointer-events:none;
    animation:fogDrift var(--fg-dur,18s) ease-in-out var(--fg-delay,0s) infinite; }
  @keyframes fogDrift{
    0%,100%{ transform:translate(-57%,-46%) scale(1);    }
    50%    { transform:translate(-43%,-56%) scale(1.2);  } }

  /* ===== LOADING overlay ===== */
  #loader{ position:fixed; inset:0; z-index:30; background:#030509;
    display:flex; flex-direction:column; align-items:flex-start; justify-content:flex-end;
    gap:16px; padding:0 0 7vh 6vw; transition:opacity .7s ease; }
  #loader.gone{ opacity:0; pointer-events:none; }
  /* boot log — lines accumulate bottom-left, newest just above the bar */
  #bootlog{ display:flex; flex-direction:column; align-items:flex-start; gap:7px;
    font-size:11px; letter-spacing:.04em; color:#5fc6ff; }
  .bootline{ position:relative; opacity:.85; white-space:nowrap; }
  .bootline::before{ content:"› "; color:rgba(95,198,255,.45); }
  #bar{ width:min(260px,52vw); height:1px; background:rgba(95,198,255,.18); position:relative; overflow:hidden; }
  #bar i{ position:absolute; left:0; top:0; height:100%; width:0%;
    background:linear-gradient(90deg,transparent,#5fc6ff); transition:width .3s ease; }
  /* click-to-load-faster feedback: a quick glow pulse on the bar each time you click */
  #bar.boost{ animation:barBoost .42s ease-out; }
  #bar.boost i{ animation:barBoostFill .42s ease-out; }
  @keyframes barBoost{ 0%{ box-shadow:0 0 18px rgba(95,198,255,.6); } 100%{ box-shadow:0 0 0 rgba(95,198,255,0); } }
  @keyframes barBoostFill{ 0%{ filter:brightness(2.4); } 100%{ filter:brightness(1); } }

  /* ===== HUD FRAME (draws in after zoom-out) ===== */
  .frame{ position:fixed; inset:16px; z-index:8; pointer-events:none;
    border:1px solid rgba(95,198,255,.0); border-radius:18px; transition:border-color 1.2s ease; }
  .frame.in{ border-color:rgba(95,198,255,.12); }
  .frame .c{ position:absolute; width:30px; height:30px; border:2px solid #5fc6ff; opacity:0;
    transition:opacity 1s ease .3s; }
  .frame.in .c{ opacity:.55; }
  .frame .tl{ top:-2px; left:-2px; border-right:0; border-bottom:0; border-radius:18px 0 0 0; }
  .frame .tr{ top:-2px; right:-2px; border-left:0; border-bottom:0; border-radius:0 18px 0 0; }
  .frame .bl{ bottom:-2px; left:-2px; border-right:0; border-top:0; border-radius:0 0 0 18px; }
  .frame .br{ bottom:-2px; right:-2px; border-left:0; border-top:0; border-radius:0 0 18px 0; }

  /* ===== HOME UI ===== */
  #home{ position:fixed; inset:0; z-index:10; display:flex; flex-direction:column;
    pointer-events:none; opacity:0; transition:opacity 1s ease; }
  #home.in{ opacity:1; }
  .toprow{ position:fixed; top:0; left:0; right:0; display:flex; justify-content:space-between;
    align-items:center; padding:34px 6vw 0; z-index:22; pointer-events:none;
    opacity:0; transition:opacity 1s ease; }
  .toprow.in{ opacity:1; }
  .toprow .sig, .toprow .menubtn, .toprow .musicbtn{ pointer-events:auto; }
  .sig{ pointer-events:auto; }
  .sig .name{ font-size:14px; letter-spacing:0.75em; font-weight:700; color:#eaf3ff;
    text-shadow:0 0 22px rgba(95,198,255,.4); }
  /* first-load logo reveal: the wordmark resolves out of a blurred, zoomed-out haze
     while its letters draw together — plays once, when .toprow first gains .in */
  .toprow.in .sig .name{ transform-origin:left center; animation:logoIn 1.25s cubic-bezier(.16,.84,.3,1) both; }
  @keyframes logoIn{
    0%   { opacity:0; filter:blur(16px); transform:scale(1.45); letter-spacing:1.6em; }
    55%  { opacity:1; filter:blur(3px); }
    100% { opacity:1; filter:blur(0);   transform:scale(1);    letter-spacing:0.75em; } }
  @media (prefers-reduced-motion:reduce){ .toprow.in .sig .name{ animation:none; } }
  .menubtn{ pointer-events:auto; display:flex; align-items:center; justify-content:center; cursor:pointer;
    width:42px; height:42px; color:#cfe6ff; transition:color .3s, transform .3s; }
  .menubtn:hover{ color:#5fc6ff; }
  .menubtn:not(.open):hover{ transform:rotate(90deg); }   /* playful spin only while it's a hamburger */
  .menubtn svg{ width:24px; height:24px; display:block; overflow:visible; }
  /* hamburger ↔ X morph: each bar rotates/fades about the SVG centre (12,12) */
  .menubtn svg line{ transform-box:view-box; transform-origin:center;
    transition:transform .4s cubic-bezier(.2,.8,.2,1), opacity .25s ease; }
  .menubtn.open .mb-l1{ transform:rotate(45deg) translateY(5px); }
  .menubtn.open .mb-l2{ opacity:0; transform:scaleX(0); }
  .menubtn.open .mb-l3{ transform:rotate(-45deg) translateY(-5px); }
  /* right-side control cluster: background-music toggle + menu */
  .topctl{ display:flex; align-items:center; gap:10px; }
  .musicbtn{ -webkit-appearance:none; appearance:none; background:none; border:0; padding:0; margin:0;
    display:flex; align-items:center; justify-content:center; cursor:pointer;
    width:40px; height:40px; color:#9fc0e6; transition:color .3s, filter .3s, transform .2s; }
  .musicbtn svg{ width:20px; height:20px; display:block; }
  .musicbtn:hover{ color:#5fc6ff; transform:scale(1.08); }
  .musicbtn:active{ transform:scale(.94); }
  .musicbtn .ico-pause{ display:none; }              /* paused → show ▶ */
  .musicbtn.playing .ico-play{ display:none; }        /* playing → show ❚❚ + lit glow */
  .musicbtn.playing .ico-pause{ display:block; }
  .musicbtn.playing{ color:#5fc6ff; filter:drop-shadow(0 0 6px rgba(95,198,255,.55)); }

  /* ===== SETTINGS (sliders button + glass dropdown) ===== */
  .settings-wrap{ position:relative; pointer-events:auto; display:flex; align-items:center; }
  .settingsbtn{ -webkit-appearance:none; appearance:none; background:none; border:0; padding:0; margin:0;
    display:flex; align-items:center; justify-content:center; cursor:pointer;
    width:40px; height:40px; color:#9fc0e6; transition:color .3s, filter .3s, transform .2s; }
  .settingsbtn svg{ width:22px; height:22px; display:block; }
  .settingsbtn:hover{ color:#5fc6ff; transform:scale(1.08); }
  .settingsbtn:active{ transform:scale(.94); }
  .settingsbtn.open{ color:#5fc6ff; filter:drop-shadow(0 0 6px rgba(95,198,255,.55)); }

  .settings-pop{ position:absolute; top:calc(100% + 14px); right:0; z-index:40;
    width:min(86vw,288px); padding:6px 16px 14px;
    background:linear-gradient(160deg, rgba(18,34,66,.66), rgba(6,12,26,.74));
    backdrop-filter:blur(18px) saturate(1.2); -webkit-backdrop-filter:blur(18px) saturate(1.2);
    border-radius:12px;
    box-shadow:0 0 0 1px rgba(120,200,255,.3), 0 26px 60px -24px rgba(0,0,0,.9),
      0 0 30px rgba(95,198,255,.1) inset;
    opacity:0; transform:translateY(-8px) scale(.98); transform-origin:top right;
    pointer-events:none; transition:opacity .22s ease, transform .22s cubic-bezier(.2,.8,.2,1); }
  .settings-pop.open{ opacity:1; transform:translateY(0) scale(1); pointer-events:auto; }
  .set-head{ font-size:10px; letter-spacing:.3em; text-transform:uppercase; color:#5fc6ff;
    padding:12px 0 10px; border-bottom:1px solid rgba(95,198,255,.16); margin-bottom:4px; }

  .set-row{ display:flex; align-items:center; justify-content:space-between; gap:14px; padding:13px 0; }
  .set-row--col{ flex-direction:column; align-items:stretch; gap:9px; }
  .set-label{ font-size:12.5px; color:#cfe0ff; letter-spacing:.03em; font-weight:500; }

  /* custom select — closed control. Native <select> stays the source of truth; when
     enhanced (.cs-on) JS hides it and shows a themed trigger + dropdown (below). The
     native <select> rules also style it as a graceful fallback before JS runs. */
  .set-select{ position:relative; }
  .set-select select,
  .set-select .cs-trigger{ -webkit-appearance:none; appearance:none; width:100%; cursor:pointer; text-align:left;
    background:rgba(8,16,38,.6); color:#dfe9ff; border:1px solid rgba(95,198,255,.3); border-radius:8px;
    padding:10px 34px 10px 12px; font:inherit; font-size:13px; letter-spacing:.02em; outline:none;
    transition:border-color .25s, box-shadow .25s; }
  .set-select select:hover,
  .set-select .cs-trigger:hover{ border-color:rgba(150,215,255,.6); }
  .set-select select:focus,
  .set-select.open .cs-trigger,
  .set-select .cs-trigger:focus-visible{ border-color:rgba(150,215,255,.8); box-shadow:0 0 0 3px rgba(95,198,255,.14); }
  .set-select select option{ background:#0a1430; color:#dfe9ff; }
  /* once enhanced, the native select is kept (value source) but visually removed */
  .set-select.cs-on select{ position:absolute; width:1px; height:1px; padding:0; opacity:0; pointer-events:none; }
  .set-select::after{ content:""; position:absolute; right:14px; top:calc(50% - 4px); width:7px; height:7px;
    border-right:1.6px solid #6fd0ff; border-bottom:1.6px solid #6fd0ff; transform:rotate(45deg);
    pointer-events:none; transition:transform .22s cubic-bezier(.2,.8,.2,1); }
  .set-select.open::after{ transform:rotate(-135deg); top:calc(50% - 2px); }

  /* custom dropdown — themed option list (replaces the native <option> popup) */
  .cs-list{ position:absolute; left:0; right:0; top:calc(100% + 6px); z-index:50; margin:0; padding:5px;
    list-style:none; max-height:248px; overflow-y:auto;
    background:linear-gradient(165deg, rgba(20,38,72,.97), rgba(8,15,32,.98));
    backdrop-filter:blur(20px) saturate(1.2); -webkit-backdrop-filter:blur(20px) saturate(1.2);
    border-radius:10px;
    box-shadow:0 0 0 1px rgba(120,200,255,.32), 0 24px 50px -20px rgba(0,0,0,.9),
      0 0 24px rgba(95,198,255,.08) inset;
    opacity:0; transform:translateY(-6px) scale(.98); transform-origin:top center; pointer-events:none;
    transition:opacity .18s ease, transform .18s cubic-bezier(.2,.8,.2,1); }
  .set-select.open .cs-list{ opacity:1; transform:translateY(0) scale(1); pointer-events:auto; }
  .cs-opt{ display:flex; align-items:center; gap:8px; padding:9px 10px 9px 12px; border-radius:6px;
    font-size:13px; color:#cfe0ff; letter-spacing:.02em; cursor:pointer;
    transition:background .15s ease, color .15s ease; }
  .cs-opt.active{ background:rgba(95,198,255,.16); color:#eaf6ff; }
  .cs-opt[aria-selected="true"]{ color:#bfe6ff; }
  .cs-opt[aria-selected="true"]::after{ content:"✓"; margin-left:auto; font-size:12px; color:#6fd0ff; }

  /* toggle switch */
  .set-toggle{ -webkit-appearance:none; appearance:none; border:0; cursor:pointer; padding:0; flex-shrink:0;
    position:relative; width:46px; height:26px; border-radius:999px;
    background:rgba(8,16,38,.7); box-shadow:0 0 0 1px rgba(95,198,255,.25) inset;
    transition:background .25s, box-shadow .25s; }
  .set-toggle .knob{ position:absolute; top:3px; left:3px; width:20px; height:20px; border-radius:50%;
    background:linear-gradient(160deg,#9fc0e6,#6b8bb8); box-shadow:0 1px 3px rgba(0,0,0,.5);
    transition:transform .25s cubic-bezier(.2,.8,.2,1), background .25s, box-shadow .25s; }
  .set-toggle[aria-checked="true"]{
    background:linear-gradient(90deg, rgba(95,198,255,.32), rgba(95,198,255,.5));
    box-shadow:0 0 0 1px rgba(150,215,255,.7) inset, 0 0 14px rgba(95,198,255,.4); }
  .set-toggle[aria-checked="true"] .knob{ transform:translateX(20px);
    background:linear-gradient(160deg,#eaf6ff,#7fd0ff); box-shadow:0 0 10px rgba(95,198,255,.7); }
  .set-toggle:focus-visible{ outline:2px solid rgba(120,200,255,.6); outline-offset:2px; }

  /* slider control (Settings → Music Volume) — label + live % above a themed range */
  .set-slabel{ display:flex; align-items:baseline; justify-content:space-between; gap:10px; }
  .set-val{ font-size:11px; color:#7fb4e6; letter-spacing:.06em; }
  .set-slider{ -webkit-appearance:none; appearance:none; width:100%; height:18px; cursor:pointer;
    background:transparent; outline:none; }
  /* WebKit track + thumb (separate rule — an unknown pseudo drops the whole selector) */
  .set-slider::-webkit-slider-runnable-track{ height:4px; border-radius:3px;
    background:linear-gradient(90deg, #5fc6ff var(--fill,70%), rgba(95,198,255,.16) var(--fill,70%)); }
  .set-slider::-webkit-slider-thumb{ -webkit-appearance:none; appearance:none; margin-top:-6px;
    width:16px; height:16px; border-radius:50%; background:linear-gradient(160deg,#eaf6ff,#7fd0ff);
    border:1px solid rgba(150,215,255,.9); box-shadow:0 0 10px rgba(95,198,255,.7);
    transition:transform .12s ease; }
  .set-slider:hover::-webkit-slider-thumb{ transform:scale(1.14); }
  /* Firefox track + progress + thumb */
  .set-slider::-moz-range-track{ height:4px; border-radius:3px; background:rgba(95,198,255,.16); }
  .set-slider::-moz-range-progress{ height:4px; border-radius:3px; background:#5fc6ff; }
  .set-slider::-moz-range-thumb{ width:16px; height:16px; border:1px solid rgba(150,215,255,.9);
    border-radius:50%; background:linear-gradient(160deg,#eaf6ff,#7fd0ff); box-shadow:0 0 10px rgba(95,198,255,.7); }
  .set-slider:focus-visible{ outline:2px solid rgba(120,200,255,.6); outline-offset:4px; }

  /* Home card carousel — the .doors box IS the carousel (width-constrained, centered).
     Inside: a clipped viewport (.doors-vp) holding the rotating .door strip (.doors-track),
     two blurred edge overlays (.doors-vp::before/::after), and plain line arrows overlaid
     INSIDE those edges. app.js rotates the strip for the infinite loop; .doors keeps the
     hide toggle + JS refs. */
  .doors{ margin-top:auto; margin-bottom:2vh; align-self:center; position:relative;
    width:min(980px,86vw); pointer-events:none;
    transition:opacity .5s ease, transform .5s ease, visibility 0s; }
  /* visibility:hidden (not just pointer-events:none) so the inner cards/arrows — which set
     their own pointer-events:auto and would otherwise override the wrapper — can't be hovered
     or clicked while hidden. Delayed 0.5s so the fade-out still plays; show is instant. */
  .doors.hidden{ opacity:0; transform:translateY(40px); visibility:hidden; pointer-events:none;
    transition:opacity .5s ease, transform .5s ease, visibility 0s .5s; }
  /* vertical padding gives the cards' hover-lift + glow room inside the clip; position:relative
     anchors the edge-blur pseudos. */
  .doors-vp{ pointer-events:auto; position:relative; overflow:hidden; width:100%; padding:18px 0;
    touch-action:pan-y;            /* keep vertical page scroll, deliver horizontal swipes to the carousel JS */
    user-select:none; -webkit-user-select:none;   /* a mouse-drag scrubs the row, doesn't select cards/text */
    cursor:grab; }                 /* affordance for native-cursor users (hidden when the custom cursor is on) */
  .doors-vp:active{ cursor:grabbing; }
  .doors-track{ --dgap:16px; display:flex; gap:var(--dgap); will-change:transform; }
  /* soft blurred fade on each side — the edge cards dissolve into blur under the arrows.
     mask makes the blur strongest at the very edge and fade to nothing toward the centre
     (no hard seam); the dark gradient tints it so cards melt into the backdrop. */
  .doors-vp::before, .doors-vp::after{ content:""; position:absolute; top:0; bottom:0; width:11%;
    z-index:2; pointer-events:none; backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px); }
  .doors-vp::before{ left:0; background:linear-gradient(90deg, rgba(3,5,9,.6), rgba(3,5,9,0));
    -webkit-mask:linear-gradient(90deg,#000 35%,transparent); mask:linear-gradient(90deg,#000 35%,transparent); }
  .doors-vp::after{ right:0; background:linear-gradient(270deg, rgba(3,5,9,.6), rgba(3,5,9,0));
    -webkit-mask:linear-gradient(270deg,#000 35%,transparent); mask:linear-gradient(270deg,#000 35%,transparent); }
  /* plain line chevrons overlaid inside the blurred edges (no bg/border/circle) */
  .dnav{ pointer-events:auto; cursor:pointer; position:absolute; top:0; height:100%; z-index:3;
    width:46px; display:flex; align-items:center;
    background:none; border:0; color:#bfe2ff; opacity:.8;
    transition:color .25s ease, opacity .25s ease; }
  .dnav.prev{ left:0; justify-content:flex-start; padding-left:8px; }
  .dnav.next{ right:0; justify-content:flex-end; padding-right:8px; }
  .dnav svg{ width:24px; height:24px; display:block;
    filter:drop-shadow(0 0 4px rgba(95,198,255,.5)); }
  .dnav:hover{ color:#fff; opacity:1; }
  .dnav:active svg{ transform:scale(.9); }
  .dnav[hidden]{ display:none; }
  /* each card occupies exactly 1/3 of the viewport (2 gaps between 3 cards) → extras overflow,
     revealed by sliding. Fixed flex-basis (not 1 1 0) so off-screen cards keep their width. */
  .door{ pointer-events:auto; cursor:pointer; flex:0 0 calc((100% - 2*var(--dgap))/3);
    padding:14px 18px;
    position:relative; overflow:hidden; border-radius:14px; isolation:isolate;
    display:flex; align-items:center; justify-content:center; min-height:88px;
    background:linear-gradient(155deg, rgba(28,46,86,.55), rgba(8,14,30,.34));
    backdrop-filter:blur(5px);
    transition:transform .4s ease, box-shadow .4s ease; }
  /* shining animated gradient border */
  .door::after{ content:""; position:absolute; inset:0; border-radius:14px; padding:1px;
    background:conic-gradient(from var(--ang, 0deg),
      rgba(95,198,255,0) 0deg,
      rgba(140,215,255,.9) 70deg,
      #ffffff 110deg,
      rgba(140,215,255,.9) 150deg,
      rgba(95,198,255,.15) 220deg,
      rgba(95,198,255,0) 360deg);
    -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite:destination-out; mask-composite:exclude;
    pointer-events:none; animation:spinBorder 5s linear infinite;
    filter:drop-shadow(0 0 4px rgba(120,200,255,.6)); }
  #doorChat.door::after{ animation-delay:-2.5s; }   /* out of sync with EXPLORE */
  #doorPlay.door::after{ animation-delay:-1.2s; }
  @property --ang{ syntax:'<angle>'; initial-value:0deg; inherits:false; }
  @keyframes spinBorder{ to{ --ang:360deg; } }
  /* static fallback ring */
  .door::before{ content:""; position:absolute; inset:0; border-radius:14px;
    border:1px solid rgba(120,200,255,.4);
    box-shadow:0 0 12px rgba(95,198,255,.2) inset, 0 0 14px rgba(95,198,255,.18);
    pointer-events:none; z-index:0; }
  .door:hover{ transform:translateY(-4px); box-shadow:0 14px 44px -12px rgba(95,198,255,.55); }
  .door:hover::before{ border-color:rgba(150,215,255,.7); box-shadow:0 0 20px rgba(95,198,255,.4) inset, 0 0 26px rgba(95,198,255,.45); }
  .door .inner{ position:relative; z-index:2; display:flex; flex-direction:column; align-items:center; gap:14px; }
  .door .dico{ width:30px; height:30px; color:#bfe2ff; opacity:.85; transition:color .35s, transform .35s; }
  .door:hover .dico{ color:#fff; transform:translateY(-2px); }
  .door .dico svg{ width:100%; height:100%; display:block; }
  .door .t{ font-size:clamp(17px,3.4vw,18px); font-weight:700; letter-spacing:.2em; text-transform:uppercase;
    color:#eaf3ff; text-shadow:0 0 20px rgba(95,198,255,.4); }
  .door .arr{ position:absolute; right:18px; bottom:16px; color:#5fc6ff;
    opacity:.5; transition:transform .35s ease, opacity .35s; z-index:2; }
  .door .arr svg{ width:18px; height:18px; display:block; }
  .door:hover .arr{ transform:translateX(4px); opacity:1; }
  .door .sheen{ position:absolute; inset:0; background:linear-gradient(120deg,transparent 40%,
    rgba(95,198,255,.12) 50%,transparent 60%); transform:translateX(-100%); z-index:1; }
  .door:hover .sheen{ animation:sheen 1.1s ease; }
  @keyframes sheen{ to{ transform:translateX(100%); } }

  /* skip */
  #skip{ position:fixed; bottom:22px; right:24px; z-index:25; pointer-events:auto; cursor:pointer;
    font-size:10px; letter-spacing:.2em; color:#41507a; text-transform:uppercase;
    background:none; border:0; transition:color .3s; }
  #skip:hover{ color:#8295bf; }
  #skip.gone{ display:none; }

  @media (max-width:760px){
    /* tighter carousel: wider box (arrows are overlaid now, no external room needed) + smaller
       chrome so 3 compact cards still fit. Keep the fixed 1/3 flex-basis from the base rule
       (don't switch to flex:1 1 0, that breaks the off-screen cards the loop relies on). */
    .doors{ width:94vw; }
    .doors-vp{ padding:14px 0; }
    .doors-track{ --dgap:8px; }
    .dnav{ width:34px; }
    .dnav svg{ width:20px; height:20px; }
    .door{ padding:16px 8px; min-height:72px; border-radius:12px; }
    .door .inner{ gap:8px; }
    .door .dico{ width:24px; height:24px; }
    .door .t{ font-size:12px; letter-spacing:.08em; }
    .door .arr{ right:8px; bottom:7px; }
    .door .arr svg{ width:14px; height:14px; }
  }
  @media (prefers-reduced-motion:reduce){
    .win-bar .spread .live-dot{ animation:none; }
  }
  /* ===== MENU (floating glass cards, perspective opposite the content) ===== */
  .menu{ position:fixed; top:50%; right:4vw; transform:translateY(-50%); z-index:18;
    display:flex; flex-direction:column; width:min(60vw,220px);
    opacity:0; pointer-events:none; transition:opacity .5s ease;
    perspective:1400px; perspective-origin:0% 50%; }
  .menu.in{ opacity:1; pointer-events:auto; }
  /* wrappers: on normal viewports these are transparent — the track is just the item column.
     On short viewports (@media max-height below) the vp clips to ~4 items and scrolls. */
  .menu-vp{ display:block; min-height:0; }
  .menu-track{ display:flex; flex-direction:column; gap:clamp(12px,2vh,20px);
    perspective:1400px; perspective-origin:0% 50%; }
  .mscroll{ display:none; }   /* arrows: shown only in the short-viewport scroll mode */
  .mi{ position:relative; display:flex; align-items:center; gap:16px; padding:22px 22px;
    border-radius:4px; background:linear-gradient(150deg, rgba(30,50,92,.4), rgba(8,14,30,.22));
    backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
    border:0; cursor:pointer; color:#dfe9ff; text-align:left; overflow:hidden; isolation:isolate;
    transform-style:preserve-3d;
    transform:perspective(1400px) rotateY(-10deg) translateX(30px); opacity:0;
    transition:transform .45s cubic-bezier(.2,.8,.2,1), box-shadow .4s ease, background .4s ease; }
  .menu.in .mi{ animation:miIn .5s cubic-bezier(.2,.8,.2,1) forwards; }
  .menu.in .mi:nth-child(1){ animation-delay:.08s } .menu.in .mi:nth-child(2){ animation-delay:.16s }
  .menu.in .mi:nth-child(3){ animation-delay:.24s } .menu.in .mi:nth-child(4){ animation-delay:.32s }
  .menu.in .mi:nth-child(5){ animation-delay:.40s } .menu.in .mi:nth-child(6){ animation-delay:.48s }
  .menu.in .mi:nth-child(7){ animation-delay:.56s }
  @keyframes miIn{ to{ opacity:1; transform:perspective(1400px) rotateY(-10deg) translateX(0); } }
  .mi::before{ content:""; position:absolute; inset:0; border-radius:4px;
    border:1px solid rgba(120,200,255,.32);
    box-shadow:0 0 10px rgba(95,198,255,.14) inset; pointer-events:none; z-index:0;
    transition:border-color .4s, box-shadow .4s; }
  .mi::after{ content:""; position:absolute; inset:0; border-radius:4px; padding:1px;
    background:conic-gradient(from var(--ang,0deg), rgba(95,198,255,0) 0deg,
      rgba(140,215,255,.8) 80deg, #fff 115deg, rgba(140,215,255,.8) 150deg,
      rgba(95,198,255,.1) 230deg, rgba(95,198,255,0) 360deg);
    -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite:destination-out; mask-composite:exclude;
    pointer-events:none; opacity:0; transition:opacity .4s;
    animation:spinBorder 6s linear infinite; filter:drop-shadow(0 0 3px rgba(120,200,255,.5)); z-index:0; }
  .mi:nth-child(2)::after{ animation-delay:-1.5s } .mi:nth-child(3)::after{ animation-delay:-3s }
  .mi:nth-child(4)::after{ animation-delay:-4.5s }
  .mi:hover{ transform:perspective(1400px) rotateY(-10deg) translateX(-8px) scale(1.02); }
  .mi:hover::before{ border-color:rgba(150,215,255,.6); box-shadow:0 0 16px rgba(95,198,255,.25) inset; }
  /* active: gently elevated toward viewer + bright animated border (feels "opened", stays in line with the rail) */
  .mi.active{ transform:perspective(1400px) rotateY(0deg) translateX(-16px) translateZ(34px) scale(1.1);
    background:linear-gradient(150deg, rgba(50,90,150,.55), rgba(12,22,46,.35));
    box-shadow:0 14px 38px -16px rgba(95,198,255,.55); z-index:3; }
  .mi.active::before{ border-color:rgba(160,220,255,.85); box-shadow:0 0 28px rgba(95,198,255,.45) inset; }
  .mi.active::after{ opacity:1; }
  .mi.active .mi-t{ color:#fff; text-shadow:0 0 18px rgba(95,198,255,.7); }
  .mi.active .mi-ico{ color:#bfe6ff; }
  .mi > *{ position:relative; z-index:1; }
  .mi-ico{ width:22px; height:22px; color:#7fd0ff; flex-shrink:0; }
  .mi-ico svg{ width:100%; height:100%; display:block; }
  .mi-t{ font-size:16px; font-weight:600; letter-spacing:.06em; flex:1; }
  /* Settings backdrop: full-screen blur behind the Settings dropdown. z:21 sits just under the
     top bar (.toprow z:22), so the page (reactor/home/panel/menu) blurs while the top control
     cluster + the settings panel stay crisp. visibility:hidden at rest (no paint/clicks); on
     .blur it shows instantly and the blur fades in via opacity, fades out on close. */
  #scrim{ position:fixed; inset:0; z-index:21; background:rgba(3,5,9,.18);
    -webkit-backdrop-filter:blur(7px); backdrop-filter:blur(7px);
    opacity:0; visibility:hidden; pointer-events:none;
    transition:opacity .3s ease, visibility 0s .3s; }
  #scrim.blur{ opacity:1; visibility:visible; pointer-events:auto;
    transition:opacity .3s ease, visibility 0s; }

  /* ===== HOLOGRAPHIC PANEL (content, eases from left) ===== */
  .panel-wrap{ position:fixed; top:0; left:0; bottom:0; right:min(64vw,270px); z-index:14;
    display:flex; align-items:center; justify-content:center;
    padding:120px 6vw 90px; pointer-events:none; opacity:0; transition:opacity .4s ease;   /* side padding = logo's 6vw margin so content aligns with the logo */
    perspective:1700px; perspective-origin:85% 45%; }
  .panel-wrap.in{ opacity:1; pointer-events:auto; }
  .panel-back{ display:none; }

  /* main floating window */
  .panel{ width:min(940px,100%); max-height:100%; overflow:hidden; position:relative;
    display:flex; flex-direction:column;
    transform-style:preserve-3d;
    background:linear-gradient(160deg, rgba(18,34,66,.32), rgba(6,12,26,.22));
    backdrop-filter:blur(18px) saturate(1.2); -webkit-backdrop-filter:blur(18px) saturate(1.2);
    border-radius:10px; padding:0;
    box-shadow:0 0 0 1px rgba(120,200,255,.3), 0 40px 90px -30px rgba(0,0,0,.85),
      0 0 36px rgba(95,198,255,.1) inset;
    opacity:0;
    /* size/position morph (maximize/minimize) is driven by the FLIP in app.js; the bg + blur
       cross-fade here so the fill changes smoothly alongside it. transform is NOT transitioned
       (the FLIP owns it). */
    transition:background .35s ease, backdrop-filter .35s ease, -webkit-backdrop-filter .35s ease; }
  /* themed scrollbars — the panel's vertical bar auto-hides until hover; code-block bar stays on */
  .win-body{ scrollbar-width:thin; scrollbar-color:transparent transparent; }
  .win-body:hover{ scrollbar-color:rgba(95,198,255,.45) transparent; }
  .md-body pre{ scrollbar-width:thin; scrollbar-color:rgba(95,198,255,.45) transparent; }
  .win-body::-webkit-scrollbar, .md-body pre::-webkit-scrollbar{ width:9px; height:9px; }
  .win-body::-webkit-scrollbar-track{ background:transparent; }
  .md-body pre::-webkit-scrollbar-track{ background:rgba(8,16,38,.35); border-radius:9px; margin:6px; }
  /* panel thumb: invisible at rest, fades in on hover */
  .win-body::-webkit-scrollbar-thumb{ background:transparent; border-radius:9px;
    border:1px solid transparent; transition:background .25s ease, border-color .25s ease, box-shadow .25s ease; }
  .win-body:hover::-webkit-scrollbar-thumb{
    background:linear-gradient(180deg, rgba(150,215,255,.6), rgba(95,198,255,.32));
    border-color:rgba(120,200,255,.3); box-shadow:0 0 8px rgba(95,198,255,.25) inset; }
  .md-body pre::-webkit-scrollbar-thumb{
    background:linear-gradient(180deg, rgba(150,215,255,.6), rgba(95,198,255,.32));
    border-radius:9px; border:1px solid rgba(120,200,255,.3);
    box-shadow:0 0 8px rgba(95,198,255,.25) inset; }
  .win-body:hover::-webkit-scrollbar-thumb:hover, .md-body pre::-webkit-scrollbar-thumb:hover{
    background:linear-gradient(180deg, rgba(180,230,255,.85), rgba(120,205,255,.5)); }
  .win-body::-webkit-scrollbar-corner, .md-body pre::-webkit-scrollbar-corner{ background:transparent; }
  /* (animated glowing edge removed — the corner brackets + soft shadow define the window;
     keeps it calm against the bordered nav rail and animated card edges) */
  /* Resting state is declared on the class (NOT held via animation `forwards`) so the panel's
     transform is a normal, overridable property — the maximize FLIP lands on it and the
     maximized rule can flatten it without `!important`. The animation just plays entry/exit. */
  .panel.show{ opacity:1; transform:perspective(1700px) rotateY(8deg);
    animation:panelIn .6s cubic-bezier(.2,.8,.2,1); }
  .panel.out{ opacity:0; transform:perspective(1700px) rotateY(10deg) translateX(-60px);
    animation:panelOut .4s cubic-bezier(.4,0,.7,.3); }
  @keyframes panelIn{
    0%{ opacity:0; transform:perspective(1700px) rotateY(10deg) translateX(-60px); filter:blur(6px); }
    100%{ opacity:1; transform:perspective(1700px) rotateY(8deg) translateX(0); filter:blur(0); } }
  @keyframes panelOut{
    0%{ opacity:1; transform:perspective(1700px) rotateY(8deg) translateX(0); filter:blur(0); }
    100%{ opacity:0; transform:perspective(1700px) rotateY(10deg) translateX(-60px); filter:blur(6px); } }

  /* ===== FLAT VIEW (Settings → Tilt: Off) — face the menu/panel to the viewer ===== */
  /* !important is required: CSS animations (miIn/panelIn) otherwise win over plain rules. */
  body.flat .menu, body.flat .panel-wrap{ perspective:none; }
  body.flat .mi{ transform:translateX(0) !important; }
  body.flat .mi:hover{ transform:translateX(-8px) scale(1.02) !important; }
  body.flat .mi.active{ transform:translateX(-16px) scale(1.05) !important; }
  body.flat .panel, body.flat .panel.show, body.flat .panel.out{ transform:none !important; }

  /* ===== COMPACT RAIL (short viewports) — the centered 6-item rail would overflow and collide
     with the toprow controls, so window it to ~4 items inside a clipped, scroll/drag-able viewport
     with up/down arrows (vp max-height is set precisely in app.js; the calc here is a no-JS cap).
     The rail is FLATTENED here (perspective/3D pop removed) so the clipped viewport can't cut the
     tilt or the active item's elevation. Arrow wiring + drag live in initMenuScroll(). ===== */
  @media (max-height:700px){
    /* vertically centered within the area below the toprow (the +28px nudges the centre past the
       top controls so the rail clears them even on very short screens) */
    .menu{ top:calc(50% + 28px); gap:8px; perspective:none; max-height:calc(100dvh - 120px); }
    /* NO scroll-snap here: it would snap on every programmatic scrollTop set during a mouse drag,
       so the list "auto-arranges" mid-drag instead of following the cursor. The drag follows 1:1
       and snaps to the nearest item on RELEASE in JS (snapNearest) — the footer-nav feel. */
    .menu-vp{ overflow-y:auto; overscroll-behavior:contain;
      -webkit-overflow-scrolling:touch; scrollbar-width:none; max-height:calc(100dvh - 170px);
      cursor:grab; user-select:none; -webkit-user-select:none; }   /* drag-scroll, like the footer nav */
    .menu-vp:active{ cursor:grabbing; }
    .menu-vp::-webkit-scrollbar{ width:0; height:0; display:none; }
    .menu-track{ perspective:none; gap:10px; }
    /* flatten every item state (base/entrance/hover/active) so nothing pops out of the clip */
    .menu .mi, .menu.in .mi, .menu .mi:hover, .menu .mi.active{ transform:none !important; }
    /* edge blur-fade — items dissolve into blur under the arrows (same effect as the footer nav's
       side edges, here top & bottom). On .menu (which doesn't scroll) so it stays put while the vp
       scrolls; ~36px offset clears the arrows so each strip sits over the viewport's top/bottom edge. */
    .menu::before, .menu::after{ content:""; position:absolute; left:0; right:0; height:44px; z-index:2;
      pointer-events:none; backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px); }
    .menu::before{ top:36px; background:linear-gradient(180deg, rgba(3,5,9,.6), rgba(3,5,9,0));
      -webkit-mask:linear-gradient(180deg,#000 35%,transparent); mask:linear-gradient(180deg,#000 35%,transparent); }
    .menu::after{ bottom:36px; background:linear-gradient(0deg, rgba(3,5,9,.6), rgba(3,5,9,0));
      -webkit-mask:linear-gradient(0deg,#000 35%,transparent); mask:linear-gradient(0deg,#000 35%,transparent); }
    /* plain chevron arrows — no border/box (matches the footer nav's .dnav) */
    .mscroll{ display:flex; align-items:center; justify-content:center; flex:none; height:30px;
      border:0; background:none; color:#bfe2ff; opacity:.8; cursor:pointer; position:relative; z-index:3;
      -webkit-appearance:none; appearance:none; transition:color .25s, opacity .25s; }
    .mscroll svg{ width:22px; height:22px; display:block; }
    .mscroll:hover{ color:#cfe6ff; opacity:1; }
    .mscroll[disabled]{ opacity:.22; pointer-events:none; }
  }

  /* window chrome bar */
  .win-bar{ display:flex; align-items:center; gap:10px; padding:12px 16px;
    border-bottom:1px solid rgba(95,198,255,.16); }
  .win-bar .dots{ display:flex; gap:6px; }
  .win-bar .dots i{ width:8px; height:8px; border-radius:50%; background:rgba(95,198,255,.4);
    box-shadow:0 0 6px rgba(95,198,255,.4); }
  .win-bar .dots i:first-child{ background:#5fc6ff; }
  .win-bar .fname{ font-size:10px; letter-spacing:.18em; color:#8fb4e6; text-transform:uppercase; }
  .win-bar .spread{ margin-left:auto; font-size:10px; color:#41608f; letter-spacing:.1em;
    display:inline-flex; align-items:center; }
  /* interactive window controls (chat title bar): minimize / refresh / fullscreen.
     Icon-only (no colored dots). Only the chat's .dots get .win-ctrls. */
  .win-bar .dots.win-ctrls{ gap:11px; }
  .win-bar .dots.win-ctrls i{ width:auto; height:auto; background:none; box-shadow:none;
    cursor:pointer; display:flex; align-items:center; justify-content:center;
    transition:transform .15s ease; }
  .win-bar .dots.win-ctrls i::after{ content:attr(data-g); font-size:12px; line-height:1;
    font-weight:700; color:#6f93c4; transition:color .2s ease; }
  .win-bar .dots.win-ctrls i:hover{ transform:scale(1.18); }
  .win-bar .dots.win-ctrls i:hover::after{ color:#cfe6ff; }
  /* live "online" status dot — pulsing brand-blue beside JUN · ONLINE */
  .win-bar .spread .live-dot{ display:inline-block; width:6px; height:6px; border-radius:50%;
    background:#5fc6ff; box-shadow:0 0 6px rgba(95,198,255,.9); margin:0 5px 0 3px;
    animation:livePulse 2s ease-in-out infinite; }
  @keyframes livePulse{ 0%,100%{ opacity:1; box-shadow:0 0 5px rgba(95,198,255,.85); }
    50%{ opacity:.4; box-shadow:0 0 11px rgba(95,198,255,1); } }
  .win-body{ padding:30px 30px 34px; flex:1; min-height:0; overflow-y:auto; }
  /* drag-to-scroll: while actively panning, suppress text selection; show a
     grabbing cursor only when the custom cursor is OFF (else the press-pulse
     of the custom cursor is the feedback). See enableDragScroll() in app.js. */
  .win-body.dragging, .chat-scroll.dragging{ user-select:none; -webkit-user-select:none; }
  body:not(.cur-on) .win-body.dragging,
  body:not(.cur-on) .chat-scroll.dragging{ cursor:grabbing; }

  /* ===== CONVERSE chat interface ===== */
  .chat{ display:flex; flex-direction:column; height:100%; }
  /* chat scrollbar: hidden at rest, fades in only while hovering the thread */
  .chat-scroll{ flex:1; overflow-y:auto; padding:22px 24px 8px; display:flex; flex-direction:column;
    gap:14px; scrollbar-width:thin; scrollbar-color:transparent transparent; transition:scrollbar-color .3s ease; }
  .chat-scroll:hover{ scrollbar-color:rgba(95,198,255,.4) transparent; }
  .chat-scroll::-webkit-scrollbar{ width:5px; }
  .chat-scroll::-webkit-scrollbar-thumb{ background:transparent; border-radius:3px; transition:background .3s ease; }
  .chat-scroll:hover::-webkit-scrollbar-thumb{ background:rgba(95,198,255,.35); }
  .msg{ max-width:84%; padding:12px 15px; border-radius:12px; font-size:14px; line-height:1.5;
    animation:msgIn .35s cubic-bezier(.2,.8,.2,1); white-space:pre-wrap; word-wrap:break-word; }
  @keyframes msgIn{ from{ opacity:0; transform:translateY(8px); } to{ opacity:1; transform:none; } }
  .msg.user{ align-self:flex-end; background:linear-gradient(150deg, rgba(60,100,165,.5), rgba(30,55,100,.4));
    border:1px solid rgba(120,180,255,.3); color:#cfe6ff; border-bottom-right-radius:3px; }
  /* Jun's replies: plain text — no bubble background or border */
  .msg.jun{ align-self:flex-start; max-width:100%; padding:2px 0;
    background:none; border:0; border-radius:0; color:#8fb4e6; }
  .msg.jun .who{ display:block; font-size:9px; letter-spacing:.22em; text-transform:uppercase;
    color:#5fc6ff; margin-bottom:5px; opacity:.8; }
  /* rendered inline markdown inside Jun's replies (mdInline → strong/em/code/a) */
  .msg.jun .body strong{ color:#eaf3ff; font-weight:700; }
  .msg.jun .body em{ font-style:italic; }
  .msg.jun .body a{ color:#7fd0ff; text-decoration:underline; text-underline-offset:2px; }
  .msg.jun .body a:hover{ color:#cfe6ff; }
  .msg.jun .body code{ font-family:var(--font-mono),ui-monospace,monospace; font-size:.92em;
    color:#9fe0ff; background:rgba(95,198,255,.1); border:1px solid rgba(95,198,255,.2);
    border-radius:5px; padding:1px 5px; }
  .msg.typing{ color:#7fa6d8; font-style:italic; }
  .msg.typing .dot{ display:inline-block; animation:blink 1.2s infinite; }
  .msg.typing .dot:nth-child(2){ animation-delay:.2s } .msg.typing .dot:nth-child(3){ animation-delay:.4s }
  @keyframes blink{ 0%,60%,100%{ opacity:.25 } 30%{ opacity:1 } }
  /* streaming caret — slim blinking bar at the end of Jun's reply / the greeting while typing */
  .msg.jun.streaming .body::after,
  .chat-intro.is-typing::after,
  .bootline.typing::after{ content:""; display:inline-block; width:2px; height:1.05em;
    margin-left:3px; vertical-align:-0.14em; border-radius:1px; background:#7fd0ff;
    box-shadow:0 0 6px rgba(95,198,255,.7); animation:caret 1.05s step-end infinite; }
  @keyframes caret{ 0%,50%{ opacity:1 } 50.01%,100%{ opacity:0 } }
  /* reusable typewriter caret (typeMarkup in app.js) — slim blinking bar at the live
     typing position; same look as the chat/greeting caret */
  .tw-caret{ display:inline-block; width:2px; height:1.05em; margin-left:1px;
    vertical-align:-0.14em; border-radius:1px; background:#7fd0ff;
    box-shadow:0 0 6px rgba(95,198,255,.7); animation:caret 1.05s step-end infinite; }
  /* typewriter mask — blocks not yet reached stay hidden (incl. list bullets) so they
     animate in WITH their text rather than appearing up-front */
  .tw-mask{ display:none !important; }
  .chat-intro{ align-self:flex-start; font-size:14px; color:#8fb4e6; line-height:1.6;
    border-left:2px solid rgba(95,198,255,.4); padding-left:12px; margin-bottom:4px; }
  .chat-suggest{ display:flex; flex-wrap:wrap; gap:8px; padding:0 24px 12px; }
  .chat-chip{ font-size:14px; padding:7px 12px; border-radius:20px; cursor:pointer;
    background:rgba(95,198,255,.08); border:1px solid rgba(95,198,255,.25); color:#bcd9ff;
    transition:background .25s, border-color .25s; }
  .chat-chip:hover{ background:rgba(95,198,255,.18); border-color:rgba(120,200,255,.5); }
  .chat-input{ display:flex; gap:10px; padding:14px 20px 18px; border-top:1px solid rgba(95,198,255,.14);
    align-items:flex-end; }
  /* transparent input — no box/border; the caret matches the LLM streaming/typewriter caret */
  .chat-input textarea{ flex:1; resize:none; background:transparent; color:#cfe6ff; caret-color:#7fd0ff;
    border:0; border-radius:0; padding:11px 6px; font-size:14px;
    font-family:inherit; line-height:1.4; max-height:90px; outline:none; }
  .chat-input textarea::placeholder{ color:#5d7aa8; }
  /* send button: brand turquoise-blue arrow — no background or shadow */
  .chat-send{ flex-shrink:0; width:40px; height:40px; cursor:pointer; color:var(--plasma);
    background:none; border:0; box-shadow:none; padding:0;
    display:flex; align-items:center; justify-content:center; transition:transform .2s, color .2s; }
  .chat-send:hover{ transform:scale(1.12); color:#cfe6ff; }
  .chat-send:disabled{ opacity:.35; cursor:not-allowed; transform:none; }
  .chat-send svg{ width:22px; height:22px; }
  /* when the panel holds a chat, let it fill height */
  .panel.is-chat{ height:min(78vh,620px); display:flex; flex-direction:column; }
  .panel.is-chat .win-body{ flex:1; padding:0; display:flex; flex-direction:column; min-height:0; }
  /* maximized (fullscreen) panel — fills the viewport. z-index sits BELOW the nav rail
     (.menu z-18) and the top control row (.toprow z-22) so both stay clickable while
     maximized; it still covers the home UI / reactor (z-≤10). */
  .panel-wrap.maximized{ right:0; left:0; width:auto; z-index:16;
    padding:86px 4vw 4vw; perspective:none; }
  /* fullscreen sits over the bright central plasma → near-opaque dark fill so text stays readable */
  /* transform:none (no !important needed — .panel-wrap.maximized .panel out-specifies .panel.show,
     and the entrance animation no longer holds the tilt via `forwards`). Lets the FLIP morph work. */
  .panel-wrap.maximized .panel{ width:min(1180px,100%); max-height:100%; transform:none;
    background:linear-gradient(160deg, rgba(18,34,66,.52), rgba(6,12,26,.42));
    backdrop-filter:blur(22px) saturate(1.2); -webkit-backdrop-filter:blur(22px) saturate(1.2); }
  .panel-wrap.maximized .panel.is-chat{ height:100%; }
  /* Content swaps (Go back / switch section) while maximized must NOT replay the tilt entrance:
     a running panelIn/panelOut animation outranks the flat `transform:none` above (no !important),
     so the card would briefly tilt then snap back to the maximized position. Use a FLAT directional
     slide (same left-slide as the normal panel, minus the perspective/rotateY tilt) — translateX
     only, so it settles back onto the flat `transform:none` resting value with no tilt or snap. */
  .panel-wrap.maximized .panel.show{ animation:panelSlideIn .6s cubic-bezier(.2,.8,.2,1); transform:none; }
  .panel-wrap.maximized .panel.out{ animation:panelSlideOut .4s cubic-bezier(.4,0,.7,.3); transform:none; }
  @keyframes panelSlideIn{
    0%{ opacity:0; transform:translateX(-60px); filter:blur(6px); }
    100%{ opacity:1; transform:translateX(0); filter:blur(0); } }
  @keyframes panelSlideOut{
    0%{ opacity:1; transform:translateX(0); filter:blur(0); }
    100%{ opacity:0; transform:translateX(-60px); filter:blur(6px); } }
  /* During a maximize/minimize size morph (JS adds .sizing across the toggle), suppress the CSS
     entrance/swap animation: flipping .maximized swaps which .panel.show animation applies, and
     CSS would RESTART it — replaying a slide/tilt that fights the WAAPI grow/shrink morph. The
     morph (a script animation) is unaffected by animation:none; .sizing clears on next render. */
  .panel.sizing{ animation:none !important; }
  /* Desktop: don't let the maximized (full-width, centered) panel slide UNDER the right nav
     rail (rail stays on top at z-18). Reserve the rail's column on the right and left-align the
     panel so it sits in the space beside the rail, matching the normal panel's left placement.
     Must come AFTER the base .panel-wrap.maximized rule (equal specificity → later wins); base
     /mobile maximize stays full-width (right:0). Right reserve = rail offset 4vw + rail width
     min(60vw,220px) + a 2vw gap. */
  @media (min-aspect-ratio:1/1){
    .panel-wrap.maximized{ right:calc(6vw + min(60vw,220px)); justify-content:flex-start; }
  }

  /* corner brackets removed — the panel uses a clean continuous rounded border instead */
  .panel .br-c{ display:none; }
  .panel .br-tl{ top:6px; left:6px; border-right:0; border-bottom:0; }
  .panel .br-tr{ top:6px; right:6px; border-left:0; border-bottom:0; }
  .panel .br-bl{ bottom:6px; left:6px; border-right:0; border-top:0; }
  .panel .br-br{ bottom:6px; right:6px; border-left:0; border-top:0; }

  /* panel content */
  .p-eyebrow{ font-size:11px; letter-spacing:.3em; text-transform:uppercase; color:#5fc6ff;
    display:flex; align-items:center; gap:12px; margin-bottom:14px; }
  .p-eyebrow::before{ content:""; width:26px; height:1px; background:#5fc6ff; opacity:.6; }
  .p-title{ font-size:clamp(26px,5vw,46px); font-weight:700; letter-spacing:.02em; margin-bottom:26px;
    color:#cfe6ff; font-family:var(--font-display),var(--font-body),sans-serif; }
  /* ===== doc-section split: typed prose + a floating side graphic (SKILLS radar) =====
     Base/mobile: stacked — the radar sits on top (DOM-first), prose below. */
  .doc-split{ display:block; }
  .doc-aside{ margin:0; }
  /* mission-doc hologram host (the SKILLS radar's WebGL sibling) — a fixed-ratio stage the
     command model + projector beam render into; drag to spin. Canvas fills it absolutely. */
  .holo-doc{ position:relative; width:min(440px,100%); height:clamp(300px,42vh,430px); margin:2px auto 20px; }
  .holo-doc-canvas{ position:absolute; inset:0; width:100%; height:100%; display:block; cursor:grab; touch-action:none; }
  .holo-doc-canvas:active{ cursor:grabbing; }
  /* Desktop + maximized: float the radar to the RIGHT and let it STICK beside the prose as you
     scroll (plenty of room out there once maximized). md-body keeps order:0 so it lands on the
     left even though the radar comes first in the DOM. Must follow the base rule (later wins). */
  @media (min-aspect-ratio:1/1){
    .panel-wrap.maximized .doc-split{ display:flex; gap:clamp(24px,4vw,56px); align-items:stretch; }
    .panel-wrap.maximized .doc-split .md-body{ flex:1 1 auto; min-width:0; order:0; }
    /* radar column stretches to the prose height; the wrapper inside sticks vertically CENTERED in
       the viewport (top:50% + translateY(-50%)) and follows the scroll. The float bob lives on the
       inner <svg>, so it doesn't fight this transform. */
    .panel-wrap.maximized .doc-aside{ flex:0 0 clamp(330px,38%,500px); order:2; margin:0; }
    .panel-wrap.maximized .doc-aside .radar-wrap{ margin:0; position:sticky; top:50%; transform:translateY(-50%); }
    .panel-wrap.maximized .doc-aside .holo-doc{ margin:0; position:sticky; top:50%; transform:translateY(-50%); }
  }

  /* ===== expertise radar (SKILLS) ===== */
  .radar-wrap{ display:flex; justify-content:center; margin:2px 0 28px; }
  /* gentle float — the whole instrument bobs so it reads as "live" (no radar sweep) */
  .radar{ width:min(440px,100%); height:auto; overflow:visible;
    animation:radarFloat 7s ease-in-out infinite; }
  @keyframes radarFloat{ 0%,100%{ transform:translateY(-5px); } 50%{ transform:translateY(5px); } }
  .rd-ring{ fill:none; stroke:rgba(95,198,255,.12); stroke-width:1; }
  .rd-spoke{ stroke:rgba(95,198,255,.16); stroke-width:1; }
  /* breathing data polygon — its glow swells and settles */
  .rd-area{ fill:rgba(95,198,255,.15); stroke:#5fc6ff; stroke-width:1.6;
    filter:drop-shadow(0 0 6px rgba(95,198,255,.55)); animation:radarBreathe 4.5s ease-in-out infinite; }
  @keyframes radarBreathe{
    0%,100%{ filter:drop-shadow(0 0 5px rgba(95,198,255,.4)); }
    50%{ filter:drop-shadow(0 0 13px rgba(95,198,255,.9)); } }
  /* twinkling vertices */
  .rd-dot{ fill:#dff0ff; stroke:#5fc6ff; stroke-width:1;
    filter:drop-shadow(0 0 4px rgba(95,198,255,.85)); animation:radarTwinkle 3s ease-in-out infinite; }
  @keyframes radarTwinkle{ 0%,100%{ opacity:.8; } 50%{ opacity:1; } }
  .rd-label{ fill:#9fc0e6; font-family:var(--font-display),var(--font-mono),monospace;
    font-size:9px; letter-spacing:.04em; text-transform:uppercase; }
  .rd-val{ fill:#5fc6ff; font-weight:700; font-size:9.5px; }
  .rd-data{ transform-box:fill-box; transform-origin:center;
    animation:radarPop .75s cubic-bezier(.2,.8,.2,1) both; }
  @keyframes radarPop{ 0%{ opacity:0; transform:scale(.55); } 100%{ opacity:1; transform:scale(1); } }
  @media (prefers-reduced-motion:reduce){
    .rd-data,.radar,.rd-area,.rd-dot{ animation:none; } }
  /* auto-FILL (not auto-fit): empty column tracks are kept, so cards keep their width when a
     filter leaves only 1-2 visible instead of stretching to fill the row. */
  .p-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr)); gap:16px;
    transform-style:preserve-3d; }
  .p-card{ position:relative; border-radius:11px; padding:20px 18px 18px; overflow:hidden;
    background:linear-gradient(155deg, rgba(40,64,112,.28), rgba(8,14,30,.16));
    backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px);
    transition:transform .4s cubic-bezier(.2,.8,.2,1), box-shadow .4s; isolation:isolate;
    transform-style:preserve-3d; }
  /* inner cards sit flat in the parent panel's plane (no individual tilt) */
  .p-card:hover{ transform:translateY(-3px) translateZ(20px);
    box-shadow:0 16px 40px -14px rgba(95,198,255,.5); }
  .p-card::before{ content:""; position:absolute; inset:0; border-radius:11px;
    border:1px solid rgba(120,200,255,.32);
    box-shadow:0 0 10px rgba(95,198,255,.14) inset, 0 0 12px rgba(95,198,255,.12);
    pointer-events:none; z-index:0; }
  .p-card::after{ content:""; position:absolute; inset:0; border-radius:11px; padding:1px;
    background:conic-gradient(from var(--ang,0deg), rgba(95,198,255,0) 0deg,
      rgba(140,215,255,.85) 70deg, #fff 110deg, rgba(140,215,255,.85) 150deg,
      rgba(95,198,255,.12) 220deg, rgba(95,198,255,0) 360deg);
    -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite:destination-out; mask-composite:exclude;
    pointer-events:none; animation:spinBorder 6s linear infinite;
    filter:drop-shadow(0 0 3px rgba(120,200,255,.5)); z-index:0; }
  .p-card:nth-child(2)::after{ animation-delay:-1.5s }
  .p-card:nth-child(3)::after{ animation-delay:-3s }
  .p-card:nth-child(4)::after{ animation-delay:-4.5s }
  .p-card:nth-child(5)::after{ animation-delay:-6s }
  .p-card:hover::before{ border-color:rgba(150,215,255,.7); box-shadow:0 0 16px rgba(95,198,255,.3) inset, 0 0 22px rgba(95,198,255,.4); }
  .p-card > *{ position:relative; z-index:1; }
  .p-ico{ width:26px; height:26px; color:#7fd0ff; margin-bottom:12px; }
  .p-ico svg{ width:100%; height:100%; }
  .p-card h4{ font-family:var(--font-display),var(--font-body),sans-serif;
    font-size:16px; font-weight:600; letter-spacing:.02em; margin-bottom:7px; color:#cfe6ff; }
  .p-card p{ font-size:12px; line-height:1.5; color:#9bb0d6; }
  .p-card .chips{ margin-top:11px; display:flex; flex-wrap:wrap; gap:5px; }
  .p-card .chip{ font-size:8px; letter-spacing:.06em; color:#6fd0ff; border:1px solid rgba(95,198,255,.2);
    padding:2px 6px; border-radius:999px; font-family:var(--font-display),var(--font-mono),monospace; }
  .p-lead{ font-size:14px; line-height:1.6; color:#b6c4e2; max-width:60ch; }
  .p-stub{ font-size:13px; color:#8295bf; letter-spacing:.04em; }

  /* ===== MISSIONS filter bar (toggle chips above the grid) ===== */
  .p-filters{ display:flex; flex-wrap:wrap; gap:8px; margin:2px 0 20px; }
  .p-filter{ -webkit-appearance:none; appearance:none; cursor:pointer;
    font-size:10px; letter-spacing:.1em; text-transform:uppercase;
    font-family:var(--font-display),var(--font-mono),monospace;
    color:#8fb4e6; background:rgba(8,16,38,.4); padding:5px 12px; border-radius:999px;
    border:1px solid rgba(95,198,255,.22);
    transition:color .2s, border-color .2s, background .2s, box-shadow .2s; }
  .p-filter:hover{ color:#cfe6ff; border-color:rgba(150,215,255,.55); }
  .p-filter.active{ color:#04121f; font-weight:600; border-color:transparent;
    background:linear-gradient(160deg,#9fe0ff,#5fc6ff); box-shadow:0 0 12px rgba(95,198,255,.4); }
  /* hidden by the active filter — collapses out of the grid */
  .p-card.is-hidden{ display:none; }

  /* ===== clickable project cards (cards with a Markdown write-up) ===== */
  .p-card-link{ -webkit-appearance:none; appearance:none; border:0; margin:0; width:100%;
    text-align:left; font:inherit; color:inherit; cursor:pointer; display:block; }

  /* ===== project progress bar (status doubles as its label) ===== */
  /* states are color-coded by data-status (slug of the label). Cyan family stays on-brand;
     amber is the one sanctioned accent, reserved for "in progress" so it reads as live work. */
  .p-prog{ margin-top:13px; }
  .p-prog-head{ display:flex; justify-content:space-between; align-items:baseline; margin-bottom:5px; }
  .p-prog-label{ font-size:8.5px; letter-spacing:.18em; text-transform:uppercase; color:#7f93bf; }
  /* done → turquoise; ongoing → amber; early/parked → muted (default) */
  .p-prog-label[data-status="completed"], .p-prog-label[data-status="live"], .p-prog-label[data-status="shipped"]{ color:#5fc6ff; }
  .p-prog-label[data-status="in-progress"], .p-prog-label[data-status="active"]{ color:#ffb454; }
  .p-prog-val{ font-size:10px; letter-spacing:.04em; color:#9fe0ff; }
  /* percentage matches its label/bar: orange while in progress, cyan otherwise */
  .p-prog-val[data-status="in-progress"], .p-prog-val[data-status="active"]{ color:#ffb454; }
  .p-prog-track{ height:4px; border-radius:999px; overflow:hidden;
    background:rgba(95,198,255,.12); border:1px solid rgba(95,198,255,.14); }
  .p-prog-fill{ display:block; height:100%; width:0; border-radius:999px;
    background:linear-gradient(90deg, rgba(95,198,255,.7), #9fe0ff);
    box-shadow:0 0 8px rgba(95,198,255,.5);
    transition:width .8s cubic-bezier(.2,.8,.2,1); }
  .p-prog-fill[data-status="in-progress"], .p-prog-fill[data-status="active"]{
    background:linear-gradient(90deg, rgba(255,180,84,.55), #ffb454); box-shadow:0 0 8px rgba(255,180,84,.45); }
  .p-prog-fill[data-status="prototype"], .p-prog-fill[data-status="mvp"]{
    background:linear-gradient(90deg, rgba(130,149,191,.5), #b6c4e2); box-shadow:none; }
  @media (prefers-reduced-motion:reduce){ .p-prog-fill{ transition:none; } }

  /* ===== JOURNAL post list ===== */
  .p-postlist{ display:flex; flex-direction:column; gap:16px; }
  .p-post{ -webkit-appearance:none; appearance:none; cursor:pointer; text-align:left; font:inherit; color:inherit;
    display:flex; flex-direction:column; gap:9px; padding:22px 24px; border-radius:10px;
    background:linear-gradient(155deg, rgba(40,64,112,.22), rgba(8,14,30,.12));
    border:1px solid rgba(120,200,255,.18);
    transition:transform .3s cubic-bezier(.2,.8,.2,1), border-color .3s, box-shadow .3s; }
  .p-post:hover{ transform:translateX(5px); border-color:rgba(150,215,255,.55);
    box-shadow:0 0 18px rgba(95,198,255,.18); }
  .p-post-date{ font-size:10px; letter-spacing:.14em; text-transform:uppercase; color:#5a7bb0; }
  .p-post-title{ font-family:var(--font-display),var(--font-body),sans-serif;
    font-size:17px; font-weight:600; color:#cfe6ff; letter-spacing:.01em; }
  .p-post-ex{ font-size:12.5px; line-height:1.55; color:#9bb0d6; }

  /* ===== ARCADE game list (kind:'arcade') ===== */
  /* Arcade list shares the project grid (auto-fill, 3-up) so game cards keep a consistent size
     instead of stretching to full-width rows. Each card is a vertical poster: a clean banner up
     top, with the category / title / PLAY footer stacked over a bottom gradient scrim. */
  .p-gamelist{ display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr)); gap:16px; }
  .p-game{ -webkit-appearance:none; appearance:none; cursor:pointer; text-align:left; font:inherit; color:inherit;
    display:flex; flex-direction:column; align-items:flex-start; justify-content:flex-end; gap:7px;
    padding:16px; min-height:200px; border-radius:12px; overflow:hidden; position:relative;
    background:linear-gradient(155deg, rgba(40,64,112,.24), rgba(8,14,30,.12));
    border:1px solid rgba(120,200,255,.2);
    transition:transform .3s cubic-bezier(.2,.8,.2,1), border-color .3s, box-shadow .3s; }
  .p-game:hover{ transform:translateY(-3px); border-color:rgba(150,215,255,.6);
    box-shadow:0 14px 38px -14px rgba(95,198,255,.5), 0 0 18px rgba(95,198,255,.16); }
  /* banner variant: a hero <img> fills the card (an <img> resolves its src against the page —
     unlike a url() in a custom property, which resolves against the stylesheet → 404); the
     bottom scrim keeps the overlaid text legible */
  .p-game-bg{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; z-index:0;
    border-radius:inherit; }
  /* the banner reads as a clean image up top; a stronger bottom gradient carves out a footer
     band that holds the category, title and PLAY cta */
  .p-game-scrim{ display:none; }
  .p-game.has-banner .p-game-scrim{ display:block; position:absolute; inset:0; z-index:1;
    background:linear-gradient(0deg, rgba(5,10,22,.97) 0%, rgba(5,10,22,.9) 22%, rgba(5,10,22,.28) 52%, rgba(5,10,22,0) 78%); }
  /* content sits above bg/scrim */
  .p-game > :not(.p-game-scrim):not(.p-game-bg){ position:relative; z-index:2; }
  .p-game-meta{ width:100%; display:flex; flex-direction:column; gap:6px; }
  .p-game-tag{ font-family:var(--font-body),ui-sans-serif,system-ui,sans-serif;
    font-size:9px; letter-spacing:.16em; text-transform:uppercase; color:#5fc6ff; opacity:.9; }
  .p-game-titlerow{ display:flex; align-items:baseline; justify-content:space-between; gap:12px; width:100%; }
  .p-game-title{ font-family:var(--font-display),ui-sans-serif,system-ui,sans-serif;
    font-size:16px; font-weight:700; letter-spacing:.04em; color:#cfe6ff; text-transform:uppercase;
    text-shadow:0 1px 12px rgba(0,0,0,.6); }
  .p-game-cta{ flex:none; font-size:10px; letter-spacing:.18em; color:#cfe6ff; white-space:nowrap;
    transition:color .3s, transform .3s; }
  .p-game:hover .p-game-cta{ color:#fff; transform:translateX(4px); }

  /* ===== ARCADE — full-screen game overlay (in-portfolio iframe) ===== */
  .game-ov{ position:fixed; inset:0; z-index:90; display:flex; flex-direction:column;
    background:#020407; opacity:0; transition:opacity .3s ease; }
  .game-ov.in{ opacity:1; }
  .game-bar{ flex:0 0 auto; display:flex; align-items:center; gap:16px; height:50px; padding:0 16px;
    background:linear-gradient(180deg, rgba(8,14,28,.92), rgba(8,14,28,.62));
    border-bottom:1px solid rgba(95,198,255,.22); backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px); }
  .game-back{ -webkit-appearance:none; appearance:none; cursor:pointer;
    border:1px solid rgba(95,198,255,.32); color:#9bc4ff; background:rgba(95,198,255,.05);
    font-size:11px; letter-spacing:.16em; text-transform:uppercase; padding:7px 13px; border-radius:7px;
    font-family:var(--font-display),var(--font-mono),monospace; display:inline-flex; align-items:center; gap:6px;
    transition:color .25s, border-color .25s, background .25s; }
  .game-back:hover{ color:#fff; border-color:rgba(150,215,255,.8); background:rgba(95,198,255,.12); }
  .game-back .db-arrow{ font-size:15px; line-height:1; }
  .game-name{ font-size:14px; font-weight:700; letter-spacing:.16em; color:#cfe6ff; text-transform:uppercase; }
  .game-tag{ margin-left:auto; font-size:10px; letter-spacing:.16em; text-transform:uppercase; color:#5fc6ff; opacity:.8; }
  .game-stage{ position:relative; flex:1; min-height:0; }
  .game-frame{ position:absolute; inset:0; width:100%; height:100%; border:0; display:block; background:#020407; }
  .game-load{ position:absolute; inset:0; display:grid; place-items:center; z-index:1;
    color:#7fd0ff; font-size:12px; letter-spacing:.2em; text-transform:uppercase; pointer-events:none; }
  .game-load::after{ content:""; position:absolute; bottom:38%; width:120px; height:2px; border-radius:2px;
    background:linear-gradient(90deg, transparent, #5fc6ff, transparent); animation:gameScan 1.1s ease-in-out infinite; }
  @keyframes gameScan{ 0%,100%{ opacity:.25; transform:scaleX(.6); } 50%{ opacity:1; transform:scaleX(1); } }
  /* the portfolio's custom cursor freezes over the iframe — hide it while playing (iframe shows its own) */
  body.game-playing #cur, body.game-playing #curArrow, body.game-playing #curDot{ display:none !important; }
  @media (prefers-reduced-motion:reduce){ .game-ov{ transition:none; } .game-load::after{ animation:none; } }
  @media (max-width:760px){ .game-bar{ height:44px; padding:0 12px; gap:10px; }
    .game-name{ font-size:12px; letter-spacing:.1em; } .game-back{ padding:6px 10px; font-size:10px; } }

  /* ===== "back" button in the window bar (doc view) ===== */
  .win-back{ -webkit-appearance:none; appearance:none; background:transparent; cursor:pointer;
    border:1px solid rgba(95,198,255,.3); color:#8fb4e6; font-size:10px; letter-spacing:.16em;
    text-transform:uppercase; padding:5px 10px; border-radius:6px;
    font-family:var(--font-display),var(--font-mono),monospace;
    transition:color .25s, border-color .25s, background .25s; }
  .win-back:hover{ color:#cfe6ff; border-color:rgba(150,215,255,.7); background:rgba(95,198,255,.08); }

  /* "‹ Go back" link above a doc — returns to the list/section it opened from.
     Sticky so it stays reachable on long docs; compact + a blurred dark backdrop
     so scrolling text reads cleanly as it passes under it. */
  .doc-back{ display:inline-flex; align-items:center; gap:5px; align-self:flex-start;
    position:sticky; top:0; z-index:3;
    margin:0 0 16px; padding:4px 10px 4px 8px;
    font-size:9.5px; letter-spacing:.14em; text-transform:uppercase; color:#9fc4f0;
    background:rgba(6,11,20,.78); border:1px solid rgba(95,198,255,.22);
    border-radius:7px; cursor:pointer;
    -webkit-backdrop-filter:blur(7px); backdrop-filter:blur(7px);
    transition:background .2s ease, border-color .2s ease, color .2s ease; }
  .doc-back:hover{ color:#e3f1ff; background:rgba(14,26,44,.85); border-color:rgba(95,198,255,.5); }
  .doc-back .db-arrow{ font-size:13px; line-height:1; transition:transform .2s ease; }
  .doc-back:hover .db-arrow{ transform:translateX(-3px); }

  /* In-content CTA — a journal post / write-up closes with a REAL call to action that drives
     the chat (the primary CTA; no forms — CONVERSE captures email + context). Authored as raw
     HTML in the .md via [data-cta]; wired by the delegated panel handler in app.js. */
  .md-cta-wrap{ margin:32px 0 6px; padding-top:22px; border-top:1px solid rgba(95,198,255,.16);
    display:flex; flex-wrap:wrap; gap:12px; align-items:center; }
  .md-cta{ -webkit-appearance:none; appearance:none; cursor:pointer;
    display:inline-flex; align-items:center; gap:8px;
    font-family:var(--font-display),var(--font-mono),monospace;
    font-size:11px; letter-spacing:.16em; text-transform:uppercase; color:#cfe6ff;
    padding:11px 18px; border-radius:8px;
    background:rgba(95,198,255,.1); border:1px solid rgba(95,198,255,.42);
    transition:color .2s, background .2s, border-color .2s, transform .2s; }
  .md-cta:hover{ color:#e3f1ff; background:rgba(95,198,255,.2);
    border-color:rgba(150,215,255,.85); transform:translateY(-1px); }
  .md-cta .cta-arrow{ font-size:14px; line-height:1; transition:transform .2s ease; }
  .md-cta:hover .cta-arrow{ transform:translateX(3px); }
  /* secondary CTA (e.g. "see the project") — lighter weight than the primary chat button */
  .md-cta.md-cta-2{ background:transparent; color:#9fc4f0; border-color:rgba(95,198,255,.24); }
  .md-cta.md-cta-2:hover{ color:#e3f1ff; background:rgba(14,26,44,.7);
    border-color:rgba(95,198,255,.5); }

  /* floating "scroll to top" — line arrow, bottom-right of the panel; fades in
     once the body is scrolled down. Built by addToTop() in app.js. */
  .to-top{ position:absolute; right:16px; bottom:16px; z-index:6;
    width:38px; height:38px; display:grid; place-items:center;
    border-radius:9px; cursor:pointer; color:#7fd0ff;
    background:rgba(6,11,20,.6); border:1px solid rgba(95,198,255,.28);
    -webkit-backdrop-filter:blur(7px); backdrop-filter:blur(7px);
    opacity:0; transform:translateY(8px); pointer-events:none;
    transition:opacity .25s ease, transform .25s ease, background .2s ease, border-color .2s ease, color .2s ease; }
  .to-top.show{ opacity:1; transform:none; pointer-events:auto; }
  .to-top:hover{ color:#cfe6ff; background:rgba(95,198,255,.16); border-color:rgba(95,198,255,.6); }
  .to-top svg{ width:18px; height:18px; display:block; transition:transform .2s ease; }
  .to-top:hover svg{ transform:translateY(-2px); }

  /* ===== rendered Markdown (blog posts / project write-ups) ===== */
  /* body text matches the chat's LLM-reply color (#8fb4e6 = .msg.jun) for consistency; headings keep their own */
  .md-body{ font-size:14px; line-height:1.7; color:#8fb4e6; max-width:68ch; }
  .md-body > *:first-child{ margin-top:0; }
  .md-body h1{ font-family:var(--font-display),var(--font-body),sans-serif; font-weight:700; letter-spacing:.02em;
    font-size:clamp(22px,4vw,34px); color:#cfe6ff; margin:0 0 18px; }
  .md-body h2{ font-family:var(--font-display),var(--font-body),sans-serif; font-weight:600; letter-spacing:.02em;
    font-size:20px; color:#cfe6ff; margin:30px 0 12px; }
  .md-body h3{ font-family:var(--font-display),var(--font-body),sans-serif;
    font-size:13px; font-weight:600; letter-spacing:.08em; text-transform:uppercase;
    color:#9fd0ff; margin:24px 0 8px; }
  .md-body p{ margin:0 0 16px; }
  .md-body a{ color:#6fd0ff; text-decoration:none; border-bottom:1px solid rgba(95,198,255,.4); }
  .md-body a:hover{ color:#cfe6ff; border-bottom-color:#cfe6ff; }
  .md-body ul,.md-body ol{ margin:0 0 16px; padding-left:22px; }
  .md-body li{ margin:6px 0; }
  .md-body li::marker{ color:#5fc6ff; }
  .md-body strong{ color:#e3edff; font-weight:600; }
  .md-body em{ color:#cdd9f0; }
  .md-body blockquote{ margin:0 0 16px; padding:8px 0 8px 18px; font-style:italic; color:#9fb6df;
    border-left:2px solid rgba(95,198,255,.5); }
  .md-body code{ font-family:var(--font-mono),ui-monospace,monospace; font-size:12.5px; color:#9fe0ff;
    background:rgba(95,198,255,.1); padding:2px 6px; border-radius:5px; }
  .md-body pre{ margin:0 0 18px; padding:16px 18px; border-radius:10px; overflow-x:auto;
    background:rgba(4,10,24,.6); border:1px solid rgba(95,198,255,.18); }
  .md-body pre code{ background:none; padding:0; color:#cfe0ff; line-height:1.6; }
  .md-body hr{ border:0; height:1px; background:rgba(95,198,255,.2); margin:26px 0; }
  .md-body img{ max-width:100%; border-radius:10px; }
  .md-error{ color:#9bb0d6; font-size:13px; line-height:1.6; }

  /* ===== CUSTOM CURSOR — two selectable styles (Settings → Cursor) =====
     Fine-pointer only. JS (initCursor) builds #cur (reticle, lags) + #curArrow
     (glowing dart, exact) + #curDot (core / I-beam, exact) and hides the native
     cursor via .cur-on. Style picks: default = REVISION 1 reticle; .cur-pointer =
     REVISION 2 dart; "off" simply removes .cur-on (native cursor returns). State
     classes (cur-lock / cur-text / cur-down / cur-hidden) live on <body>. */
  body.cur-on, body.cur-on *{ cursor:none !important; }
  #cur, #curArrow, #curDot{ position:fixed; left:0; top:0; z-index:9999;
    pointer-events:none; opacity:0; will-change:transform; }
  #cur svg, #curArrow svg{ display:block; overflow:visible; }

  /* precise core dot / I-beam — tracks the mouse exactly (no lag); shared */
  #curDot{ width:6px; height:6px; margin:-3px 0 0 -3px; border-radius:50%;
    background:var(--hot);
    box-shadow:0 0 8px 2px rgba(95,198,255,.9), 0 0 2px 1px rgba(207,230,255,1);
    transition:width .18s ease, height .18s ease, margin .18s ease,
      border-radius .18s ease, box-shadow .18s ease, opacity .25s ease; }

  /* ---------- REVISION 1 — RETICLE (lock-on HUD) ---------- */
  #cur{ width:52px; height:52px; margin:-26px 0 0 -26px; }
  #cur svg{ width:100%; height:100%; transform-origin:center; transition:transform .16s ease;
    filter:drop-shadow(0 0 4px rgba(95,198,255,.85)); }
  #cur .reticle{ transform-box:fill-box; transform-origin:center;
    animation:curSpin 6s linear infinite; transition:opacity .2s ease; }
  #cur .reticle *{ stroke:var(--plasma); }
  #cur .lock{ opacity:0; transform:scale(.55); transform-box:fill-box; transform-origin:center;
    transition:opacity .2s ease, transform .22s cubic-bezier(.2,1.5,.4,1); }
  #cur .lock *{ stroke:var(--hot); }
  @keyframes curSpin{ to{ transform:rotate(360deg); } }

  /* reticle is the active style when NOT pointer and NOT hidden */
  body.cur-on:not(.cur-pointer):not(.cur-hidden) #cur,
  body.cur-on:not(.cur-pointer):not(.cur-hidden) #curDot{ opacity:1; transition:opacity .25s ease; }
  body.cur-on:not(.cur-pointer).cur-lock #cur .reticle{ opacity:0; }
  body.cur-on:not(.cur-pointer).cur-lock #cur .lock{ opacity:1; transform:scale(1); }
  body.cur-on:not(.cur-pointer).cur-lock #curDot{ width:4px; height:4px; margin:-2px 0 0 -2px; }
  body.cur-on:not(.cur-pointer).cur-down #cur svg{ transform:scale(.82); }
  body.cur-on:not(.cur-pointer).cur-down #curDot{ box-shadow:0 0 14px 4px rgba(95,198,255,1), 0 0 3px 1px #cfe6ff; }
  body.cur-on:not(.cur-pointer).cur-text #cur{ opacity:0 !important; }
  body.cur-on:not(.cur-pointer).cur-text #curDot{ width:2px; height:22px; margin:-11px 0 0 -1px;
    border-radius:1px; box-shadow:0 0 8px 1px rgba(95,198,255,.8); }

  /* ---------- REVISION 2 — POINTER (glowing dart, nested double outline) ---------- */
  /* Hotspot is the dart tip = svg (0,0) = element top-left, so it sits on the mouse. */
  #curArrow{ width:32px; height:32px; }
  #curArrow svg{ width:100%; height:100%; transform-origin:0 0;
    transition:transform .14s cubic-bezier(.2,1.3,.4,1);
    /* glow dialed back to a faint edge — was two big halos (5px + 2px) */
    filter:drop-shadow(0 0 1.5px rgba(95,198,255,.55)); }
  #curArrow .dart-out{ fill:rgba(95,198,255,.18); stroke:var(--plasma); stroke-width:2;
    stroke-linejoin:round; transition:fill .18s ease; }
  /* inner echo: same path, scaled about the dart's centroid → small centered core glyph */
  #curArrow .dart-in{ fill:none; stroke:var(--hot); stroke-width:1.1; stroke-linejoin:round;
    transform-box:fill-box; transform-origin:46% 46%; transform:scale(.42); }

  /* pointer is the active style when .cur-pointer and not hidden */
  body.cur-on.cur-pointer:not(.cur-hidden) #curArrow{ opacity:1; transition:opacity .2s ease; }
  body.cur-on.cur-pointer.cur-lock #curArrow svg{ transform:scale(1.22); }
  body.cur-on.cur-pointer.cur-lock #curArrow .dart-out{ fill:rgba(95,198,255,.45); }
  body.cur-on.cur-pointer.cur-down #curArrow svg{ transform:scale(.85); }
  body.cur-on.cur-pointer.cur-text #curArrow{ opacity:0; }
  body.cur-on.cur-pointer.cur-text:not(.cur-hidden) #curDot{ opacity:1; width:2px; height:22px;
    margin:-11px 0 0 -1px; border-radius:1px; box-shadow:0 0 8px 1px rgba(95,198,255,.8); }

  @media (prefers-reduced-motion: reduce){ #cur .reticle{ animation:none; } }

  /* ===== MOBILE layout (placed last so it wins the cascade over the base .panel-wrap/.menu rules) ===== */
  @media (max-width:760px){
    /* Phone: the FOOTER doors are the nav (PILOT/SKILLS/MISSIONS/…). Hide the side rail + the hamburger
       that opens it, and let the content panel go FULL-SCREEN. Close (✕) returns home to the footer.
       (The footer hides while a panel is open — open one section at a time, close to switch.) */
    .menu{ display:none !important; }
    .menubtn{ display:none !important; }
    /* full-width panel (no nav column to reserve anymore) */
    .panel-wrap{ right:0; left:0; padding-left:5vw; padding-right:5vw; }
    .panel-wrap.maximized{ right:0; left:0; }
    .win-body{ padding:20px 16px 24px; }
    /* near-opaque panel fill on mobile: it sits OVER the bright plasma, so the desktop translucency
       (.32/.22) left text hard to read. Maximized keeps its own (more specific) darker fill. */
    .panel{ background:linear-gradient(160deg, rgba(10,18,38,.9), rgba(3,7,16,.86)); }
  }

