/* system.kristianpaulsen.com, the docs SITE's chrome only (nav, sidebar, code
   blocks, tabs, palette tables). The consumer-facing baseline (reset, focus
   ring, color-scheme) and the tokens live in the project's globals.css, which
   every page also loads (the root landing @imports it). A project handoff is
   globals.css + a component's CSS, never this file. The chrome references the
   project tokens directly, so every page IS a live demo of its project.

   Fonts are loaded via <link> tags in each page's <head> (with preconnect), not
   an @import here, @import would make the browser fetch this file first and
   only then discover the font stylesheet, serialising two round-trips. */

/* Cascade layers. Declared once here (idempotently re-declared in tokens.css and
   components.css so the order holds regardless of load sequence). Order low→high:
   base (site chrome) < tokens < components. Any UNLAYERED CSS a consumer writes
   beats all of these, so overrides need no specificity gymnastics. */
@layer base, tokens, components;

@layer base {
  /* Chrome layout tokens. The consumer baseline (reset, color-scheme,
   --flip) lives in globals.css, loaded on every page. */
  :root {
    --nav-h: 56px;
    --sidebar-w: 15rem;
    --page-gutter: 1.5rem; /* fixed 24px chrome gutter for header + sidebar */
    --measure: 65ch; /* reading line length in characters (the actual measure rule; ~45–75 ideal) */
    --font: "Geist", -apple-system, blinkmacsystemfont, "Helvetica Neue", arial, sans-serif;
    --mono: "Geist Mono", ui-monospace, sfmono-regular, "SF Mono", menlo, monospace;
  }

  /* Cross-document view transitions: a soft cross-fade between pages as you
   navigate the docs. Both pages opt in (base.css is on every page) and same-
   origin nav is required. Progressive enhancement, unsupported browsers just
   navigate normally. Disabled at mobile widths: there the nav is a hamburger
   drawer, and transitioning an open drawer into the next page reads as a jump. */
  @media (min-width: 640px) {
    @view-transition {
      navigation: auto;
    }
  }

  /* The global reduced-motion guard (in globals.css) can't reach view-transition
   pseudos, so silence the page-transition animation explicitly here. */
  @media (prefers-reduced-motion: reduce) {
    ::view-transition-group(*),
    ::view-transition-old(*),
    ::view-transition-new(*) {
      animation: none !important;
    }
  }

  html {
    -webkit-text-size-adjust: 100%;

    /* Kill the mobile tap-highlight (a square, radius-ignoring translucent overlay
     the browser paints on tap). Our own :active states are the press feedback. */
    -webkit-tap-highlight-color: transparent;

    /* Sticky nav overlaps the top, reserve its height when jumping to any
     in-page anchor (Skip-to-content, hash links). */
    scroll-padding-top: var(--nav-h);
  }

  body {
    /* 340, not the 360 narrowest target: a classic vertical scrollbar eats ~15px
       of a 360 window, so a 360 floor makes that exact width scroll sideways. */
    min-width: 340px;
    margin: 0;
    background: var(--background-primary);
    color: var(--ink-primary);
    font-family: var(--font);
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
  }

  /* Applied briefly during a theme swap so no element animates its color change. */
  .no-transitions,
  .no-transitions *,
  .no-transitions *::before,
  .no-transitions *::after {
    transition: none !important;
  }

  /* Accessibility: skip-to-content, hidden until keyboard-focused. */
  .skip-link {
    position: fixed;
    top: 1rem;
    left: 50%;
    z-index: 100;
    display: inline-flex;
    align-items: center;
    height: 40px;
    padding-inline: 1.25rem;
    color: var(--background-primary);
    background: var(--ink-primary);
    border-radius: var(--radius-full);
    font-size: var(--font-body-small-size);
    font-weight: 500;
    text-decoration: none;
    transform: translate(-50%, calc(-100% - 1rem));
  }

  .skip-link:focus-visible {
    transform: translate(-50%, 0);
    outline: var(--focus-ring-width) solid var(--focus);
    outline-offset: var(--focus-ring-offset);
  }

  /* Site nav (injected on every page) */
  .site-nav {
    position: sticky;
    top: 0;
    z-index: 20;
    background: var(--background-primary);
  }

  /* Root landing: transparent, brandless, just the theme toggle. */
  .site-nav--bare {
    background: transparent;
  }

  .site-nav__inner {
    height: var(--nav-h);
    padding-inline: var(--page-gutter);
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  .site-nav__left {
    display: flex;
    align-items: center;
    gap: 0.25rem;
  }

  /* Right cluster: the Example link sits beside the theme toggle, which keeps
   its own -0.5rem end margin so its icon lands on the page-gutter. */
  .site-nav__right {
    display: flex;
    align-items: center;
    gap: 0.5rem;
  }

  .site-nav__menu {
    display: inline-flex; /* mobile drawer toggle; hidden at lg */
    position: relative;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;

    /* Mirror the theme toggle: 32px hit target, pulled 8px toward the corner
     so the icon's visible content sits at the page-gutter. */
    margin-inline-start: -0.5rem;
    padding: 0;
    color: var(--ink-primary);
    background: none;
    border: 0;
    border-radius: var(--radius-sm);
    cursor: pointer;
  }

  /* Hamburger ↔ X morph. Both lines pivot around the SVG's centre. To keep the
   crossover perfectly symmetrical, we normalise each line to y=12 first
   (translate) then rotate ±45° around the same origin. */
  .site-nav__menu .ham-line {
    /* Custom mark, pin its own weight so the shared icon stroke can be Tabler's 2. */
    stroke-width: 1.75;
    transform-origin: center;
    transform-box: fill-box;
    transition: transform 0.2s ease;
  }

  .site-nav__menu[aria-expanded="true"] .ham-line--top {
    transform: translateY(3px) rotate(45deg);
  }

  .site-nav__menu[aria-expanded="true"] .ham-line--bot {
    transform: translateY(-3px) rotate(-45deg);
  }

  /* Sidebar (injected). Base = mobile off-canvas drawer; becomes a sticky left
   rail inside the shell at lg (≥1024, see media query below). */
  .sidebar {
    position: fixed;
    inset: 0;
    width: auto;
    height: 100dvh;
    padding: calc(var(--nav-h) + 1.5rem) var(--page-gutter) 3rem;
    background: color-mix(in srgb, var(--background-primary) 85%, transparent);
    -webkit-backdrop-filter: saturate(1.4) blur(12px);
    backdrop-filter: saturate(1.4) blur(12px);
    z-index: 15; /* Below nav (z-index: 20) so header contents stay clickable. */
    opacity: 0;

    /* Drawer scrolls internally but hides its own scrollbar, the outer page
     scrollbar stays visible for continuity. Overscroll is
     contained so reaching the end doesn't bleed to the page. */
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: none;

    /* visibility: hidden stops the browser painting the drawer at all, prevents
     a flash of the open state when the viewport crosses the breakpoint. */
    visibility: hidden;
    pointer-events: none;
  }

  /* Container is only a programmatic focus holder on drawer open, never show a
   ring on it (the links have their own focus styles). */
  .sidebar:focus-visible {
    outline: none;
  }

  .sidebar::-webkit-scrollbar {
    display: none;
  }

  /* Animate the drawer only when the user actually toggles it, not on the
   media-query flip when the viewport crosses the breakpoint. JS adds .is-anim
   on first interaction so pure resize snaps instead of slides. */
  .sidebar.is-anim {
    transition:
      opacity 0.2s ease-out,
      visibility 0s linear 0.2s;
  }

  .sidebar.is-anim.is-open {
    transition: opacity 0.2s ease-out;
  }

  .sidebar.is-open {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
  }

  /* Scroll fade: when the sticky sidebar overflows, its top/bottom edges fade to
   hint more content. Scroll-driven, so the top fades only once scrolled down and
   the bottom only while there's more below, and neither fades when the list
   fits (an inactive scroll timeline leaves the fade sizes at 0). Chrome/Edge;
   other browsers just show no fade. @property makes the fade sizes animatable. */
  @property --sidebar-fade-top {
    syntax: "<length>";
    inherits: false;
    initial-value: 0;
  }

  @property --sidebar-fade-bottom {
    syntax: "<length>";
    inherits: false;
    initial-value: 0;
  }

  @media (min-width: 1024px) {
    /* Rail: undo the drawer, sticky left column, opaque, no blur, always shown.
     Inline-start padding = page-gutter minus the link's inline padding, so link
     text lands at 24px and its hover/active pill extends 8px before the text. */
    .sidebar {
      position: sticky;
      inset: auto;
      top: var(--nav-h);
      align-self: flex-start;
      flex: none;
      width: var(--sidebar-w);
      height: auto;
      max-height: calc(100dvh - var(--nav-h));
      padding-block: 0.75rem 3rem;
      padding-inline: calc(var(--page-gutter) - 0.5rem) 1.5rem;
      background: var(--background-primary);
      -webkit-backdrop-filter: none;
      backdrop-filter: none;
      z-index: 18;
      opacity: 1;
      visibility: visible;
      pointer-events: auto;
    }

    @supports (animation-timeline: scroll()) {
      .sidebar {
        --sidebar-fade: 2.5rem;

        /* Clear strip of page surface below the bottom fade, the fade finishes
         this far above the edge instead of running to it. */
        --sidebar-fade-gap: 2rem;

        mask-image: linear-gradient(
          to bottom,
          transparent 0,
          #000 var(--sidebar-fade-top),
          #000 calc(100% - var(--sidebar-fade-gap) - var(--sidebar-fade-bottom)),
          transparent calc(100% - var(--sidebar-fade-gap))
        );
        animation:
          sidebar-fade-top linear both,
          sidebar-fade-bottom linear both;
        animation-timeline: scroll(self), scroll(self);
        animation-range:
          0 var(--sidebar-fade),
          calc(100% - var(--sidebar-fade)) 100%;
      }
    }

    @keyframes sidebar-fade-top {
      from {
        --sidebar-fade-top: 0;
      }

      to {
        --sidebar-fade-top: var(--sidebar-fade);
      }
    }

    @keyframes sidebar-fade-bottom {
      from {
        --sidebar-fade-bottom: var(--sidebar-fade);
      }

      to {
        --sidebar-fade-bottom: 0;
      }
    }

    /* Hamburger toggle is drawer-only. */
    .site-nav__menu {
      display: none;
    }
  }

  /* Base = mobile drawer: big links, no pill. Compact rail values restored at lg. */
  .sidebar__group {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
  }

  .sidebar__group + .sidebar__group {
    margin-top: 2rem;
  }

  .sidebar__group-title {
    display: flex;
    align-items: center;
    height: 32px;
    margin: 0;
    padding-inline: 0;
    font-size: var(--font-caption-medium-size);
    font-weight: 500;
    color: var(--ink-secondary);
  }

  .sidebar__link,
  .sidebar__back {
    display: block;
    text-decoration: none;
  }

  .sidebar__link {
    font-size: var(--font-title-small-size);
    line-height: 1.4;
    color: var(--ink-primary);
    font-weight: 500;
  }

  /* "New" dot, primary marker next to recently-added pages. Sits inside the
   link's inner span, so the flex gap spaces it off the label. */
  .sidebar__new {
    flex-shrink: 0;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--primary-foreground);
  }

  .sidebar__back {
    margin-bottom: 1.75rem;
    font-size: var(--font-body-small-size);
    color: var(--ink-secondary);
    font-weight: 500;
  }

  /* Visual pill wraps just the label; the anchor stays full width for clicks.
   The drawer shows no pill (background added at lg); keep the radius so the
   focus ring reads rounded. */
  .sidebar__link > span,
  .sidebar__back > span {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0;
    border-radius: var(--radius-sm);
  }

  .sidebar__link:focus-visible,
  .sidebar__back:focus-visible {
    outline: none;
  }

  /* Bare text in the drawer, push the ring out for breathing room (0 at lg). */
  .sidebar__link:focus-visible > span,
  .sidebar__back:focus-visible > span {
    outline: var(--focus-ring-width) solid var(--focus);
    outline-offset: 4px;
    animation: focus-ring-converge var(--motion-default);
  }

  @media (hover: hover) {
    .sidebar__back:hover {
      color: var(--ink-primary);
    }
  }

  .sidebar-backdrop {
    display: none;
  }

  /* lg ≥1024: restore the compact rail typography + pill that the drawer enlarged.
   Placed AFTER the base drawer rules so these overrides win on desktop. */
  @media (min-width: 1024px) {
    .sidebar__group {
      gap: 2px;
    }

    .sidebar__group + .sidebar__group {
      margin-top: 1.5rem;
    }

    .sidebar__group-title {
      padding-inline: 0.5rem;
      font-size: var(--font-caption-small-size);
    }

    .sidebar__link {
      font-size: var(--font-caption-medium-size);
      line-height: 20px;
    }

    .sidebar__back {
      margin-bottom: 1.25rem;
      font-size: var(--font-caption-medium-size);
    }

    /* Pill returns: label-hugging surface, ring sits tight to it. */
    .sidebar__link > span,
    .sidebar__back > span {
      padding: 0.375rem 0.5rem;
    }

    .sidebar__link:focus-visible > span,
    .sidebar__back:focus-visible > span {
      outline-offset: 0;
    }

    .sidebar__link.is-active > span {
      background: var(--background-secondary-active);
    }
  }

  /* Hover pill, desktop rail only (pointer-capable). The active link keeps its
   own surface, so it opts out of the hover pill. */
  @media (min-width: 1024px) and (hover: hover) {
    .sidebar__link:not(.is-active):hover > span {
      background: var(--background-secondary);
    }
  }

  .site-nav__brand {
    font-size: var(--font-body-medium-size);
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--ink-primary);
    text-decoration: none;
  }

  .site-nav__brand:focus-visible {
    outline: var(--focus-ring-width) solid var(--focus);
    outline-offset: var(--focus-ring-offset);
    border-radius: 4px;
  }

  .theme-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font: inherit;
    width: 32px;
    height: 32px;

    /* Sit 16px from the viewport edge (rather than 24), tighter to the corner. */
    margin-inline-end: -0.5rem;
    padding: 0;
    color: var(--ink-primary);
    background: none;
    border: 0;
    border-radius: var(--radius-sm);
    cursor: pointer;
  }

  @media (hover: hover) {
    .theme-toggle:hover {
      background: var(--background-secondary);
    }
  }

  /* "View source" link: same 32px icon-button footprint as the theme toggle. */
  .site-nav__source {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    color: var(--ink-primary);
    border-radius: var(--radius-sm);
    text-decoration: none;
  }

  @media (hover: hover) {
    .site-nav__source:hover {
      background: var(--background-secondary);
    }
  }

  /* Hairline between the Example button and the icon cluster. The flex gap on
   .site-nav__right gives it symmetric breathing room on both sides. */
  .site-nav__divider {
    width: var(--border-width);
    height: 20px;
    background: var(--fill-tertiary);
  }

  /* Project search: a typeahead over this project's pages. The wrap is the
   positioning context for the results dropdown. Hidden on phones (too little
   nav room); shown from 768 up (below), and the divider immediately after the
   search hides with it. */
  .site-nav__search-wrap {
    display: none;
    position: relative;
    width: 12rem;
    min-width: 0; /* allow the input to shrink instead of overflowing the nav */
  }

  .site-nav__search-wrap + .site-nav__divider {
    display: none;
  }

  /* Plain secondary-surface fill; focus adds a tinted hairline (no ring, no
   fill change) as the active cue. */
  .site-nav__search {
    width: 100%;
    height: 32px;
    padding: 0 0.625rem;
    font: inherit;
    font-size: var(--font-caption-medium-size);
    color: var(--ink-primary);
    background: var(--background-secondary);
    border: 0;
    border-radius: 10px;
  }

  .site-nav__search::placeholder {
    color: var(--ink-secondary);
  }

  .site-nav__search:focus {
    outline: none;
    box-shadow: inset 0 0 0 1px var(--fill-primary);
  }

  /* Retint the native clear button (WebKit only) so it tracks the theme instead
   of the browser's hardcoded grey. Firefox shows none; Esc still clears. */
  .site-nav__search::-webkit-search-cancel-button {
    -webkit-appearance: none;
    appearance: none;
    width: 28px;
    height: 28px;
    background: var(--ink-secondary);
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 6l-12 12'/%3E%3Cpath d='M6 6l12 12'/%3E%3C/svg%3E")
      right / 16px no-repeat;
    cursor: pointer;
  }

  .site-nav__search::-webkit-search-cancel-button:hover {
    background: var(--ink-primary);
  }

  /* No stale clear button lingering on an unfocused field (matches Safari). */
  .site-nav__search:not(:focus)::-webkit-search-cancel-button {
    display: none;
  }

  /* Results dropdown: the same floating-surface recipe as .popover, concentric
   4px padding around 8px items. */
  .site-nav__results {
    position: absolute;
    inset-inline: 0;
    top: calc(100% + 4px);
    z-index: 30;
    max-height: 60vh;
    margin: 0;
    padding: 4px;
    overflow-y: auto;
    list-style: none;
    background: var(--background-secondary);
    border-radius: calc(var(--radius-sm) + 4px);
    box-shadow: var(--shadow-sm), var(--surface-rim);
  }

  .site-nav__results[hidden] {
    display: none;
  }

  .site-nav__result {
    padding: 0.375rem 0.5rem;
    font-size: var(--font-caption-medium-size);
    color: var(--ink-primary);
    border-radius: var(--radius-sm);
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* Single highlight: hover moves .is-active (wired in JS) rather than adding a
   separate :hover state, so the keyboard default and the cursor never both show. */
  .site-nav__result.is-active {
    background: var(--background-secondary-hover);
  }

  .site-nav__result--empty {
    color: var(--ink-secondary);
    cursor: default;
  }

  @media (min-width: 768px) {
    .site-nav__search-wrap,
    .site-nav__search-wrap + .site-nav__divider {
      display: block;
    }
  }

  /* Show the icon for the theme you'd switch TO: sun in dark, moon in light. */
  :root[data-theme="light"] .icon-sun,
  :root[data-theme="dark"] .icon-moon {
    display: none;
  }

  .wrap {
    max-width: calc(40rem + 2 * var(--grid-margin, 1.5rem));
    margin-inline: auto;
    padding: 3rem var(--grid-margin, 1.5rem) 6rem;
  }

  /* Wrap gets focused (tabindex=-1) via the skip link, kill the default focus ring. */
  .wrap:focus-visible {
    outline: none;
  }

  /* Shell: full width; the sidebar carries its own left gutter, and .wrap carries
   its own side gutter (grid-margin). No shell padding, so nothing double-stacks. */
  .shell {
    display: flex;
    align-items: flex-start;
  }

  .content-region {
    flex: 1 1 auto;
    min-width: 0;
  }

  /* Base = mobile: stacked column. Copy-tokens button is hidden until md
   (rule sits after .btn below to keep specificity ascending). */
  .site-head {
    margin-bottom: 2rem;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1rem;
  }

  /* Divider only when the page has a subhead below. Pages without subheads
   (e.g. Typography) go straight from lede into content, no floating rule. */
  body:has(.subhead) .site-head {
    margin-bottom: 1.5rem;
    padding-bottom: 1.5rem;
    border-bottom: var(--border-width) solid var(--fill-tertiary);
  }

  .site-head__body {
    flex: 1;
    min-width: 0;
  }

  /* Title + Copy Tokens on the same row, above the lede. h1's own margin-bottom
   provides the gap to the lede. */
  .site-head__title {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
  }

  /* sm ≥640: header returns to a row (title left, actions right). */
  @media (min-width: 640px) {
    .site-head {
      flex-direction: row;
      gap: 1.5rem;
      justify-content: space-between;
    }
  }

  h1 {
    font-size: 1.875rem; /* 30px */
    line-height: 2.25rem; /* 36px */
    font-weight: 600;
    letter-spacing: -0.02em;
    margin: 0 0 0.5rem;
  }

  .lede {
    color: var(--ink-primary);
    font-size: var(--font-body-small-size);
    margin: 0;
  }

  @media (min-width: 640px) {
    .lede {
      text-wrap: balance;
    }
  }

  @media (min-width: 768px) {
    .lede {
      max-width: 80%;
    }
  }

  .toolbar {
    margin-top: 1.5rem;
  }

  .btn {
    display: inline-flex;
    align-items: center;
    height: 32px;
    padding: 0 0.625rem;
    font: inherit;
    font-size: var(--font-caption-medium-size);
    font-weight: 500;
    color: var(--ink-primary);
    text-decoration: none;
    background: var(--background-secondary);
    border: 0;
    border-radius: 10px;
    cursor: pointer;
    transition:
      background-color 0.15s ease,
      transform 0.1s ease;
  }

  @media (hover: hover) {
    .btn:hover {
      background: var(--background-secondary-hover);
    }
  }

  .btn:active {
    background: var(--background-secondary-active);
    transform: scale(0.97);
  }

  /* Copy-tokens button hidden by default; reappears at md ≥768 (hidden on phones
   + small tablets). Ordered after .btn so specificity ascends, and the md query
   sits after this base rule so the show wins. */
  .site-head__title .btn {
    display: none;
  }

  @media (min-width: 768px) {
    .site-head__title .btn {
      display: inline-flex;
    }
  }

  /* Outlined variant: transparent surface with a hairline stroke; hover fills
   with the same subtle wash the solid .btn rests on. */
  .btn--outline {
    background: transparent;
    box-shadow: inset 0 0 0 1px var(--fill-secondary);
  }

  @media (hover: hover) {
    .btn--outline:hover {
      background: var(--background-secondary-hover);
    }
  }

  .btn--outline:active {
    background: var(--background-secondary-active);
  }

  section + section {
    margin-top: 3.5rem;
    padding-top: 3.5rem;
    border-top: var(--border-width) solid var(--fill-tertiary);
  }

  h2 {
    font-size: var(--font-body-large-size);
    font-weight: 600;
    letter-spacing: -0.01em;
    margin: 0 0 0.75rem;
  }

  .section-note {
    color: var(--ink-primary);
    font-size: 0.9375rem;
    max-width: var(--measure);
    margin: 0.25rem 0 0;
  }

  /* Inline text links in prose: medium-weight ink text (same when visited), an
   ink-tertiary underline that lifts to ink-primary on hover. */
  .section-note a,
  .lede a {
    color: var(--ink-primary);
    font-weight: 500;
    text-decoration: underline;
    text-decoration-color: var(--ink-tertiary);
    text-underline-offset: 2px;

    /* Rounds the focus ring a touch. */
    border-radius: 4px;
    transition: text-decoration-color var(--motion-default);
  }

  @media (hover: hover) {
    .section-note a:where(:hover),
    .lede a:where(:hover) {
      text-decoration-color: var(--ink-primary);
    }
  }

  /* Inline + block code samples, uses --fill-quaternary alpha tint so the chip
   contrasts against any surface it's placed on (whether that's the tinted page
   or an elevated card). */
  code {
    font-family: var(--mono);
    font-size: 0.8125rem;
    padding: 0.1rem 0.3rem;
    background: var(--fill-quaternary);
    border: 0;
    border-radius: 4px;
  }

  .code-block {
    position: relative;
    margin: 0 0 1rem;
  }

  /* In a component section the grid gap handles rhythm, so the code card's own
   bottom margin would just be dead space below the surface. */
  .preview-block .code-block {
    margin-bottom: 0;
  }

  pre {
    margin: 0;
    padding: 0.875rem 0;
    background: var(--background-secondary);
    border: 0;
    border-radius: var(--radius-md);
    overflow-x: auto;
    scrollbar-width: none;
  }

  /* Chrome makes horizontally-scrollable <pre> keyboard-focusable (to scroll it);
   the shared :where ring already covers it. */
  pre::-webkit-scrollbar {
    display: none;
  }

  /* Copy button auto-injected by app.js on every <pre>. Sits on the same
   surface as the code block so the icon reads as a floating affordance
   rather than a distinct pill. */
  .code-copy {
    position: absolute;
    top: 10px;
    inset-inline-end: 10px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    color: var(--ink-secondary);
    background: var(--background-secondary);
    border: 0;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition:
      color var(--motion-default),
      background var(--motion-default);
  }

  @media (hover: hover) {
    .code-copy:hover {
      color: var(--ink-primary);
      background: var(--background-secondary-hover);
    }
  }

  .code-copy svg {
    width: 16px;
    height: 16px;
  }

  .code-copy .code-copy__check {
    display: none;
  }

  .code-copy.is-copied .code-copy__copy {
    display: none;
  }

  .code-copy.is-copied .code-copy__check {
    display: block;
  }

  /* Collapsible code. Over 4 lines starts collapsed to a short
   peek under a full-surface fade with a "View Code" pill; opening turns it into
   a taller scroll box so it reads as "there's more". */

  /* Opened: a ~10.5-line scroll box, scrollbar hidden. */
  .code-block--collapsible:not(.is-collapsed) pre {
    max-height: 14.5rem;
    overflow-y: auto;
    scrollbar-width: none;
  }

  .code-block--collapsible:not(.is-collapsed) pre::-webkit-scrollbar {
    display: none;
  }

  /* Closed peek is exactly the first 3 lines, see the .line:nth-child rule
   below, which hides the rest so the pre's own 14px bottom padding breathes
   under line 3 with no clipped sliver of line 4. */
  .code-block--collapsible.is-collapsed pre {
    overflow-y: hidden;
  }

  /* Copy affordance appears only once the code is revealed. */
  .code-block--collapsible.is-collapsed .code-copy {
    display: none;
  }

  /* Fade covers the whole peek (surface at the bottom → transparent by 60% up),
   with the pill resting at the bottom. Only present while collapsed. */
  .code-block__toggle {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(to top, var(--background-secondary), transparent);
    border-radius: var(--radius-md);

    /* Only the pill is interactive, the fade area passes clicks through. */
    pointer-events: none;
  }

  /* The pill reuses .btn.btn--outline (the header Example button) for sizing and
   the hairline stroke, but takes a solid surface by default so code never shows
   through it against the fade. */
  .code-block__toggle-pill {
    background: var(--background-primary);
    pointer-events: auto;
  }

  pre code {
    padding: 0;
    background: none;
    border: 0;
    font-size: 0.8125rem;
    line-height: 1.6;
  }

  /* Syntax tokens for the code samples, mapped to our palette (GitHub-flavoured hue
   families, but on-system and centrally contrast-controlled). Each --token is themed
   per color-scheme, so one rule covers both; the light-dark() fallback keeps colors
   right if a page ever loads base.css without the tokens. All clear AA on the code
   surface in both themes (light 5.4–7.0, dark 6.5–7.1). key=red, prop=amber,
   selector=green, fn=fuchsia, str/var=blue (both blue-family, as before). */
  pre .tok-key {
    color: var(--red, light-dark(#bf0000, #ff9696));
  }

  pre .tok-selector {
    color: var(--green, light-dark(#007a16, #54cb99));
  }

  pre .tok-prop {
    color: var(--amber, light-dark(#aa5100, #eaaa50));
  }

  pre .tok-fn {
    color: var(--fuchsia, light-dark(#9827a0, #eb9def));
  }

  pre .tok-str {
    color: var(--blue, light-dark(#1950c8, #8cbeff));
  }

  pre .tok-var {
    color: var(--blue, light-dark(#1950c8, #8cbeff));
  }

  /* Line numbers. <code> is a grid so each .line is its own row; the "\n"s kept
   between lines (for clean copy) are whitespace-only and dropped by grid, so no
   blank rows. The number is a counter-generated ::before in a fixed column, and
   a per-line background (future highlight) can span the whole row behind it. */
  .code-block--numbered code {
    display: grid;
    counter-reset: line;
  }

  .code-block--numbered .line {
    min-height: 1lh;
    counter-increment: line;
  }

  /* The number is an in-flow sticky cell (with the surface as its background) so
   it stays pinned to the left while the code scrolls horizontally under it. */
  .code-block--numbered .line::before {
    content: counter(line);
    position: sticky;
    left: 0;
    display: inline-block;
    width: 2.5rem;
    padding-inline-end: 1.5rem;
    background: var(--background-secondary);
    color: var(--ink-tertiary);
    text-align: right;
    user-select: none;
  }

  /* Closed peek: keep only the first 3 lines (hidden, not clipped, so the pre's
   bottom padding still shows under line 3). Placed here for specificity order. */
  .code-block--collapsible.is-collapsed .line:nth-child(n + 4) {
    display: none;
  }

  /* Palette table, one card per color family, rows for base tokens, columns
   for states (default / hover / active). Content-driven min-width triggers
   horizontal scroll on narrow viewports instead of squeezing swatches. */
  .palette-table {
    background: var(--background-secondary);
    border: var(--border-width) solid var(--fill-tertiary);
    border-radius: var(--radius-md);
    overflow: hidden;
    padding-right: 4px;
  }

  .palette-table__scroll {
    overflow-x: auto;
  }

  .palette-table table {
    width: 100%;
    min-width: 480px;

    /* Separate (not collapse) so the row dividers render through the normal
     box-border path. iOS/mobile WebKit snaps collapsed table borders to whole
     device pixels and skips the sub-pixel AA it gives block borders, so the same
     --fill-tertiary hairline looks harder here than on the div-based token/type
     lists. border-spacing:0 keeps the cell tops touching into one continuous line. */
    border-collapse: separate;
    border-spacing: 0;
  }

  .palette-table thead th {
    height: 48px;
    padding: 0 1rem;
    font-size: var(--font-caption-medium-size);
    font-weight: 500;
    line-height: 20px;
    text-align: start;
  }

  .palette-table__state {
    width: 72px;
    padding-inline: 0.5rem !important;
    text-align: center !important;
  }

  .palette-table tbody th,
  .palette-table tbody td {
    height: 72px;
    padding: 0 1rem;
    font-size: var(--font-caption-medium-size);
    vertical-align: middle;
    border-top: var(--border-width) solid var(--fill-tertiary);
  }

  .palette-table tbody th[scope="row"] {
    padding-inline-end: 0.75rem;
    font-weight: 400;
    text-align: start;
  }

  .palette-table tbody td.palette-table__state {
    text-align: center;
  }

  .palette-swatch {
    display: inline-block;
    width: 64px;
    height: 48px;
    padding: 0;
    border: var(--border-width) solid var(--fill-quaternary);
    border-radius: var(--radius-sm);
    background: var(--c);
    cursor: pointer;
    vertical-align: middle;
  }

  /* Section subhead, vertical rhythm builds upward: only push down against
   what the heading introduces, and rely on the next element's margin-top for
   the gap below (so removing a description doesn't collapse the layout). */
  .subhead {
    font-size: var(--font-body-large-size);
    font-weight: 600;
    margin: 1.75rem 0 0;
  }

  .subhead + :not(.section-note),
  .section-note + * {
    margin-top: 1rem;
  }

  /* Type role card, one bordered card per tier, rows separated by dividers. */
  .type-list {
    list-style: none;
    padding: 0;
    margin: 0;
    background: var(--background-secondary);
    border: var(--border-width) solid var(--fill-tertiary);
    border-radius: var(--radius-md);
    overflow: hidden;
  }

  /* Restore the 16px above the type card that .type-list's margin:0 wipes. */
  .section-note + .type-list {
    margin-top: 1rem;
  }

  .type-list > li + li {
    border-top: var(--border-width) solid var(--fill-tertiary);
  }

  .type-row {
    display: block;
    padding: 1rem;
  }

  .type-row__sample {
    display: block;
    margin-bottom: 0.4rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .type-row__meta {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    font-size: 0.8125rem;
    color: var(--ink-secondary);
  }

  .type-row__name {
    color: var(--ink-primary);
  }

  /* Hydration leaves lh/weight empty (spec is compacted into the size slot), so
   suppress the empty spans instead of letting them consume space-between. */
  .type-row__meta [data-spec]:empty {
    display: none;
  }

  /* Role classes, reference the type tokens directly */
  .t-headline-large {
    font-size: var(--font-headline-large-size);
    line-height: var(--font-headline-large-lh);
    font-weight: var(--font-headline-large-weight);
    letter-spacing: -0.02em;
  }

  .t-headline-medium {
    font-size: var(--font-headline-medium-size);
    line-height: var(--font-headline-medium-lh);
    font-weight: var(--font-headline-medium-weight);
    letter-spacing: -0.02em;
  }

  .t-headline-small {
    font-size: var(--font-headline-small-size);
    line-height: var(--font-headline-small-lh);
    font-weight: var(--font-headline-small-weight);
    letter-spacing: -0.01em;
  }

  .t-title-large {
    font-size: var(--font-title-large-size);
    line-height: var(--font-title-large-lh);
    font-weight: var(--font-title-large-weight);
  }

  .t-title-medium {
    font-size: var(--font-title-medium-size);
    line-height: var(--font-title-medium-lh);
    font-weight: var(--font-title-medium-weight);
  }

  .t-title-small {
    font-size: var(--font-title-small-size);
    line-height: var(--font-title-small-lh);
    font-weight: var(--font-title-small-weight);
  }

  .t-body-large {
    font-size: var(--font-body-large-size);
    line-height: var(--font-body-large-lh);
    font-weight: var(--font-body-large-weight);
  }

  .t-body-large-emphasized {
    font-weight: var(--font-body-large-emphasized-weight);
  }

  .t-body-medium {
    font-size: var(--font-body-medium-size);
    line-height: var(--font-body-medium-lh);
    font-weight: var(--font-body-medium-weight);
  }

  .t-body-medium-emphasized {
    font-weight: var(--font-body-medium-emphasized-weight);
  }

  .t-body-small {
    font-size: var(--font-body-small-size);
    line-height: var(--font-body-small-lh);
    font-weight: var(--font-body-small-weight);
  }

  .t-body-small-emphasized {
    font-weight: var(--font-body-small-emphasized-weight);
  }

  .t-caption-medium {
    font-size: var(--font-caption-medium-size);
    line-height: var(--font-caption-medium-lh);
    font-weight: var(--font-caption-medium-weight);
  }

  .t-caption-medium-emphasized {
    font-weight: var(--font-caption-medium-emphasized-weight);
  }

  .t-caption-small {
    font-size: var(--font-caption-small-size);
    line-height: var(--font-caption-small-lh);
    font-weight: var(--font-caption-small-weight);
  }

  .t-caption-small-emphasized {
    font-weight: var(--font-caption-small-emphasized-weight);
  }

  /* Prev/next docs pager: a 64px row with 24px gap above. Distinct
   from the Pagination component (.pagination); this is the between-pages nav. */
  .pager {
    display: flex;
    align-items: center;
    gap: 1rem;
    height: 64px;
    margin-top: 1.5rem;
  }

  .pager__link {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    height: 32px;
    padding: 0 0.625rem;
    color: var(--ink-primary);
    font-size: var(--font-caption-medium-size);
    font-weight: 500;
    text-decoration: none;
    background: var(--background-secondary);
    border-radius: 10px;
    transition:
      background-color 0.15s ease,
      transform 0.1s ease;
  }

  @media (hover: hover) {
    .pager__link:hover {
      background: var(--background-secondary-hover);
    }
  }

  .pager__link:active {
    background: var(--background-secondary-active);
    transform: scale(0.97);
  }

  .pager__link--next {
    margin-inline-start: auto;
  }

  /* Switch, pill track + circle thumb. Uses role="switch" + aria-checked. */
  .docs-switch {
    display: inline-flex;
    align-items: center;
    gap: 0.625rem;
    cursor: pointer;
  }

  .docs-switch__toggle {
    position: relative;
    width: 36px;
    height: 20px;
    padding: 0;
    background: var(--fill-secondary);
    border: 0;
    border-radius: var(--radius-full);
    cursor: pointer;
    transition: background-color 0.15s ease;
  }

  .docs-switch__toggle::before {
    content: "";
    position: absolute;
    top: 2px;
    inset-inline-start: 2px;
    width: 16px;
    height: 16px;
    background: var(--background-secondary);
    border-radius: 50%;
    transition: transform 0.15s ease;
  }

  .docs-switch__toggle[aria-checked="true"] {
    background: var(--primary);
  }

  .docs-switch__toggle[aria-checked="true"]::before {
    transform: translateX(calc(16px * var(--flip)));
  }

  .docs-switch__label {
    font-size: var(--font-caption-medium-size);
    color: var(--ink-primary);
  }

  /* Underlined tabs. Active tab has a text-coloured bottom line
   that sits over the tablist's bottom border. */
  .docs-tabs {
    display: flex;
    gap: 1.5rem;
    margin-bottom: 1rem;
  }

  /* Code-tab row sits directly under the intro, so it needs more breathing room
   down to the first example than the inline grid tabs do. */
  .docs-tabs[data-code-tabs] {
    margin-bottom: 2rem;
  }

  .docs-tabs__tab {
    position: relative;
    padding: 0.375rem 0;
    color: var(--ink-secondary);
    background: none;
    border: 0;
    font: inherit;
    font-size: var(--font-body-small-size);
    font-weight: 500;
    cursor: pointer;
  }

  @media (hover: hover) {
    .docs-tabs__tab:hover {
      color: var(--ink-primary);
    }
  }

  .docs-tabs__tab:focus-visible {
    outline: var(--focus-ring-width) solid var(--focus);
    outline-offset: var(--focus-ring-offset);
    border-radius: 4px;
  }

  .docs-tabs__tab[aria-selected="true"] {
    color: var(--ink-primary);
  }

  .docs-tabs__tab[aria-selected="true"]::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 1px;
    height: 2px;
    background: var(--ink-primary);
  }

  /* Link list: 3-column grid on desktop, 2 on mobile. Each <a>
   fills its column width so the click target spans the cell horizontally;
   vertical rhythm comes from the grid row-gap, not per-link padding. */
  .link-list {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.25rem 2rem;
    margin: 0;
    padding: 0;
    list-style: none;
  }

  @media (min-width: 640px) {
    .link-list {
      grid-template-columns: repeat(3, minmax(0, 1fr));
    }
  }

  .link-list a {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    justify-content: flex-start;
    color: var(--ink-primary);
    font-size: var(--font-body-small-size);
    font-weight: 500;
    text-decoration: none;
  }

  .link-list a:focus-visible {
    outline: var(--focus-ring-width) solid var(--focus);
    outline-offset: var(--focus-ring-offset);
    border-radius: var(--radius-sm);
  }

  /* Two-part chevron: horizontal stem fades in, ">" tip slides right. Same
   easing as the UngSex button arrow it's borrowed from. */
  .link-arrow {
    width: 12px;
    height: 12px;
    overflow: visible;
    flex-shrink: 0;
  }

  .link-arrow__line {
    opacity: 0;
    transition: opacity 0.3s cubic-bezier(0.25, 1, 0.5, 1);
  }

  .link-arrow__chevron {
    transition: translate 0.3s cubic-bezier(0.25, 1, 0.5, 1);
  }

  @media (hover: hover) {
    .link-list a:hover .link-arrow__line {
      opacity: 1;
    }

    .link-list a:hover .link-arrow__chevron {
      translate: 3px 0;
    }
  }

  /* Single-card token list, one bordered box, rows separated by dividers.
   Used on the Layout page for both Grid values and the Spacing scale. */
  .token-list {
    background: var(--background-secondary);
    border: var(--border-width) solid var(--fill-tertiary);
    border-radius: var(--radius-md);
    overflow: hidden;
  }

  .token-list__row {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 0.5rem 0.75rem 0.5rem 1rem;
    min-height: 48px;
  }

  .token-list__row + .token-list__row {
    border-top: var(--border-width) solid var(--fill-tertiary);
  }

  .token-list__name {
    font-size: var(--font-caption-medium-size);
    white-space: nowrap;
  }

  .token-list__bar {
    flex: none;
    height: 8px;
    background: var(--ink-primary);
    border-radius: 4px;
  }

  .token-list__value {
    margin-inline-start: auto;
    font-size: var(--font-caption-medium-size);
  }

  /* Linked rows (used on the intro to jump into each section). Chevron appended
   via a mask so we don't repeat SVG markup on every row. */

  /* Base = mobile: the value drops under the label (left-aligned) and the
   chevron is pinned to the vertical centre. Returns to a single row at sm. */
  a.token-list__row {
    position: relative;
    flex-wrap: wrap;
    row-gap: 0;
    padding: 0.75rem 2.5rem 0.75rem 1rem;
    color: inherit;
    text-decoration: none;
  }

  a.token-list__row .token-list__name {
    flex-basis: 100%;
    font-weight: 500;
  }

  a.token-list__row .token-list__value {
    margin-inline-start: 0;
    color: var(--ink-secondary);
    text-align: start;
  }

  a.token-list__row::after {
    content: "";
    position: absolute;
    top: 50%;
    inset-inline-end: 12px;
    flex: none;
    width: 16px;
    height: 16px;
    background: currentcolor;
    color: var(--ink-tertiary);
    mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='m9 18 6-6-6-6'/></svg>")
      no-repeat center / 16px;
    transform: translateY(-50%);
    transition: color var(--motion-default);
  }

  @media (hover: hover) {
    a.token-list__row:hover::after {
      color: var(--ink-primary);
    }
  }

  /* Full-width rows: ring sits just inside so it never collides with the
   container edge. No converge, a 6px→-2px sweep would fly in from outside. */
  a.token-list__row:focus-visible {
    outline: var(--focus-ring-width) solid var(--focus);
    outline-offset: -2px;
    animation: none;
  }

  /* sm ≥640: label + value share one row; chevron returns to the flex flow. */
  @media (min-width: 640px) {
    a.token-list__row {
      flex-wrap: nowrap;
      padding: 0.5rem 0.75rem 0.5rem 1rem;
    }

    a.token-list__row .token-list__name {
      flex-basis: auto;
    }

    a.token-list__row .token-list__value {
      margin-inline-start: auto;
    }

    a.token-list__row::after {
      position: static;
      transform: none;
    }

    /* What pushes the chevron to the end of the row is the value's auto margin,
       so a row without a value has nothing pushing it and the chevron lands
       against the label. Those rows take the auto margin themselves. */
    a.token-list__row:not(:has(.token-list__value))::after {
      margin-inline-start: auto;
    }
  }

  /* Flush variant, drops the box so each linked row is its own action-row-style
   surface: resting on --background-secondary with card hover/active. Used on
   the project landing. */
  .token-list--flush {
    display: flex;
    flex-direction: column;
    gap: 8px;
    background: transparent;
    border: 0;
    border-radius: 0;
    overflow: visible;
  }

  .token-list--flush .token-list__row + .token-list__row {
    border-top: 0;
  }

  .token-list--flush a.token-list__row {
    min-height: 0;
    padding: 12px 16px;
    background: var(--background-secondary);
    border-radius: var(--radius-md);
    transition: background var(--motion-default);
  }

  .token-list--flush a.token-list__row .token-list__name {
    font-size: var(--font-body-small-size);
    font-weight: 500;
  }

  .token-list--flush a.token-list__row::after {
    width: 20px;
    height: 20px;
    mask-size: 20px;
  }

  @media (hover: hover) {
    .token-list--flush a.token-list__row:hover {
      background: var(--background-secondary-hover);
    }
  }

  .token-list--flush a.token-list__row:active {
    background: var(--background-secondary-active);
  }

  /* Only the surface reacts to hover, keep the chevron steady. */
  @media (hover: hover) {
    .token-list--flush a.token-list__row:hover::after {
      color: var(--ink-tertiary);
    }
  }

  /* These rows are spaced cards, not edge-to-edge, so they take the standard
   outset ring + converge instead of the inset treatment above. */
  .token-list--flush a.token-list__row:focus-visible {
    outline-offset: var(--focus-ring-offset);
    animation: focus-ring-converge var(--motion-default);
  }

  /* Preview blocks, heading + description above, boxed demo + token below. */
  .preview-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 3rem;
  }

  /* Tighter rhythm, used on the intro where sections are short link lists. */
  .preview-grid--tight {
    gap: 2rem;
  }

  .preview-block {
    display: flex;
    flex-direction: column;
    min-width: 0;
  }

  /* display:flex above would otherwise beat the [hidden] attribute, so a
   tab-toggled section (data-lang) wouldn't actually hide. */
  .preview-block[hidden] {
    display: none;
  }

  .preview-block > .subhead {
    margin-top: 0;
  }

  /* Adjacent generic <section>s draw a divider; preview-blocks flow together instead. */
  .preview-block + .preview-block {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
  }

  .preview-card {
    position: relative;
    display: flex;
    flex-direction: column;
    min-width: 0;
    overflow: hidden;
    background: var(--background-secondary);
    border: var(--border-width) solid var(--fill-tertiary);
    border-radius: var(--radius-md);
  }

  .preview-card__demo {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 220px;
    padding: 2rem;
    background: var(--background-primary);
  }

  /* Component demo container, a "stage" that isolates the live example on the
   project's elevated surface (--background-secondary). Components that need
   to match this surface (Tabs' active pill, Switch's knob) reference it too. */
  .component-preview {
    display: flex;
    align-items: center;
    align-content: center;
    justify-content: center;
    min-height: calc(200px + 5rem);
    padding: 2.5rem;
    background: var(--background-secondary);
    border: var(--border-width) solid var(--fill-tertiary);
    border-radius: var(--radius-md);
  }

  /* Cap any single fixed-width demo (e.g. the 280px combobox/select) to the
   stage so it shrinks to fit on narrow screens instead of overflowing and
   reading as left-aligned. */
  .component-preview > * {
    max-width: 100%;
  }

  /* The calendar is a fixed grid, not a fluid-width demo: keep its size and let
   the example card mask any overflow on narrow screens instead of squashing it. */
  .component-preview > .calendar {
    max-width: none;
  }

  /* Example card: the live preview on top, its code panel below,
   both inside one border. Language switches via the page's HTML/React tabs. */
  .example {
    overflow: hidden;
    border: var(--border-width) solid var(--fill-tertiary);
    border-radius: var(--radius-md);
  }

  .example .component-preview {
    border: 0;
    border-radius: 0;
  }

  .example__code {
    border-top: var(--border-width) solid var(--fill-tertiary);
  }

  .example__code .code-block {
    margin: 0;
  }

  .example .example__code pre {
    border-radius: 0;
  }

  .preview-card__chip {
    display: block;
    width: 100%;
    max-width: 196px;
    height: 128px;
    background: var(--background-secondary);
    border-radius: 12px;
  }

  /* Shadow demo keeps a subtler surface so the shadow reads against the card. */
  .preview-card__chip[data-shadow-demo] {
    background: var(--background-secondary);
  }

  /* Radius demo, subtle surface + alpha ring.
   Uses --fill-tertiary for the ring so the alpha overlay reads correctly on
   the demo's own surface across both themes, no per-theme override needed. */
  .preview-card__chip[data-radius-demo] {
    background: var(--background-secondary);
    border: 0;
    box-shadow: 0 0 0 1px var(--fill-tertiary);
  }

  .preview-card__meta {
    position: relative;
    padding: 0.875rem 1rem;
    border-top: var(--border-width) solid var(--fill-tertiary);
  }

  .preview-card__meta-text {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    min-width: 0;
  }

  .preview-card__name {
    position: absolute;
    inset-block-start: 1rem;
    inset-inline-start: 1rem;
    font-size: var(--font-caption-medium-size);
    font-weight: 400;
    white-space: nowrap;
  }

  .preview-card__value {
    font-size: var(--font-caption-medium-size);
    min-width: 0;
    overflow-wrap: anywhere;
  }

  /* Motion demo, small horizontal track with a dot that slides across on card
   hover, using the token duration + easing so you can see each token in action. */
  .motion-demo {
    --motion-demo-transition: var(--motion-default);

    position: relative;
    width: 100%;
    max-width: 160px;
    height: 24px;
    background: var(--background-secondary);
    border-radius: var(--radius-full);
  }

  .motion-demo::after {
    content: "";
    position: absolute;
    top: 4px;
    bottom: 4px;
    left: 4px;
    width: 16px;
    background: var(--ink-primary);
    border-radius: var(--radius-full);
    transition: left var(--motion-demo-transition);
  }

  .preview-card:hover .motion-demo::after {
    left: calc(100% - 20px);
  }

  /* Ease-in / ease-out demos use a longer duration so the curve shape is legible. */
  .motion-demo[data-motion="ease-in"] {
    --motion-demo-transition: 400ms var(--motion-ease-in);
  }

  .motion-demo[data-motion="ease-out"] {
    --motion-demo-transition: 400ms var(--motion-ease-out);
  }

  /* Grid overlay, pinned over the content column so its columns line up with the page.
   The overlay box spans the content region; the inner mirrors .wrap (capped + centered). */
  .grid-overlay {
    --grid-overlay-color: #ef4444;

    position: fixed;
    inset: 0;
    z-index: 50;
    pointer-events: none;
  }

  .grid-overlay__inner {
    position: relative;
    height: 100%;

    /* content + the outer margins (32px each), so columns fill the 1200 content
     and the margin bands sit OUTSIDE it, matching "1200 content, 1264 w/ margins". */
    max-width: calc(var(--container-max, 1200px) + 2 * var(--grid-margin, 1.5rem));
    margin-inline: auto;
  }

  .grid-overlay[hidden] {
    display: none;
  }

  .grid-overlay__cols {
    position: absolute;
    inset: 0;
    padding-inline: var(--grid-margin, 0);
    display: grid;
    grid-template-columns: repeat(var(--grid-columns), 1fr);
    gap: var(--grid-gutter);
  }

  .grid-overlay__col {
    background: color-mix(in srgb, var(--grid-overlay-color) 16%, transparent);
    border-inline: var(--border-width) solid
      color-mix(in srgb, var(--grid-overlay-color) 30%, transparent);
  }

  /* Outer margin bands, with their measured value */
  .grid-overlay__margin {
    position: absolute;
    top: 0;
    bottom: 0;
    width: var(--grid-margin, 0);
    display: flex;
    align-items: center;
    justify-content: center;
    background: color-mix(in srgb, var(--grid-overlay-color) 7%, transparent);
  }

  .grid-overlay__margin--left {
    left: 0;
    border-left: 1px dashed color-mix(in srgb, var(--grid-overlay-color) 40%, transparent);
  }

  .grid-overlay__margin--right {
    right: 0;
    border-right: 1px dashed color-mix(in srgb, var(--grid-overlay-color) 40%, transparent);
  }

  .grid-overlay__margin-label {
    font-family: var(--mono);
    font-size: var(--font-caption-small-size);
    white-space: nowrap;
    color: var(--grid-overlay-color);
    transform: rotate(-90deg);
  }

  /* Toast (docs chrome only).
   The component itself lives in components.css, which is NOT loaded site-wide;
   pages like /demo/colors still need a pill for the "Copied CSS" confirmation,
   so the plain, untyped part of it is repeated here. Only what chrome uses:
   no semantic types and no promise spinner, both of which are demoed on pages
   that do load components.css (where that copy wins, being later in the same
   layer). Keep the shared declarations in step with the Toast section there. */
  .toast {
    position: fixed;
    left: 50%;
    bottom: 1.5rem;
    transform: translateX(-50%) translateY(1rem);
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;

    /* % (not 100vw) resolves against the viewport minus any scrollbar, so a wide
     toast can't itself trigger a horizontal scrollbar. */
    max-width: calc(100% - 2rem);
    padding: 0.75rem 1.25rem;
    background: var(--background-secondary);
    color: var(--ink-primary);
    border-radius: var(--radius-full);
    box-shadow: var(--shadow-lg), var(--surface-rim);
    font-size: var(--font-caption-medium-size);
    font-weight: 500;
    opacity: 0;
    pointer-events: none;
    transition:
      opacity 0.18s ease,
      transform 0.18s ease;
    z-index: var(--z-toast);
  }

  .toast.is-visible {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
  }

  /* The icon span is always rendered; chrome never fills it, so collapse it or
   it opens a gap in front of the text. */
  .toast__icon:empty {
    display: none;
  }

  /* ── Preference fallbacks ────────────────────────────────────────────────────
   Each block only activates on a matching OS/user setting, so the default look
   is untouched. */

  /* Reduced transparency: drop the drawer's blur + translucency for an opaque
   surface. Only the base drawer has blur; the lg rail is already opaque, so this
   needs no width bound. */
  @media (prefers-reduced-transparency: reduce) {
    .sidebar {
      background: var(--background-primary);
      -webkit-backdrop-filter: none;
      backdrop-filter: none;
    }
  }

  /* More contrast: firm up the hairline dividers/borders (normally a faint
   --fill-tertiary) to an ink tone so they're clearly visible. */
  @media (prefers-contrast: more) {
    .palette-table,
    .type-list,
    .token-list,
    .preview-card,
    .component-preview,
    .site-head,
    section + section,
    .type-list > li + li,
    .token-list__row + .token-list__row,
    .preview-card__meta {
      border-color: var(--ink-tertiary);
    }
  }

  /* Forced colors (Windows High Contrast) for the site chrome. Only active in
   that mode; uses system-color keywords and never touches the token pipeline,
   so neutral retinting is unaffected. (Component library has its own block.) */
  @media (forced-colors: active) {
    /* Standalone code snippets lose their surface background; a CanvasText border
     keeps each block delineated. Code inside a preview .example is already bounded
     by that box (its own border + the .example__code divider), so exclude it to
     avoid a double border. */
    pre {
      border: var(--border-width) solid CanvasText;
    }

    .example pre {
      border: 0;
    }

    /* Toast pops up on its own surface, which the forced palette strips; a
     CanvasText border keeps it a visible, delineated notification. The typed
     icon and spinner overrides live with the component in components.css. */
    .toast {
      border: var(--border-width) solid CanvasText;
    }

    .docs-switch__toggle {
      border: var(--border-width) solid ButtonBorder;
    }

    .docs-switch__toggle[aria-checked="true"] {
      forced-color-adjust: none;
      background: Highlight;
    }

    /* Recentre the knob: the added 1px track border shrinks the inner box, so the
     original 2px inset sits it low (dark gap under it). 1px keeps it centred. */
    .docs-switch__toggle::before {
      top: 1px;
      inset-inline-start: 1px;
      background: ButtonText;
    }

    .docs-switch__toggle[aria-checked="true"]::before {
      background: HighlightText;
    }

    /* Swatches convey meaning via background alone; opt out of the forced palette
     so the real colors still render, with a border so the grid keeps its cells. */
    .palette-swatch {
      forced-color-adjust: none;
      border: var(--border-width) solid ButtonBorder;
    }

    /* Spacing bars show a length via their width but paint it with a background
     fill that's stripped; use a solid CanvasText bar so the length still reads. */
    .token-list__bar {
      background: CanvasText;
    }

    /* Landing picker rows are link-cards on a plain background fill (no border), so
     they vanish; outline each with its link color so the selectable row is
     bounded and reads as interactive. */
    .token-list--flush a.token-list__row {
      border: var(--border-width) solid currentcolor;
    }

    /* The row chevron sets its own faint --ink-tertiary color, which dims to an
     invisible GrayText; pin it to LinkText so it matches the row's link color. */
    .token-list--flush a.token-list__row::after {
      forced-color-adjust: none;
      color: LinkText;
    }

    /* The search field's painted box vanishes; outline it so the input is visible,
     and mark the active typeahead result as a system selection. */
    .site-nav__search {
      border: var(--border-width) solid ButtonBorder;
    }

    .site-nav__result[aria-selected="true"] {
      forced-color-adjust: none;
      background: Highlight;
      color: HighlightText;
    }

    /* The search clear (X) is a mask tinted faint --ink-secondary that dims in the
     forced palette; paint it CanvasText so the clear affordance stays visible. Its
     hover (normally --ink-primary) maps to an invisible color, so pin that too. */
    .site-nav__search::-webkit-search-cancel-button,
    .site-nav__search::-webkit-search-cancel-button:hover {
      background: CanvasText;
    }

    /* The typeahead dropdown floats on a shadow, stripped in high contrast; border
     it so the results panel stays a delineated surface. */
    .site-nav__results {
      border: var(--border-width) solid CanvasText;
    }

    /* Radius / shadow previews: don't force the brand surface (that fights the
     mode). Show a plain outlined box instead: the radius reads via the border's
     corners; shadow depth can't be shown in high contrast at all, so those tiles
     read uniform, which is honest for a mode with no depth. */
    .preview-card__chip[data-shadow-demo],
    .preview-card__chip[data-radius-demo] {
      border: var(--border-width) solid CanvasText;
    }

    /* Motion preview: bound the track and make the sliding handle a Highlight dot,
     like the slider. Use outline (not border) for the track: the handle is sized
     by top/bottom insets, so a border would shrink its box and make it oval. */
    .motion-demo {
      outline: var(--border-width) solid CanvasText;
      outline-offset: calc(-1 * var(--border-width));
    }

    .motion-demo::after {
      forced-color-adjust: none;
      background: Highlight;
    }

    /* Docs buttons (Copy CSS, Example, code-block copy) paint their own surface,
     which the forced palette strips; outline them so each stays a visible button.
     currentColor makes the border match the button's own text: a link-styled
     button like Example keeps its accent/LinkText edge, a plain button gets
     ButtonText, so the outline always agrees with the label. */
    .btn,
    .code-copy {
      border: var(--border-width) solid currentcolor;
    }

    /* Hover feedback: mark the hovered button's border as a system selection
     (the theme's accent, e.g. brown on Desert). */
    .btn:hover,
    .code-copy:hover {
      border-color: Highlight;
    }

    /* The active React/HTML tab is shown only by an underline bar (::after) whose
     background the forced palette strips; restate it as a system selection. */
    .docs-tabs__tab[aria-selected="true"]::after {
      background: Highlight;
    }
  }
}
