/* ╔══════════════════════════════════════════════════════════════════════╗
   ║  GALLERY CHROME — the documentation tool, not the design system        ║
   ║                                                                        ║
   ║  Everything here is prefixed `g-` and is deliberately NOT part of the  ║
   ║  system. Keeping it in its own file is half the answer to "not         ║
   ║  properly structured": in the old gallery, 100+ lines of tool chrome   ║
   ║  lived in the same <style> block as the specimens, so it was never     ║
   ║  obvious which rules were the product and which were the wrapper.      ║
   ║                                                                        ║
   ║  The shell reads the SELECTED WORLD from html[data-g-world] and dresses ║
   ║  itself to match, so browsing BACKSTAGE puts you in daylight and   ║
   ║  browsing the Stage puts you in the dark. You are always inside the    ║
   ║  world you are reading about.                                          ║
   ╚══════════════════════════════════════════════════════════════════════╝ */

:root {
  --g-shell:   #0a0a0a;
  --g-panel:   #131313;
  --g-line:    rgba(255, 255, 255, 0.12);
  --g-line-soft: rgba(255, 255, 255, 0.06);
  --g-ink:     rgba(255, 255, 255, 0.95);
  --g-ink-2:   rgba(255, 255, 255, 0.62);
  --g-ink-3:   rgba(255, 255, 255, 0.42);
  --g-accent:  #ffa33f;
  --g-rail:    248px;
  --g-head:    64px;
}
/* ONSTAGE · day mode — the chrome follows the world it is documenting. */
html[data-g-world="consumer"][data-g-theme="light"] {
  --g-shell:   #F4F2EE;
  --g-panel:   #FFFFFF;
  --g-line:    rgba(26, 20, 14, 0.14);
  --g-line-soft: rgba(26, 20, 14, 0.06);
  --g-ink:     rgba(26, 20, 14, 0.94);
  --g-ink-2:   rgba(26, 20, 14, 0.72);
  --g-ink-3:   rgba(26, 20, 14, 0.54);
  --g-accent:  #be600e;   /* --yellow-700: brand orange, but legible on paper */
}
html[data-g-world="studio"] {
  --g-shell:   #F0F0EE;
  --g-panel:   #FFFFFF;
  --g-line:    #E4E4E2;
  --g-line-soft: #EFEFED;
  --g-ink:     #1A1A1A;
  --g-ink-2:   #4D4D4D;
  --g-ink-3:   #6E6E68;
  --g-accent:  #8F4E00;   /* orange you may set as type — never --primary on light */
}
/* The Studio world has TWO themes, so the chrome must track the theme as well
   as the world. Missing this was a real break caught by the gallery's own
   audit: in Studio-dark the specimen text correctly went near-white while
   these panels stayed #FFFFFF, putting white type on white at 1.02:1. The
   chrome dresses itself in the same ladder the world uses — content darkest,
   shell one step lighter. */
html[data-g-world="studio"][data-g-theme="dark"] {
  --g-shell:   #0C0D0D;
  --g-panel:   #1C1D1E;
  --g-line:    #373839;
  --g-line-soft: #262728;
  --g-ink:     #FDFDFD;
  --g-ink-2:   #B9B9B9;
  --g-ink-3:   #979797;
  --g-accent:  #ffa33f;   /* on a dark ground the brand orange IS readable (8.49:1) */
}

html { scrollbar-gutter: stable; }
body.g-body {
  margin: 0;
  background: var(--g-shell);
  color: var(--g-ink);
  font-family: var(--font-cjk-text);
  font-weight: 400;
  /* Antialiasing is NOT set here. It is theme-aware policy owned by
     tokens.css — the gallery chrome inherits it like everything else, so the
     tool cannot drift from the system it documents. */
}

/* ── header ────────────────────────────────────────────────────────── */
.g-head {
  position: sticky; top: 0; z-index: 50;
  height: var(--g-head);
  display: flex; align-items: center; gap: 20px;
  padding: 0 24px;
  background: color-mix(in srgb, var(--g-shell) 88%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
          backdrop-filter: blur(14px) saturate(140%);
  border-bottom: 1px solid var(--g-line);
}
/* Brand lockup: mark + descriptor. The two are deliberately NOT the same size —
   a lockup reads as one object when the mark leads and the descriptor supports,
   and as two competing labels when they match. Baseline-aligned so the bump
   does not push the descriptor off the header's optical centre line. */
.g-head__mark {
  font-family: var(--font-cjk-display);
  font-weight: 700; letter-spacing: -0.01em;
  color: var(--g-ink); white-space: nowrap;
  display: inline-flex; align-items: baseline; gap: 8px;
  font-size: 14px;                 /* the descriptor */
}
/* The mark itself — the real SVG, painted through a mask.
   Mask rather than <img> on purpose: the file has #D6D5CB baked in, and a
   plain <img> would pin that value where no token could reach it. As a mask,
   the artwork supplies the SHAPE and --brand-logo supplies the COLOUR, so the
   one constant still governs and the light-surface variant below can apply.
   Aspect ratio 1000:316 from the master viewBox — 92 / 3.165 ≈ 29. */
.g-head__mark i.g-logo {
  display: block;
  width: 92px; height: 29px;       /* bigger, per L (was 15px type) */
  flex: 0 0 auto;
  background-color: var(--brand-logo);
  -webkit-mask: url('../assets/icons/ztor_logo.svg') left center / contain no-repeat;
          mask: url('../assets/icons/ztor_logo.svg') left center / contain no-repeat;
  /* nudge the optical baseline: the wordmark's letterforms sit lower than the
     descriptor's cap-height, so baseline alignment alone reads a hair high */
  position: relative; top: 2px;
}
/* On a bright canvas the bone grey is ~1.3:1 and effectively vanishes. The
   header has no dark ground to give it, so the mark takes the readable step of
   the same warm ramp rather than a different colour. */
html[data-g-theme="light"] .g-head__mark i.g-logo,
html[data-g-world="studio"]:not([data-g-theme="dark"]) .g-head__mark i.g-logo {
  background-color: var(--brand-logo-on-light, #4a473c);
}
.g-head__spacer { flex: 1; }

.g-seg {
  display: inline-flex; align-items: center;
  background: var(--g-panel);
  border: 1px solid var(--g-line);
  border-radius: 9999px; padding: 3px;
}
.g-seg__b {
  appearance: none; border: 0; background: none; cursor: pointer;
  font: inherit; font-size: 12px; font-weight: 500;
  color: var(--g-ink-2);
  padding: 6px 14px; border-radius: 9999px; white-space: nowrap;
  transition: background 180ms ease, color 180ms ease;
}
.g-seg__b:hover { color: var(--g-ink); }
/* Selected state — THREE signals, because two different owners set it.
   The world and theme pairs are driven by gallery.js, which writes
   aria-pressed. The language pair is driven by assets/i18n.js, which is shared
   with the product and writes aria-selected + .lang-dropdown__item--active
   instead. Styling only aria-pressed left the language switcher with no visible
   selection at all — it looked broken while behaving correctly. Match all
   three rather than fighting the shared runtime for control of the markup. */
.g-seg__b[aria-pressed="true"],
.g-seg__b[aria-selected="true"],
.g-seg__b.lang-dropdown__item--active {
  background: color-mix(in srgb, var(--g-accent) 16%, transparent);
  color: var(--g-accent);
}
.g-seg__b:focus-visible { outline: 2px solid var(--g-accent); outline-offset: 2px; }
.g-ctl { display: inline-flex; align-items: center; gap: 8px; }
.g-ctl__lab { font-size: 11px; color: var(--g-ink-3); letter-spacing: .06em; text-transform: uppercase; }

/* ── layout ────────────────────────────────────────────────────────── */
.g-layout { display: grid; grid-template-columns: var(--g-rail) 1fr; align-items: start; }

.g-rail {
  position: sticky; top: var(--g-head);
  height: calc(100vh - var(--g-head));
  overflow-y: auto;
  padding: 24px 12px 48px 24px;
  border-right: 1px solid var(--g-line);
}
.g-nav { display: flex; flex-direction: column; gap: 2px; }
.g-nav__link {
  display: block; padding: 9px 12px; border-radius: 8px;
  /* Section labels are short HEADINGS, so they take the display face — which
     puts Chinese on LINE Seed TW, per the system's heading rule. They were
     inheriting the body stack (Chiron Hei HK) simply because nothing said
     otherwise. Stepped 13→15px: LINE Seed is a geometric brand face that wants
     size, and at 13px its open counters were doing nothing for it. */
  font-family: var(--font-cjk-display);
  font-size: 15px; letter-spacing: -0.005em;
  color: var(--g-ink-2); text-decoration: none;
  transition: background 160ms ease, color 160ms ease;
}
.g-nav__link:hover { background: var(--g-line-soft); color: var(--g-ink); }
.g-nav__link.is-on {
  background: color-mix(in srgb, var(--g-accent) 14%, transparent);
  color: var(--g-accent); font-weight: 500;
}
.g-rail__foot {
  margin-top: 28px; padding-top: 16px; border-top: 1px solid var(--g-line-soft);
  font-size: 11px; line-height: 1.6; color: var(--g-ink-3);
}
.g-rail__foot code { font-family: var(--font-mono); font-size: 10px; }

.g-main { min-width: 0; padding: 0 0 120px; }
.g-scope { padding: 40px 32px 0; }

/* Intro */
.g-intro { padding: 44px 32px 8px; max-width: 720px; }
.g-intro h1 {
  font-family: var(--font-cjk-display);
  font-size: 30px; font-weight: 700; letter-spacing: -0.02em;
  margin: 0 0 10px; color: var(--g-ink);
}
.g-intro p { margin: 0; color: var(--g-ink-2); font-size: 14px; line-height: 1.65; }

/* ── sections + specimens ─────────────────────────────────────────── */
.g-sec { margin-bottom: 64px; scroll-margin-top: calc(var(--g-head) + 16px); }
.g-sec__title {
  font-family: var(--font-cjk-display);
  font-size: 13px; font-weight: 500; letter-spacing: .12em; text-transform: uppercase;
  color: var(--g-ink-3); margin: 0 0 18px;
  padding-bottom: 10px; border-bottom: 1px solid var(--g-line-soft);
}

.g-spec {
  margin-bottom: 28px;
  border: 1px solid var(--g-line);
  border-radius: 14px;
  overflow: hidden;
  background: var(--g-panel);
  scroll-margin-top: calc(var(--g-head) + 16px);
}
.g-spec__head { padding: 18px 20px 14px; }
.g-spec__title {
  font-family: var(--font-cjk-display);
  margin: 0; font-size: 16px; font-weight: 700; color: var(--g-ink);
  letter-spacing: -0.01em;
}
.g-spec__note {
  margin: 6px 0 0; font-size: 13px; line-height: 1.65; color: var(--g-ink-2);
  max-width: 68ch;
}

/* Stages — the surface a specimen sits on. */
.g-stage {
  padding: 28px 20px;
  display: flex; flex-wrap: wrap; gap: 16px; align-items: center;
  border-top: 1px solid var(--g-line-soft);
  border-bottom: 1px solid var(--g-line-soft);
}
.g-stage--found { display: block; }
.g-stage--tight { padding: 16px 20px; }
.g-stage--card {
  background: var(--card, var(--g-panel));
}
/* Glass needs something behind it — this is the demonstration ground. */
.g-stage--backdrop {
  background-image:
    radial-gradient(circle at 18% 30%, #ffa33f 0 12%, transparent 42%),
    radial-gradient(circle at 74% 22%, #8b5cf6 0 14%, transparent 46%),
    radial-gradient(circle at 46% 82%, #014aff 0 16%, transparent 48%),
    radial-gradient(circle at 88% 74%, #00c853 0 10%, transparent 40%),
    linear-gradient(135deg, #1b1b1b, #333);
  background-size: cover;
}
html[data-g-world="studio"] .g-stage--backdrop {
  background-image:
    radial-gradient(circle at 18% 30%, #ffd9a0 0 14%, transparent 44%),
    radial-gradient(circle at 74% 22%, #c4b5fd 0 16%, transparent 48%),
    radial-gradient(circle at 46% 82%, #93bbf8 0 18%, transparent 50%),
    radial-gradient(circle at 88% 74%, #bbf7d0 0 12%, transparent 42%),
    linear-gradient(135deg, #fafafa, #e9e9e6);
}
.g-split { display: flex; width: 100%; gap: 0; border-radius: 10px; overflow: hidden; }
.g-split__half { flex: 1; display: grid; place-items: center; padding: 30px 12px; }
.g-split__half--flat { background: var(--card, #fff); }
html[data-g-world="consumer"] .g-split__half--flat { background: var(--bg-primary, #0a0a0a); }
.g-split__half--rich {
  background-image:
    radial-gradient(circle at 30% 40%, #ffa33f 0 22%, transparent 60%),
    radial-gradient(circle at 70% 60%, #8b5cf6 0 24%, transparent 62%),
    linear-gradient(135deg, #2a2a2a, #444);
}
html[data-g-world="studio"] .g-split__half--rich {
  background-image:
    radial-gradient(circle at 30% 40%, #ffd9a0 0 24%, transparent 62%),
    radial-gradient(circle at 70% 60%, #c4b5fd 0 26%, transparent 64%),
    linear-gradient(135deg, #f4f4f2, #dedede);
}

.g-sel { display: flex; flex-wrap: wrap; gap: 32px; }
.g-sel__col { display: flex; flex-direction: column; gap: 8px; min-width: 170px; }
.g-sel__cap { font-family: var(--font-mono); font-size: 11px; color: var(--g-ink-3); }

/* ── foundations ──────────────────────────────────────────────────── */
.g-found__label {
  font-size: 11px; letter-spacing: .08em; text-transform: uppercase;
  color: var(--g-ink-3); margin: 20px 0 10px;
}
.g-found__label:first-child { margin-top: 0; }
.g-found__note { font-size: 13px; line-height: 1.65; color: var(--g-ink-2); margin: 0; max-width: 68ch; }
/* A trailing note is a caption for the thing above it, not a continuation of
   it. Flush against the last bar it read as another row of the scale; the gap
   is what tells the eye the list ended. Applied on the FOLLOWS-something
   selector so a note that opens a block keeps its flush top. */
* + .g-found__note { margin-top: 18px; }
.g-rev + .g-copy { margin-top: 2px; }

.g-sw-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; }
.g-sw {
  appearance: none; text-align: left; cursor: pointer; padding: 0;
  background: transparent; border: 1px solid var(--g-line);
  border-radius: 10px; overflow: hidden;
  transition: transform 150ms ease, border-color 150ms ease;
}
.g-sw:hover { transform: translateY(-2px); border-color: var(--g-accent); }
.g-sw.is-copied { border-color: var(--g-accent); }
/* Copy confirmation. A ::after glyph cannot be drawn — a pseudo-element has no
   path to stroke — so the swatch borrows the tick's DRAW rather than its shape:
   a stroke that wipes in left-to-right on the same entrance ease. Same grammar
   as .ds-success-tick, expressed in the one property a pseudo-element has. */
.g-sw__n::after {
  content: ''; display: inline-block; vertical-align: middle;
  width: 0; height: 2px; margin-left: 0; border-radius: 2px;
  background: var(--g-accent);
  transition: width 260ms var(--ease-settle, ease), margin-left 260ms var(--ease-settle, ease);
}
.g-sw.is-copied .g-sw__n::after { width: 12px; margin-left: 6px; }
.g-sw:focus-visible { outline: 2px solid var(--g-accent); outline-offset: 2px; }
.g-sw__chip { display: block; height: 46px; border-bottom: 1px solid var(--g-line); }
.g-sw__meta { display: block; padding: 7px 9px; }
.g-sw__n { display: block; font-family: var(--font-mono); font-size: 11px; color: var(--g-ink); }
.g-sw__v { display: block; font-family: var(--font-mono); font-size: 10px; color: var(--g-ink-3); margin-top: 2px; }

.g-space-list { display: flex; flex-direction: column; gap: 5px; }
.g-space { display: grid; grid-template-columns: 110px 1fr 64px; align-items: center; gap: 12px; }
.g-space code { font-family: var(--font-mono); font-size: 11px; color: var(--g-ink-2); }
.g-space__bar { height: 12px; border-radius: 3px; background: var(--g-accent); }
.g-dim { color: var(--g-ink-3) !important; text-align: right; }

.g-rad-grid { display: flex; flex-wrap: wrap; gap: 14px; }
.g-rad { display: flex; flex-direction: column; gap: 5px; align-items: center; }
.g-rad__box {
  width: 74px; height: 56px;
  background: color-mix(in srgb, var(--g-accent) 18%, transparent);
  border: 1px solid var(--g-accent);
}
.g-rad code { font-family: var(--font-mono); font-size: 11px; color: var(--g-ink-2); }

.g-type { display: flex; flex-direction: column; }
.g-type__row {
  display: grid; grid-template-columns: 130px 1fr; gap: 16px; align-items: baseline;
  padding: 10px 0; border-bottom: 1px solid var(--g-line-soft);
}
.g-type__tag { font-family: var(--font-mono); font-size: 11px; color: var(--g-ink-3); }

.g-elev { display: flex; flex-wrap: wrap; gap: 18px; }
.g-elev__c {
  background: var(--card, var(--g-panel));
  border-radius: 10px; padding: 16px 20px; min-width: 96px;
  display: flex; flex-direction: column; gap: 3px;
}
.g-elev__c b { font-size: 14px; font-weight: 500; color: var(--g-ink); }
.g-elev__c code { font-family: var(--font-mono); font-size: 10px; color: var(--g-ink-3); }

.g-motion { display: flex; flex-direction: column; gap: 10px; }
.g-motion__row { display: grid; grid-template-columns: 150px 1fr 72px; align-items: center; gap: 14px; cursor: pointer; }
.g-motion__track {
  position: relative; height: 22px; border-radius: 9999px;
  background: var(--g-line-soft); overflow: hidden;
}
.g-motion__dot {
  position: absolute; top: 4px; left: 4px;
  width: 14px; height: 14px; border-radius: 50%; background: var(--g-accent);
}

/* ── inspector ────────────────────────────────────────────────────── */
.g-insp { background: color-mix(in srgb, var(--g-shell) 55%, var(--g-panel)); }
.g-insp__tabs { display: flex; gap: 2px; padding: 8px 12px 0; border-bottom: 1px solid var(--g-line-soft); }
.g-insp__tab {
  appearance: none; border: 0; background: none; cursor: pointer; font: inherit;
  font-size: 12px; font-weight: 500; color: var(--g-ink-3);
  padding: 7px 12px; border-radius: 7px 7px 0 0;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.g-insp__tab:hover { color: var(--g-ink); }
.g-insp__tab.is-on { color: var(--g-ink); border-bottom-color: var(--g-accent); }
.g-insp__tab:focus-visible { outline: 2px solid var(--g-accent); outline-offset: -2px; }
.g-insp__pane { display: none; }
.g-insp__pane.is-on { display: block; }
.g-insp__bar {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 14px 6px;
}
.g-insp__src { font-family: var(--font-mono); font-size: 11px; color: var(--g-ink-3); }
.g-insp__empty { margin: 0; padding: 14px; font-size: 13px; line-height: 1.6; color: var(--g-ink-2); }
.g-insp__empty code { font-family: var(--font-mono); font-size: 11px; color: var(--g-ink); }

.g-copy {
  appearance: none; cursor: pointer; font: inherit; font-size: 11px; font-weight: 500;
  color: var(--g-ink-2); background: var(--g-panel);
  border: 1px solid var(--g-line); border-radius: 7px; padding: 4px 10px;
  transition: color 150ms ease, border-color 150ms ease;
}
.g-copy:hover { color: var(--g-ink); border-color: var(--g-accent); }
.g-copy.is-done { color: var(--g-accent); border-color: var(--g-accent); }
.g-copy:focus-visible { outline: 2px solid var(--g-accent); outline-offset: 2px; }

.g-code {
  margin: 0; padding: 4px 14px 16px;
  overflow-x: auto;
  font-family: var(--font-mono); font-size: 12px; line-height: 1.7;
  color: var(--g-ink-2);
  tab-size: 2;
}
.g-code code { white-space: pre; }

.g-tok { width: 100%; border-collapse: collapse; margin: 0 0 12px; }
.g-tok td { padding: 6px 14px; border-top: 1px solid var(--g-line-soft); vertical-align: middle; }
.g-tok code { font-family: var(--font-mono); font-size: 11px; }
.g-tok__n { white-space: nowrap; }
.g-tok__n code { color: var(--g-ink); }
.g-tok__v code { color: var(--g-ink-2); }
.g-tok__c { text-align: right; }
.g-tok__c code { color: var(--g-ink-3); }
.g-tok__sw {
  display: inline-block; width: 13px; height: 13px; border-radius: 4px;
  border: 1px solid var(--g-line); margin-right: 7px; vertical-align: -2px;
}
.g-tok b.is-pass { color: #4ade80; font-weight: 500; }
.g-tok b.is-fail { color: #ff5a5a; font-weight: 500; }
html[data-g-world="studio"] .g-tok b.is-pass { color: #1D5126; }
html[data-g-world="studio"] .g-tok b.is-fail { color: #A81E38; }

/* ── responsive ───────────────────────────────────────────────────── */
@media (max-width: 1023.98px) {
  .g-layout { grid-template-columns: 1fr; }
  .g-rail {
    position: static; height: auto; border-right: 0;
    border-bottom: 1px solid var(--g-line); padding: 12px 16px;
  }
  .g-nav { flex-direction: row; flex-wrap: wrap; }
  .g-rail__foot { display: none; }
}
@media (max-width: 767.98px) {
  .g-head { gap: 10px; padding: 0 14px; height: auto; min-height: var(--g-head); flex-wrap: wrap; padding-block: 10px; }
  .g-head__spacer { display: none; }
  .g-ctl__lab { display: none; }
  .g-scope, .g-intro { padding-inline: 16px; }
  .g-spec__head, .g-stage { padding-inline: 14px; }
  .g-space { grid-template-columns: 84px 1fr 54px; }
  .g-type__row { grid-template-columns: 1fr; gap: 4px; }
  .g-motion__row { grid-template-columns: 104px 1fr 60px; }
}

@media (prefers-reduced-motion: reduce) {
  .g-sw, .g-copy, .g-nav__link, .g-seg__b { transition: none; }
  .g-motion__dot { transition: none !important; }
}

/* auditor abstention — a measurement it cannot make honestly */
.g-tok b.is-skip { color: var(--g-ink-3); font-weight: 400; font-size: 11px; }

/* ── motion lab ─────────────────────────────────────────────────────────
   Each row plays on hover AND on click, so the grammar can be felt rather
   than read. The dot starts from an already-visible resting position — an
   entrance that begins at opacity 0 teaches nothing about the curve. */
.g-mo { display: flex; flex-direction: column; gap: 14px; }
.g-mo__row { display: grid; grid-template-columns: 168px 1fr 78px; gap: 14px; align-items: center; cursor: pointer; }
.g-mo__meta { display: flex; flex-direction: column; gap: 2px; }
.g-mo__name { font-family: var(--font-mono); font-size: 11px; color: var(--g-ink); }
.g-mo__role { font-size: 11px; color: var(--g-ink-3); line-height: 1.4; }
.g-mo__track {
  position: relative; height: 26px; border-radius: 9999px;
  background: var(--g-line-soft); overflow: hidden;
}
.g-mo__dot {
  position: absolute; top: 5px; left: 5px;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--g-accent);
}
.g-mo__row.is-playing .g-mo__dot { transform: translateX(calc(100cqw - 26px)); }
.g-mo__track { container-type: inline-size; }
.g-mo__dur { font-family: var(--font-mono); font-size: 11px; color: var(--g-ink-3); text-align: right; }

/* travel + stagger demo */
/* RESERVE THE TRAVEL. The cards animate with `backwards` fill, so during their
   stagger delay each one physically sits --g-y (24px) BELOW its final position.
   Without reserved space that pre-animation state overlaps whatever follows —
   which is exactly how the replay button ended up underneath the first card.
   The container holds the travel distance so nothing below is ever collided
   with, at rest or mid-stagger. */
.g-rev {
  display: flex; gap: 10px; flex-wrap: wrap;
  padding-bottom: var(--g-y, 24px);
  margin-bottom: 4px;
}
.g-rev__card {
  width: 86px; height: 58px; border-radius: 10px;
  background: var(--g-line-soft); border: 1px solid var(--g-line);
  display: grid; place-items: center;
  font-family: var(--font-mono); font-size: 10px; color: var(--g-ink-3);
}
.g-rev.is-playing .g-rev__card { animation: g-arrive var(--g-arrive, 688ms) var(--ease-settle) backwards; }
/* the replay control is a sibling of the stage, never inside its travel zone */
.g-rev + .g-copy { display: inline-flex; align-items: center; gap: 6px; }
@keyframes g-arrive { from { opacity: 0; transform: translateY(var(--g-y, 24px)); } }

@media (prefers-reduced-motion: reduce) {
  .g-mo__dot { transition: none !important; }
  .g-rev.is-playing .g-rev__card { animation: none; }
}


/* ── slide-in specimen stages ───────────────────────────────────────────
   The stage is a miniature viewport: the overlay positions against IT rather
   than the page, so the same component markup demos at real scale here and
   covers the screen in production. Content behind it is not decoration —
   glass needs something to refract, and an empty stage would misrepresent the
   material (see the Glass-Needs-A-Ground rule). */
.ds-stage__bg {
  position: absolute; inset: 0; z-index: 0;
  background-image:
    radial-gradient(circle at 22% 28%, #ffa33f 0 13%, transparent 44%),
    radial-gradient(circle at 72% 24%, #8b5cf6 0 15%, transparent 47%),
    radial-gradient(circle at 44% 84%, #014aff 0 17%, transparent 49%),
    radial-gradient(circle at 86% 76%, #00c853 0 11%, transparent 41%),
    linear-gradient(135deg, #1b1b1b, #343434);
}
html[data-g-theme="light"] .ds-stage__bg,
html[data-g-world="studio"]:not([data-g-theme="dark"]) .ds-stage__bg {
  background-image:
    radial-gradient(circle at 22% 28%, #ffd9a0 0 15%, transparent 46%),
    radial-gradient(circle at 72% 24%, #c4b5fd 0 17%, transparent 49%),
    radial-gradient(circle at 44% 84%, #93bbf8 0 19%, transparent 51%),
    radial-gradient(circle at 86% 76%, #bbf7d0 0 13%, transparent 43%),
    linear-gradient(135deg, #fbfbfa, #e6e6e2);
}
.g-stage .ds-stage { width: 100%; }
/* the trigger sits centred in its stage, above the ground, below the overlay */
.ds-trigger {
  position: absolute; z-index: 2;
  left: 50%; top: 50%; transform: translate(-50%, -50%);
}
.ds-trigger:active { transform: translate(-50%, -50%) scale(0.96); }

/* ── INTERACTION LAYER ──────────────────────────────────────────────────
   L: "all the components that is clickable or can mouse over should be
   clickable with the animation". Specimens were rendering as static pictures
   of components; a design system that cannot be poked teaches only half of
   each component. Every affordance below now moves on hover and answers on
   press, on the system's own feedback tier (--ease-out, fast) so the gallery
   demonstrates the motion grammar rather than inventing a second one. */
.g-stage :is(button, a, [role="button"], .liquid-glass, .ds-chip, .ds-nav__i, .ds-tab) {
  cursor: pointer;
}
/* Glass: lifts toward the light, then meets the finger. */
.g-stage .liquid-glass:not(.ds-slide) {
  transition: transform var(--dur-nav-show) var(--ease-out),
              filter var(--dur-nav-show) var(--ease-out);
}
.g-stage .liquid-glass:not(.ds-slide):hover { filter: brightness(1.08); }
.g-stage .liquid-glass:not(.ds-trigger):not(.ds-slide):hover { transform: translateY(-2px); }
.g-stage .liquid-glass:not(.ds-trigger):not(.ds-slide):active { transform: translateY(0) scale(0.97); }

/* Solid + ghost buttons authored by specimens */
.g-stage :is(.ds-btn, .btn) {
  transition: transform var(--dur-nav-show) var(--ease-out),
              filter var(--dur-nav-show) var(--ease-out),
              background var(--dur-nav-show) var(--ease-out);
}
.g-stage :is(.ds-btn, .btn):hover { filter: brightness(1.06); transform: translateY(-1px); }
.g-stage :is(.ds-btn, .btn):active { transform: scale(0.97); }

/* Selection surfaces answer without committing — hover previews, click holds */
.g-stage .ds-nav__i { transition: background var(--dur-nav-show) var(--ease-out); }
.g-stage .ds-nav__i:not(.ds-nav__i--on):hover { background: var(--accent); }
.g-stage .ds-tab { transition: color var(--dur-nav-show) var(--ease-out); }
.g-stage .ds-tab:not(.ds-tab--on):hover { color: var(--foreground); }
.g-stage .ds-chip { transition: transform var(--dur-nav-show) var(--ease-out); }
.g-stage .ds-chip:hover { transform: translateY(-1px); }

/* Inputs earn a border response, not a jump — a field that moves reads broken */
.g-stage .ds-input {
  transition: border-color var(--dur-nav-show) var(--ease-out),
              box-shadow var(--dur-nav-show) var(--ease-out);
}
.g-stage .ds-input:hover { border-color: var(--foreground-muted); }

/* Cards already lift; make sure the whole tile is a target */
.g-stage :is(.ds-card, .gc-card) { cursor: pointer; }

@media (prefers-reduced-motion: reduce) {
  .g-stage :is(.liquid-glass, .ds-btn, .btn, .ds-chip, .ds-nav__i, .ds-tab, .ds-input),
  .ds-trigger { transition: none !important; }
  .g-stage :is(.liquid-glass, .ds-btn, .btn, .ds-chip):hover { transform: none; }
}
