/* ── Dark mode — MOBILE ONLY (2026-09-14) ──────────────────────────────────
   Palette "B — Slate", picked from /darkmode-test.html: the literal inversion
   of the light theme, keeping #DCDCE3's faint blue-violet cast.

   How it switches: an inline script at the very top of every page's <head>
   sets <html data-theme="dark"> from localStorage.theme BEFORE first paint, and
   the burger menu's Hell/Dunkel toggle (/theme/theme.js) flips it live. The nav
   iframe links this same file and follows along on its own.

   Everything below sits inside the 640px query on purpose — "mobile only for
   now". A desktop window, or a phone rotated past 640px, ignores the attribute
   and renders the light theme. This is also the burger menu's breakpoint, so
   the toggle and the theme it controls exist at exactly the same widths.

   This file is <link>ed AFTER each page's own <style>, and every selector here
   carries one more attribute than the rule it overrides, so it wins on
   specificity and source order both — including against the pages' late
   mobile token blocks.

   Deliberately NO `color-scheme: dark`. When an iframe document's used colour
   scheme differs from the colour scheme of the <iframe> element in its parent,
   browsers paint an OPAQUE backdrop behind the frame — and the nav bar is a
   transparent iframe over the page. Colour-scheme on one side and not the
   other would turn the nav strip into a solid block. Nothing here needs it:
   the site draws its own scrollbar and has no native form controls on mobile.

   Shadow pair, derived from the surface rather than picked by eye:
     shadow    = surface x 0.42            rgb(12, 12, 14)
     highlight = surface mixed 10% to white rgb(51, 51, 56), at ~0.5 alpha
   The light theme's highlight is pure white at full alpha; copied onto a dark
   surface that reads as a glowing halo, not a raised edge. Offsets and blur are
   the mobile x0.65 values the light theme already uses at this width. */

@media (max-width: 640px) {

  :root[data-theme="dark"] {
    --bg-surface:     #1C1C22;
    --text-primary:   #E8E8E9;
    --text-secondary: #99999C;
    --text-tertiary:  #606064;
    --border-color:   #404045;

    --shadow-raised-sm: 3.25px 3.25px 7.8px rgba(12,12,14,0.9), -3.25px -3.25px 7.8px rgba(51,51,56,0.55);
    --shadow-raised:    5.2px 5.2px 11.7px rgba(12,12,14,0.9), -5.2px -5.2px 11.7px rgba(51,51,56,0.55);
    --shadow-pressed:   inset 3.25px 3.25px 6.5px rgba(12,12,14,0.85), inset -3.25px -3.25px 6.5px rgba(51,51,56,0.5);

    /* Local names for the same pair, for the handful of rules below that were
       written with literal light-theme shadows instead of the tokens. */
    --dk-shadow:   rgba(12,12,14,0.9);
    --dk-hl:       rgba(51,51,56,0.55);
  }

  /* ── Burger menu ─────────────────────────────────────────────────────── */

  /* #555 is tuned for the light grey surface; on Slate it measures ~2:1. The
     secondary token keeps the same "unselected" weight at ~6:1. */
  [data-theme="dark"] .mobile-mode-btn,
  [data-theme="dark"] .mobile-nav-btn { color: var(--text-secondary); }
  [data-theme="dark"] .mobile-nav-btn.is-active { color: var(--text-primary); }

  /* Selected segment well. :not([data-mode]) keeps the 2D/3D toggle's flat
     orange pill — that rule ties with this one on specificity and would
     otherwise lose its orange on source order. */
  [data-theme="dark"] .mobile-mode-btn:not([data-mode]).is-active,
  [data-theme="dark"] .mobile-mode-btn[data-lang]:active:not(.is-active),
  [data-theme="dark"] .mobile-mode-btn[data-theme-choice]:active:not(.is-active) {
    box-shadow: var(--shadow-pressed);
  }

  /* The darkened backdrop behind the menu panel needs a little more weight
     to separate a dark panel from a dark page. */
  [data-theme="dark"] .mobile-menu { background: rgba(0, 0, 0, 0.7); }

  /* ── Rules written with literal light-theme colours ──────────────────── */

  /* 2D.html: project tags and the to.morrow tile's icon shadow. */
  [data-theme="dark"] .tag { box-shadow: var(--shadow-pressed); }
  [data-theme="dark"] .morrow-icon-tile .app-icon,
  [data-theme="dark"] .app-icon,
  [data-theme="dark"] .win-devices-inner { filter: drop-shadow(0 12px 22px rgba(0, 0, 0, 0.55)); }

  /* about2d.html accordion rows + inset text, contact2d.html buttons. */
  [data-theme="dark"] .item,
  [data-theme="dark"] .contact-item {
    box-shadow: -3.25px -3.25px 7.8px 0 var(--dk-hl), 3.25px 3.25px 7.8px 0 var(--dk-shadow);
  }
  /* Touch keeps :hover stuck on the last-tapped row, and the light rule for it
     outranks the plain .item selector above. */
  [data-theme="dark"] .item:hover:not(.open):not(.closing),
  [data-theme="dark"] .contact-item:hover {
    box-shadow: -3.25px -3.25px 7.8px 0 var(--dk-hl), 3.25px 3.25px 7.8px 0 var(--dk-shadow);
  }
  [data-theme="dark"] .item.open,
  [data-theme="dark"] .item.closing {
    box-shadow: -5.2px -5.2px 15.6px 0 var(--dk-hl), 5.2px 5.2px 15.6px 0 var(--dk-shadow);
  }
  [data-theme="dark"] .text-inset { box-shadow: var(--shadow-pressed); }

  /* Project pages: the hairline between spec-list rows, and the <b class="kw">
     skim keywords, which darken on a light page and so must LIGHTEN here. */
  [data-theme="dark"] .meta-tile + .meta-tile { border-top-color: rgba(255, 255, 255, 0.08); }
  [data-theme="dark"] b.kw { color: #D4D4D8; }

  /* mac-lamp2d.html gallery dots. */
  [data-theme="dark"] .gallery-dot { box-shadow: 2px 2px 2px var(--dk-shadow), -2px -2px 2px var(--dk-hl); }

  /* kaffeemaschine2d.html: the "[ klick mich ]" veil over the machine. */
  [data-theme="dark"] .machine-overlay { background: rgba(28, 28, 34, 0.68); }

  /* unify2d.html: the #1A1A1A palette swatch is the page colour, near enough,
     so it vanishes. A hairline gives it an edge without recolouring the chip. */
  [data-theme="dark"] .color-swatch[style*="1A1A1A"] { box-shadow: inset 0 0 0 1px var(--border-color); }

  /* to-shove2d.html SkeuKit exhibits — the retro/modern icon clusters, the
     "Off air"/"Personal" pill, the day/segmented toggle. First shipped with
     ONLY their light material (the app's own UI, scoped tokens on .sk), on
     the theory that they read as light objects on a dark page and needed no
     more than an opaque backing on the two pure-glass pieces (the .dna-ws
     lip and the icon-cluster rounds, which have no base of their own — see
     the comment on .ic-side .ic-bubble in the page itself). Lucas reported
     they still read as bright UI chrome floating on the dark page, so the
     component now gets its OWN dark palette — .sk.dark from
     shove95-components.css, which this page's .sk block is itself a lifted
     subset of (see the comment there: "the .dark half of that file is left
     out rather than shipped dead"). Re-declaring the tokens here, rather
     than shipping the unused rules, keeps that same convention.
     Every child (.sk-pill, .sk-round, .sk-trough, .sk-seg, .sk-glass, the
     retro/modern bubbles) reads these through var(), so overriding the
     tokens on .sk is enough to redye the whole component tree — nothing
     below needs its own rule. .dna's two extra tokens (ink-muted/faint,
     scoped there because the page's trimmed .sk block never defined them)
     get the same swap. The two pure-glass pieces still need an opaque
     backing — var(--material) now resolves dark, so the SAME rule that
     used to patch them light-on-dark now gives them a correct dark base. */
  [data-theme="dark"] .sk {
    --canvas:#2C2D30; --material:#2C2D30; --material-top:#3C3F42;
    --recess:#191A1C; --recess-bottom:#35373B;
    --outline:#141517; --outline-bottom:#505357;
    --edge-light:#D6E5FF; --edge-shade:#070708;
    --ink:#DCE5F5; --shadow:#09090A;
  }
  [data-theme="dark"] .dna { --ink-muted: #9FAABD; --ink-faint: #707B8C; }

  /* Dark: no inward relief, thinner/dimmer rim, no 1.3x boost on the top
     stop — a bright glass rim reads as a highlight on a dark material, so it
     needs less of it than the light version's near-white-on-near-white
     contact edge. Lifted verbatim from .dark .sk-glass / .dark .sk-glass::after
     in shove95-components.css. */
  [data-theme="dark"] .sk-glass {
    background: radial-gradient(ellipse 62% 62% at 50% 118%,
      rgba(255,255,255,calc(.30 * var(--str))) 0%, rgba(255,255,255,0) 70%);
  }
  [data-theme="dark"] .sk-glass::after {
    padding: max(.8px, calc(2.971px * var(--k)));
    background: linear-gradient(to bottom,
      color-mix(in srgb, var(--edge-light) calc(55% * var(--str)), transparent) 0%,
      color-mix(in srgb, var(--edge-light) calc(5% * var(--str)), transparent) 50%,
      color-mix(in srgb, var(--edge-light) calc(60% * var(--str)), transparent) 100%);
  }

  [data-theme="dark"] .dna-ws,
  [data-theme="dark"] .ic-item .sk-round { background-color: var(--material); }

  /* .ic-side .ic-bubble (the retro/modern labels) already carries this exact
     declaration, unconditionally, in the page's own <style> — so why did it
     need restating here? Both rules are (0,2,0): `.ic-side .ic-bubble`
     (2 classes) versus `[data-theme="dark"] .sk-glass`'s IMPLICIT
     `background-color: transparent` (its `background:` shorthand two rules
     up resets every sub-property it doesn't mention, including this one) —
     an attribute selector plus a class is also (0,2,0). A tie is broken by
     source order, and this file is `<link>`ed AFTER the page's own `<style>`,
     so the transparent reset from .sk-glass was winning and the pills lost
     their opaque backing — which is why they read visibly darker than the
     icon rounds next to them (those get the same fix, one rule up, but at
     (0,3,0): `.ic-item .sk-round` is two classes, so the attribute selector
     tips it over the tie and it never had this problem). One more class in
     the selector is what fixes it here too. */
  [data-theme="dark"] .ic-side .ic-bubble { background-color: var(--material); }

  /* The trough's outer "bloom" (the soft halo that seats it into the page —
     .sk-trough__bloom and its .live__bloom twin) fades from --material-top
     at the top to --recess at the bottom. In dark mode --recess (#191A1C) is
     only a hair off the page's own #1C1C22, so the object had a lit top edge
     and NO contact shadow at all underneath it — it read as floating rather
     than seated, which is what Lucas flagged on the segmented On/Off toggle
     (the others sit further from their own bloom's bottom edge, so it showed
     less there). Bottom stop swapped to a real near-black so the underside
     actually reads as a cast shadow against the page. */
  [data-theme="dark"] .sk-trough__bloom,
  [data-theme="dark"] .live__bloom {
    background: linear-gradient(to bottom, var(--material-top), #000000);
  }

  /* The "Live" mark (the ring-and-core icon under "Live notifications")
     never had a dark palette at all — it is a self-contained component with
     its OWN token scope (.live, not .sk), built when "this site has no dark
     mode" per its own comment, and dark-mobile.css only ever touched .sk. */
  [data-theme="dark"] .live {
    --canvas:#2C2D30; --material-top:#3C3F42;
    --recess:#191A1C; --recess-bottom:#35373B;
    --outline:#141517; --outline-bottom:#505357;
    --edge-light:#D6E5FF; --edge-shade:#070708;
    --ink:#DCE5F5; --shadow:#09090A;
  }
  /* The breathing mark's own keyframes hard-code colours (`breathe-light`
     grows DARK → BLACK, which reads as "more present" against a light page)
     rather than reading --ink, so they need a real second keyframe, not a
     token swap: the dark equivalent has to grow the OTHER way, light → white,
     to read as "more present" against a dark one. Swapping only
     `animation-name` (not the whole `animation` shorthand) keeps the
     duration/timing/iteration the base rule already sets. */
  @keyframes breathe-dark {
    from { opacity: .28; transform: scale(.88); border-color: var(--ink); --core: var(--ink); }
    to   { opacity: 1;   transform: scale(1.08); border-color: #fff;      --core: #fff; }
  }
  [data-theme="dark"] .live[data-onair="true"] .live__mark { animation-name: breathe-dark; }

  /* to-shove2d.html's screen recordings need nothing here any more: the phone
     frame is a separate transparent image over a clipped video (see
     .phone-shot in that page), which looks the same on either theme. */

  /* 2D.html's Cybercoffee tile: two full recordings, not a filter — see the
     markup comment on the tile for why. Swap which one is in flow; the CSS
     `autoplay`/`loop` on both means the hidden one keeps decoding, so a tiny
     script (bottom of 2D.html) also pauses/plays them to match. */
  /* !important: both videos carry an inline `style="display: ..."` (their
     own base state), which any external rule can only beat with !important. */
  [data-theme="dark"] .cc-vid-light { display: none !important; }
  [data-theme="dark"] .cc-vid-dark  { display: block !important; }
  /* The dark recording is 1168x1376 (0.8488), not the light one's
     1064x1104 (0.9638) the tile is shaped for — object-fit: contain was
     therefore pillarboxing it inside the tile, showing the tile's own
     flat background as bars down both sides and shrinking the egg inside
     them (reported as "a frame around it, the egg isn't cut out").
     Reshaping the tile itself to the dark video's own aspect ratio in
     dark mode removes the mismatch at the source, so contain renders
     with zero letterbox — no re-encode, no crop, nothing lost. */
  [data-theme="dark"] .project-tile.tile-tall-media { aspect-ratio: 1168 / 1376; }

  /* ── 3D page (index.html) ────────────────────────────────────────────── */

  /* The joystick is frosted glass over the scene, tinted with the LIGHT
     surface. On a dark theme that pale disc is the brightest thing on screen,
     so the tint becomes Slate and the rims lose most of their white. */
  [data-theme="dark"] #joystick-base {
    background: rgba(28, 28, 34, 0.30);
    border-color: rgba(255, 255, 255, 0.10);
  }
  [data-theme="dark"] #joystick-thumb {
    background: rgba(28, 28, 34, 0.72);
    border-color: rgba(255, 255, 255, 0.16);
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
  }
  /* #help-fab is built from the tokens and follows on its own. */

  /* The veil behind the About / Contact / Craft / Controls windows. The pages
     inside those frames follow the theme themselves — see
     /theme/overlay-theme.js. */
  [data-theme="dark"] #about-overlay,
  [data-theme="dark"] #contact-overlay,
  [data-theme="dark"] #craft-overlay,
  [data-theme="dark"] #controls-overlay { background: rgba(0, 0, 0, 0.6); }

  /* ── Nav iframe (top_row_permanent_V3.html) ──────────────────────────── */

  /* The logo, pill and hamburger hover lifts are written with a literal pure
     white highlight. A phone keeps :hover stuck on whatever was tapped last,
     so after opening the menu the hamburger sat there with a white glow on its
     upper-left edge, as if it were lit. Same lift, Slate pair, at the mobile
     x0.65 scale. */
  [data-theme="dark"] .logo:hover,
  [data-theme="dark"] .pill:hover,
  [data-theme="dark"] .hamburger:hover {
    box-shadow: 7.15px 7.15px 15.6px rgba(12,12,14,0.95), -5.2px -5.2px 13px rgba(51,51,56,0.6);
  }

  /* The LM mark is a black PNG; on a Slate pill it disappears. */
  [data-theme="dark"] .logo-mark { filter: invert(1) brightness(0.92); }
  [data-theme="dark"] .pill__seg.is-active { box-shadow: var(--shadow-pressed); }
}

/* The theme toggle's tap preview, in the LIGHT theme — the language toggle has
   the same rule keyed on [data-lang]; this is its twin for [data-theme-choice]
   so both toggles press the same way. Mobile-only because the burger menu is. */
@media (max-width: 640px) {
  /* The sun / moon glyphs. 13 cells at exactly 2px each = 26px, so every pixel
     of the pixel art lands on whole CSS pixels (a non-integer cell size makes
     crispEdges round some rows fat and some thin). The vertical margins make up
     the rest of the segment's 35.2px text line box (22px x line-height 1.6), so
     this track comes out exactly as tall as the language toggle above it. */
  .theme-pix { display: block; width: 26px; height: 26px; margin: 4.6px auto; }
  /* Moon glyph sized down slightly relative to the sun — margin grows by the
     same amount the icon shrinks so the segment's 35.2px line box (and the
     track's overall height, matched to the language toggle) is unchanged. */
  .mobile-mode-btn[data-theme-choice="dark"] .theme-pix { width: 22px; height: 22px; margin: 6.6px auto; }

  .mobile-mode-btn[data-theme-choice]:active:not(.is-active) {
    box-shadow: inset 3.25px 3.25px 6.5px rgba(174, 174, 192, 0.6), inset -3.25px -3.25px 6.5px rgba(255, 255, 255, 1);
  }
}
