/* ==========================================================================
   DevEloop � shared stylesheet for index.html and the per-asset pages.
   Extracted from index.html when the site gained its second page.
   ========================================================================== */

    :root{
      color-scheme: light dark;

      /* ---- Ember palette: #ba1200 #031927 #9dd1f1 #508aa8 #c8e0f4 ----
         Ink navy and sky blue. The five swatches don't cover a UI on their own,
         so the page, card, border and muted values are derived in the same hue
         family, and #508aa8 is darkened here because the raw swatch only makes
         3.3:1 on white. */
      --accent:#2f6a8a;
      /* A mid navy, not the palette's ink. Ink here would equal --text, which
         made the h1 gradient end in the same colour as the sentence around it
         and took the primary button to near-black. */
      --accent-2:#16496b;
      /* Text laid over the accent gradient (primary button, active filter).
         Inverts to ink in dark mode, where the accents are pale. */
      --on-accent:#ffffff;
      --ok:#1f7a4d;

      --bg:#eef4f9;
      --bg-2:#e0ebf5;
      --card:#ffffff;
      --text:#031927;
      --muted:#556775;
      --border:rgba(3,25,39,.11);
      --border-strong:rgba(3,25,39,.19);
      --shadow:0 1px 2px rgba(3,25,39,.05), 0 8px 24px rgba(3,25,39,.07);
      --shadow-lg:0 2px 4px rgba(3,25,39,.06), 0 18px 40px rgba(3,25,39,.13);
      --chip:rgba(47,106,138,.11);
      /* Hero background wash. Set apart from the accents so the brick red gets
         a place: it is the palette's signature, but a red primary button reads
         as "destructive", so it stays out of anything clickable. */
      --glow-1:#ba1200;
      --glow-2:#9dd1f1;
      /* Second stop of the h1 gradient, kept off --accent-2 so the headline can
         carry the brick red — the one piece of text that can. Dark mode lifts it
         to a brighter ember: the raw #ba1200 only makes 2.81:1 on the ink page. */
      --headline-1:var(--accent);
      --headline-2:#ba1200;

      --radius:16px;
      --radius-sm:10px;
      --maxw:1120px;

      font-family:"Inter var", Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
      font-feature-settings:"cv02","cv03","cv04","cv11";
    }

    /* Dark mode inverts the palette: the ink becomes the page and the two pale
       blues become the accents, which is why --on-accent flips to ink. Keep this
       block and the prefers-color-scheme one below identical. */
    :root[data-theme="dark"]{
      --accent:#9dd1f1;
      --accent-2:#c8e0f4;
      --on-accent:#031927;
      --ok:#7fd8a8;
      --bg:#03151f;
      --bg-2:#08202c;
      --card:#0e2836;
      --text:#dfeaf3;
      --muted:#8ba6b8;
      --border:rgba(223,234,243,.11);
      --border-strong:rgba(223,234,243,.20);
      --shadow:0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.35);
      --shadow-lg:0 2px 4px rgba(0,0,0,.4), 0 18px 40px rgba(0,0,0,.5);
      --chip:rgba(157,209,241,.14);
      --glow-1:#ba1200;
      --glow-2:#508aa8;
      --headline-2:#f2543f;
    }

    @media (prefers-color-scheme: dark){
      :root:not([data-theme="light"]){
        --accent:#9dd1f1;
        --accent-2:#c8e0f4;
        --on-accent:#031927;
        --ok:#7fd8a8;
        --bg:#03151f;
        --bg-2:#08202c;
        --card:#0e2836;
        --text:#dfeaf3;
        --muted:#8ba6b8;
        --border:rgba(223,234,243,.11);
        --border-strong:rgba(223,234,243,.20);
        --shadow:0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.35);
        --shadow-lg:0 2px 4px rgba(0,0,0,.4), 0 18px 40px rgba(0,0,0,.5);
        --chip:rgba(157,209,241,.14);
        --glow-1:#ba1200;
        --glow-2:#508aa8;
        --headline-2:#f2543f;
      }
    }

    *,*::before,*::after{box-sizing:border-box}
    /* Smooth scrolling is switched on by script once the page has loaded, never
       before: while the browser is still doing its own jump to a `#hash` it turns
       that jump into an animation and then abandons it, so a page opened at an
       anchor lands at the top instead of the section. Off during load, the jump is
       instant and lands; clicks afterwards still glide. */
    html.smooth-scroll{scroll-behavior:smooth}
    body{
      margin:0;
      background:var(--bg);
      color:var(--text);
      line-height:1.6;
      -webkit-font-smoothing:antialiased;
      -moz-osx-font-smoothing:grayscale;
      overflow-x:hidden;
    }
    a{color:inherit}
    img{max-width:100%;display:block}

    .wrap{width:100%;max-width:var(--maxw);margin-inline:auto;padding-inline:20px}

    /* ---------- top bar ---------- */
    .topbar{
      position:sticky; top:0; z-index:40;
      backdrop-filter:saturate(180%) blur(12px);
      -webkit-backdrop-filter:saturate(180%) blur(12px);
      background:color-mix(in srgb, var(--bg) 82%, transparent);
      border-bottom:1px solid var(--border);
    }
    .topbar .wrap{display:flex;align-items:center;gap:16px;height:64px}
    .brand{display:flex;align-items:center;gap:10px;text-decoration:none;font-weight:700;letter-spacing:-.01em}
    /* Brand mark: transparent PNG/WebP, so it sits on either theme unchanged. */
    .brand .logo{height:28px;width:auto;flex:0 0 auto}
    .navlinks{margin-left:auto;display:flex;align-items:center;gap:4px}
    .navlinks a{
      text-decoration:none;color:var(--muted);font-size:14px;font-weight:500;
      padding:8px 12px;border-radius:8px;transition:color .15s, background .15s;
    }
    .navlinks a:hover{color:var(--text);background:var(--bg-2)}
    @media (max-width:720px){ .navlinks a.hide-sm{display:none} }

    .theme-toggle{
      width:38px;height:38px;border-radius:10px;flex:0 0 auto;
      border:1px solid var(--border);background:var(--card);color:var(--muted);
      cursor:pointer;display:grid;place-items:center;transition:color .15s,border-color .15s;
    }
    .theme-toggle:hover{color:var(--text);border-color:var(--border-strong)}
    .theme-toggle svg{width:18px;height:18px}
    /* Which glyph shows is driven by JS setting data-shows on the button. */
    .theme-toggle[data-shows="sun"] .icon-moon,
    .theme-toggle[data-shows="moon"] .icon-sun{display:none}

    /* ---------- hero ---------- */
    .hero{position:relative;padding:72px 0 4px;overflow:hidden}
    /* Sized to the hero itself, with both gradients anchored to the top edge so they
       fade out before the bottom — .hero has overflow:hidden, and a gradient still
       carrying colour at that edge gets sliced into a visible hard line. */
    .hero::before{
      content:"";position:absolute;inset:0 -20%;z-index:-1;
      background:
        radial-gradient(52% 115% at 22% 0%, color-mix(in srgb, var(--glow-1) 24%, transparent), transparent 72%),
        radial-gradient(46% 105% at 78% 0%, color-mix(in srgb, var(--glow-2) 22%, transparent), transparent 72%);
      filter:blur(12px);
      pointer-events:none;
    }
    h1{
      margin:0;font-size:clamp(2rem, 5.2vw, 3.35rem);line-height:1.08;
      letter-spacing:-.035em;font-weight:800;
    }
    h1 .grad{
      background:linear-gradient(100deg,var(--headline-1),var(--headline-2));
      -webkit-background-clip:text;background-clip:text;color:transparent;
      /* Keep the phrase intact: background-clip gives each line fragment its own
         gradient sweep, so a split renders "save" and "you hours" as different
         colours. This also forces any narrow-screen wrap to land after "that". */
      white-space:nowrap;
    }

    .btn{
      display:inline-flex;align-items:center;gap:8px;
      padding:11px 20px;border-radius:11px;font-size:14.5px;font-weight:600;
      text-decoration:none;cursor:pointer;border:1px solid transparent;
      transition:transform .15s, box-shadow .15s, background .15s, border-color .15s;
    }
    .btn svg{width:16px;height:16px;flex:0 0 auto}
    .btn-primary{
      background:linear-gradient(135deg,var(--accent),var(--accent-2));color:var(--on-accent);
      box-shadow:0 6px 20px color-mix(in srgb, var(--accent) 32%, transparent);
    }
    .btn-primary:hover{transform:translateY(-1px);box-shadow:0 10px 28px color-mix(in srgb, var(--accent) 40%, transparent)}
    .btn-ghost{background:var(--card);border-color:var(--border-strong);color:var(--text)}
    .btn-ghost:hover{transform:translateY(-1px);box-shadow:var(--shadow)}

    /* ---------- sections ---------- */
    section{scroll-margin-top:80px}
    .section-head{margin:0 0 8px}
    h2{margin:0;font-size:clamp(1.45rem,3vw,1.9rem);letter-spacing:-.025em;font-weight:750}
    .divider{height:1px;background:var(--border);margin:0}

    #catalog{padding:24px 0 80px}

    .filters{display:flex;flex-wrap:wrap;gap:8px;margin:26px 0 26px}
    .filter{
      padding:8px 15px;border-radius:999px;font-size:13.5px;font-weight:600;
      border:1px solid var(--border-strong);background:var(--card);color:var(--muted);
      cursor:pointer;transition:all .15s;
    }
    .filter:hover{color:var(--text)}
    .filter[aria-pressed="true"]{
      background:linear-gradient(135deg,var(--accent),var(--accent-2));
      color:var(--on-accent);border-color:transparent;
      box-shadow:0 4px 14px color-mix(in srgb, var(--accent) 30%, transparent);
    }
    .filter .count{opacity:.65;font-weight:500;margin-left:5px}

    .grid{
      display:grid;gap:22px;
      grid-template-columns:repeat(auto-fill,minmax(300px,1fr));
      list-style:none;padding:0;margin:0;
    }

    .card{
      /* Clears the sticky topbar when jumped to via its #anchor. */
      scroll-margin-top:84px;
      display:flex;flex-direction:column;
      background:var(--card);border:1px solid var(--border);border-radius:var(--radius);
      box-shadow:var(--shadow);overflow:hidden;
      transition:transform .18s ease, box-shadow .18s ease, border-color .18s ease;
    }
    .card:hover{transform:translateY(-3px);box-shadow:var(--shadow-lg);border-color:var(--border-strong)}
    /* Filtering hides cards with the hidden attribute. Needed because the rule
       above sets display:flex, which would otherwise override the UA default of
       [hidden]{display:none} and leave "hidden" cards on screen. */
    .card[hidden]{display:none}

    .thumb{
      position:relative;aspect-ratio:3/2;overflow:hidden;
      /* Idle state is a plain neutral tile. Every card has cover art, so in the
         moment before it decodes the reader should see a quiet placeholder — the
         coloured gradient and the two initials underneath used to flash for a
         fraction of a second on every load, which reads as a blink. They are the
         fallback for a cover that genuinely failed, and `onerror` on the image
         marks the thumb `.no-img` to bring them back. */
      background:var(--bg-2);
      display:grid;place-items:center;
    }
    /* Decorative wash so a coverless tile reads as designed, not as a missing image. */
    .thumb.no-img{background:linear-gradient(135deg, var(--c1,#2f6a8a), var(--c2,#031927))}
    .thumb.no-img::before{
      content:"";position:absolute;inset:0;
      background:
        radial-gradient(60% 80% at 78% 12%, rgba(255,255,255,.30), transparent 62%),
        radial-gradient(46% 64% at 12% 96%, rgba(0,0,0,.28), transparent 60%);
    }
    /* The image sits on top of the gradient/monogram, which stays as the fallback. */
    .thumb img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:1}
    .thumb .mono{
      position:relative;z-index:1;
      font-size:clamp(2rem,5vw,2.6rem);font-weight:800;letter-spacing:-.04em;
      color:rgba(255,255,255,.94);text-shadow:0 2px 18px rgba(0,0,0,.28);
      /* Hidden until the cover is known to be missing — see .thumb above. */
      opacity:0;
    }
    .thumb.no-img .mono{opacity:1}
    .thumb::after{
      content:"";position:absolute;inset:0;pointer-events:none;
      box-shadow:inset 0 -1px 0 var(--border);
    }

    .play{
      position:absolute;inset:0;z-index:2;display:grid;place-items:center;
      border:0;background:rgba(9,14,22,.28);cursor:pointer;
      opacity:0;transition:opacity .18s;
    }
    .card:hover .play,.play:focus-visible{opacity:1}
    .play span{
      width:52px;height:52px;border-radius:50%;background:rgba(255,255,255,.94);
      display:grid;place-items:center;box-shadow:0 6px 20px rgba(0,0,0,.35);
    }
    .play svg{width:20px;height:20px;margin-left:3px;fill:#031927}

    /* Cover art that links to a per-asset page instead of opening the video. */
    .thumb-link{text-decoration:none;cursor:pointer}
    .thumb-cta{
      position:absolute;inset:0;z-index:2;display:grid;place-items:center;
      background:rgba(9,14,22,.42);color:#fff;
      font-size:14.5px;font-weight:650;letter-spacing:.01em;
      opacity:0;transition:opacity .18s;
    }
    .card:hover .thumb-cta,.thumb-link:focus-visible .thumb-cta{opacity:1}

    .card-body{padding:18px 18px 0;flex:1 1 auto;display:flex;flex-direction:column}
    .card h3{margin:0;font-size:1.06rem;line-height:1.32;letter-spacing:-.015em;font-weight:700}
    .card h3 a{text-decoration:none}
    .card h3 a:hover{color:var(--accent)}
    .kicker{
      display:flex;align-items:center;gap:7px;flex-wrap:wrap;
      font-size:12px;color:var(--muted);margin-bottom:9px;
    }
    .kicker .cat{
      color:var(--accent);background:var(--chip);border-radius:6px;
      padding:2px 8px;font-weight:600;
    }
    .card p{margin:10px 0 0;font-size:14px;color:var(--muted);flex:1 1 auto}
    .tags{display:flex;flex-wrap:wrap;gap:6px;margin:14px 0 0;padding:0;list-style:none}
    .tags li{
      font-size:11.5px;color:var(--muted);border:1px solid var(--border);
      border-radius:6px;padding:2px 8px;
    }
    /* Wraps because the two Audio Face Animator cards carry a second button
       (Docs) alongside the store link, which does not fit beside it and the price
       at the 300px minimum card width. */
    .card-foot{
      display:flex;flex-wrap:wrap;align-items:center;gap:10px;
      padding:16px 18px;margin-top:16px;border-top:1px solid var(--border);
    }
    .card-foot .btn{padding:9px 15px;font-size:13.5px}
    .card-foot .price{margin-left:auto;font-size:13.5px;font-weight:700;color:var(--muted)}
    .card-foot .price.free{color:var(--ok)}

    .empty{color:var(--muted);padding:40px 0;text-align:center;grid-column:1/-1}

    /* ---------- about / contact ---------- */
    #about{padding:70px 0;background:var(--bg-2);border-block:1px solid var(--border)}
    /* A single centred column. The old two-column layout was sized for a much taller
       left side (two paragraphs plus a four-item feature list); once that went, the
       short paragraph left a large empty block beside the contact card. */
    #about .section-head,
    #about .about-grid{max-width:680px;margin-inline:auto}
    .about-grid{display:grid;gap:24px;margin-top:26px}
    .about-grid p{color:var(--muted);margin:0 0 14px}
    .panel{
      background:var(--card);border:1px solid var(--border);border-radius:var(--radius);
      padding:26px;box-shadow:var(--shadow);
    }
    .panel h3{margin:0 0 6px;font-size:1.1rem;letter-spacing:-.015em}
    .panel p{margin:0 0 16px;color:var(--muted);font-size:14.5px}
    .mailto{
      display:flex;align-items:center;gap:10px;font-weight:650;text-decoration:none;
      color:var(--accent);word-break:break-all;
    }
    .mailto svg{width:17px;height:17px;flex:0 0 auto}
    .note{
      margin-top:18px;padding-top:16px;border-top:1px solid var(--border);
      font-size:13px;color:var(--muted);
    }

    /* ---------- footer ---------- */
    footer{padding:34px 0;font-size:13.5px;color:var(--muted)}
    footer .wrap{display:flex;flex-wrap:wrap;gap:12px;align-items:center;justify-content:space-between}
    footer a{color:var(--muted);text-decoration:none}
    footer a:hover{color:var(--text)}
    .foot-links{display:flex;flex-wrap:wrap;align-items:center;gap:8px 20px}
    .foot-links a{display:inline-flex;align-items:center;gap:7px}
    .foot-links svg{width:17px;height:17px;flex:0 0 auto}
    .foot-links a:hover svg{color:#ff0000}

    /* ---------- video modal ---------- */
    dialog#video{
      border:0;padding:0;background:transparent;max-width:min(960px,94vw);width:100%;
      overflow:visible;
    }
    dialog#video::backdrop{background:rgba(5,8,12,.78);backdrop-filter:blur(4px)}
    .video-shell{position:relative;aspect-ratio:16/9;border-radius:14px;overflow:hidden;background:#000;box-shadow:var(--shadow-lg)}
    /* Positioned against the shell's edges rather than sized with height:100%.
       The shell's height comes from aspect-ratio, and a percentage height does not
       resolve against that — the iframe silently fell back to its default 150px. */
    .video-shell iframe{position:absolute;inset:0;width:100%;height:100%;border:0;display:block}
    .video-close{
      position:absolute;top:-14px;right:-14px;width:36px;height:36px;border-radius:50%;
      border:0;background:#fff;color:#031927;cursor:pointer;font-size:19px;line-height:1;
      box-shadow:0 4px 14px rgba(0,0,0,.4);
    }
    @media (max-width:600px){ .video-close{top:-46px;right:0} }

    /* ---------- motion ---------- */
    /* Cards are visible by default; JS opts them into the fade-in by adding .pre,
       so a failed observer or blocked script can never leave the grid blank. */
    .reveal{transition:opacity .5s ease, transform .5s ease}
    .reveal.pre{opacity:0;transform:translateY(14px)}
    @media (prefers-reduced-motion: reduce){
      html.smooth-scroll{scroll-behavior:auto}
      *,*::before,*::after{animation:none!important;transition:none!important}
      .reveal.pre{opacity:1;transform:none}
    }
    :focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:6px}
    .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

    /* ==========================================================================
       Per-asset detail pages (e.g. /audio-face-animator/). Only these pages use
       the rules below; the home page never emits these classes.
       ========================================================================== */
    .crumbs{font-size:13px;color:var(--muted);margin-bottom:18px}
    .crumbs a{color:var(--muted);text-decoration:none}
    .crumbs a:hover{color:var(--accent)}
    .crumbs span[aria-hidden]{margin:0 6px;opacity:.5}

    .page-lead{
      margin:22px 0 0;font-size:clamp(1rem,1.9vw,1.175rem);
      color:var(--muted);
    }
    .cta-row{display:flex;flex-wrap:wrap;gap:12px;margin-top:28px}

    /* One measure for the whole page, centred. Prose alone was once capped at 70ch
       while the grids ran the full 1120px, which left a ragged right edge; every
       block now shares a width. It is centred rather than run to the full wrap
       because full width would put body text at roughly 150 characters a line —
       the home page never does that, since its prose sits inside 300px cards. */
    .detail .hero .wrap > *,
    .doc .wrap > *{max-width:860px;margin-inline:auto}

    .doc{padding:8px 0 80px}
    .doc h2{margin:44px 0 12px;font-size:clamp(1.3rem,2.6vw,1.6rem)}
    /* Jumping to a section from the contents list would otherwise park the
       heading underneath the 64px sticky top bar. */
    .doc h2,.doc h3{scroll-margin-top:84px}
    .doc p{color:var(--muted);margin:0 0 14px}
    .doc strong{color:var(--text);font-weight:650}
    .doc code{
      background:var(--bg-2);border:1px solid var(--border);border-radius:6px;
      padding:1px 6px;font-size:.92em;
    }
    /* Inline code only. An unbroken file path — Assets/DevEloop/… — is one long
       "word" that widens the whole page on a phone; code inside <pre> is excluded
       because those scroll horizontally on their own instead. */
    .doc :not(pre) > code{overflow-wrap:anywhere}

    .feature-grid{
      list-style:none;padding:0;margin:18px 0 0;display:grid;gap:16px;
      grid-template-columns:repeat(auto-fit,minmax(260px,1fr));
    }
    .feature-grid li{
      background:var(--card);border:1px solid var(--border);border-radius:var(--radius-sm);
      padding:16px 18px;box-shadow:var(--shadow);
    }
    .feature-grid b{display:block;margin-bottom:4px;font-weight:650}
    .feature-grid span{color:var(--muted);font-size:14px}

    .req-note{
      background:var(--chip);border:1px solid color-mix(in srgb, var(--accent) 25%, transparent);
      border-radius:var(--radius-sm);padding:12px 16px;color:var(--text)!important;
      font-size:14.5px;
    }
    .req-list{margin:14px 0 0;padding-left:20px;color:var(--muted)}
    .req-list li{margin-bottom:8px}
    .req-list b{color:var(--text);font-weight:650}

    .video-embed{
      position:relative;aspect-ratio:16/9;margin:18px 0 12px;
      border-radius:14px;overflow:hidden;background:#000;box-shadow:var(--shadow-lg);
    }
    /* A vertical video — a YouTube Short — gets its own aspect ratio and a width
       cap, so it sits centred in the column instead of stretching the page into
       one very tall frame. */
    .video-embed.portrait{aspect-ratio:9/16;max-width:min(380px,72vw);margin-inline:auto}
    .video-embed iframe{position:absolute;inset:0;width:100%;height:100%;border:0;display:block}
    .video-facade{
      position:absolute;inset:0;width:100%;height:100%;padding:0;border:0;
      background:none;cursor:pointer;display:block;
    }
    .video-facade img{width:100%;height:100%;object-fit:cover}
    .video-play{
      position:absolute;inset:0;display:grid;place-items:center;
      background:rgba(9,14,22,.30);transition:background .18s;
    }
    .video-facade:hover .video-play{background:rgba(9,14,22,.14)}
    .video-play svg{
      width:74px;height:74px;padding:22px;box-sizing:content-box;
      background:rgba(255,255,255,.94);border-radius:50%;fill:#031927;
      box-shadow:0 6px 22px rgba(0,0,0,.4);
    }
    /* Sits directly under the featured video, so it keeps the frame's own
       bottom margin rather than adding a second one. */
    .vid-caption{margin:0 0 4px;font-size:14px;color:var(--muted)}
    .vid-links{font-size:14px}
    .vid-links a{color:var(--accent);text-decoration:none;font-weight:600}
    .vid-links a:hover{text-decoration:underline}

    .edition-grid{display:grid;gap:18px;margin-top:18px}
    @media (min-width:700px){ .edition-grid{grid-template-columns:1fr 1fr} }
    .edition-grid .panel h3{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
    .edition-grid .panel .btn{margin-top:4px}

    .back-link{margin-top:48px}
    .back-link a{color:var(--accent);text-decoration:none;font-weight:600}
    .back-link a:hover{text-decoration:underline}

    /* ==========================================================================
       Markdown output. Doc pages are written in Markdown and converted by Jekyll,
       so these elements arrive as plain tags with no classes to hook onto.
       ========================================================================== */
    .doc h3{margin:28px 0 8px;font-size:1.12rem;letter-spacing:-.015em;font-weight:700}
    /* Prose links only. Without :not(.btn) this outranks .btn-primary — a single
       class — and repaints button labels in the accent colour, leaving them at
       1:1 contrast against their own accent-gradient background. */
    .doc a:not(.btn){color:var(--accent);text-decoration:none}
    .doc a:not(.btn):hover{text-decoration:underline}
    /* A bold link is still a link. `.doc strong` above sets an explicit colour,
       which otherwise wins inside the anchor and paints the label body text. */
    .doc a:not(.btn) strong{color:inherit}
    .doc ul,.doc ol{margin:0 0 14px;padding-left:22px;color:var(--muted)}
    .doc li{margin-bottom:6px}
    .doc li > ul,.doc li > ol{margin:6px 0 0}

    /* Code blocks scroll on their own rather than widening the page. */
    .doc pre{
      background:var(--bg-2);border:1px solid var(--border);border-radius:var(--radius-sm);
      padding:14px 16px;overflow-x:auto;margin:0 0 16px;font-size:13.5px;line-height:1.55;
    }
    .doc pre code{background:none;border:0;padding:0;font-size:inherit}

    /* Same for tables — the wrapper keeps a wide table from breaking the layout.
       The horizontal margin must stay `auto`: this rule ties on specificity with
       the `.doc .wrap > *` measure above and wins on order, so a `margin:0 0 16px`
       here silently un-centres the table and hangs it 110px left of the prose. */
    .doc .table-scroll{overflow-x:auto;margin:0 auto 16px}
    .doc table{border-collapse:collapse;width:100%;font-size:14px}
    .doc th,.doc td{
      border:1px solid var(--border);padding:8px 12px;text-align:left;vertical-align:top;
    }
    .doc th{background:var(--bg-2);font-weight:650;color:var(--text)}
    .doc td{color:var(--muted)}

    /* Screenshots. Unity's editor chrome is mid-grey, so a border is what keeps a
       screenshot from bleeding into the page background in one theme or the other. */
    .doc img{
      display:block;max-width:100%;height:auto;margin:18px 0;
      border:1px solid var(--border);border-radius:var(--radius-sm);
      background:var(--bg-2);
    }

    .doc blockquote{
      margin:0 auto 16px;padding:2px 0 2px 16px;
      border-left:3px solid color-mix(in srgb, var(--accent) 45%, transparent);
      color:var(--muted);
    }
    .doc blockquote p:last-child{margin-bottom:0}
    .doc hr{border:0;border-top:1px solid var(--border);margin:32px 0}

    /* ---------- documentation table of contents ----------
       One list, two shapes. Narrow: a contents card above the prose, where it sits
       in source order. Wide: a sticky column beside the prose, in view the whole
       way down. The markup is identical — only the container changes. */
    .doc-toc{margin:22px auto 8px;padding:16px 18px;
      border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--bg-2)}
    .doc-toc-head{
      margin:0 0 10px;font-size:.75rem;font-weight:700;letter-spacing:.08em;
      text-transform:uppercase;color:var(--muted);
    }
    .doc-toc ul{list-style:none;margin:0;padding:0}
    .doc-toc li{margin:0}
    .doc-toc a{
      display:block;padding:5px 0;color:var(--muted);text-decoration:none;
      font-size:.925rem;line-height:1.4;
    }
    .doc-toc a:hover{color:var(--accent)}
    .doc-toc .lvl-3 a{padding-left:14px;font-size:.875rem}
    /* The card sits above the opening paragraph, so it has to stay short: it
       lists the sections only, exactly as the hand-written lists it replaced did,
       and it splits into two columns as soon as there is width for them. Every
       subsection is still one scroll away in the sidebar on a wide screen. */
    @media (max-width:1199px){
      .doc-toc .lvl-3{display:none}
    }
    @media (min-width:620px) and (max-width:1199px){
      .doc-toc ul{columns:2;column-gap:36px}
      .doc-toc li{break-inside:avoid}
    }
    /* The section currently being read. */
    .doc-toc a[aria-current]{color:var(--accent);font-weight:650}

    /* Two columns only once both fit at full width: 240 + 48 + 860, plus the
       wrap's own 20px padding on each side. Below that the card layout above
       stands, and the list stays at the top of the page. */
    @media (min-width:1200px){
      /* The hero shares the grid so that the title, the breadcrumb and the prose
         keep a single left edge — the doc column is off-centre on the page, and a
         centred title above it would read as a misalignment. */
      .js-toc .detail .hero .wrap,
      .js-toc .doc .wrap{
        max-width:1250px;
        display:grid;grid-template-columns:240px minmax(0,860px);
        column-gap:48px;justify-content:center;align-items:start;
      }
      .js-toc .detail .hero .wrap > *,
      .js-toc .doc .wrap > .doc-body{grid-column:2;max-width:860px;margin-inline:0}

      .js-toc .doc-toc{
        grid-column:1;grid-row:1;
        position:sticky;top:88px;
        /* Long contents lists scroll inside the column rather than running off
           the bottom of a short window. */
        max-height:calc(100vh - 116px);overflow-y:auto;overscroll-behavior:contain;
        margin:0;padding:0;background:none;border:0;
      }
      /* A rail down the left of the list, with the active entry marked on the
         rail itself — the -1px pull puts the accent segment on top of the rule
         rather than beside it, so the marker reads as part of the same line. */
      .js-toc .doc-toc ul{border-left:1px solid var(--border)}
      .js-toc .doc-toc a{
        padding:5px 0 5px 14px;margin-left:-1px;
        border-left:2px solid transparent;
      }
      .js-toc .doc-toc a[aria-current]{border-left-color:var(--accent)}
      .js-toc .doc-toc .lvl-3 a{padding-left:28px}
    }

    .doc-links{margin:14px 0 0;padding-left:20px;color:var(--muted)}
    .doc-links li{margin-bottom:8px}
    .doc-links a{color:var(--accent);text-decoration:none;font-weight:600}
    .doc-links a:hover{text-decoration:underline}

    /* Cross-links to the other documentation pages, appended to every doc page by
       `_layouts/doc.html`. Set apart from the prose by a rule rather than a card,
       because it is an appendix to the document, not another section of it. */
    .doc-more{margin:56px 0 0;padding-top:26px;border-top:1px solid var(--border)}
    .doc-more h2{font-size:1.05rem;margin:0 0 12px;letter-spacing:.01em}
    .doc-more ul{margin:0;padding:0;list-style:none;display:grid;gap:10px}
    .doc-more li{font-size:.95rem;line-height:1.5}
    .doc-more a{color:var(--accent);text-decoration:none;font-weight:600}
    .doc-more a:hover{text-decoration:underline}
    .doc-more span{color:var(--muted)}
