/* ==================================================================
   App styles. Every color/size flows from --ui-* design tokens with
   dark-glass fallbacks. Component styling follows /components/docs.
   ================================================================== */
:root {
  /* Frequently used aliases (token, dark-glass fallback) */
  --sfc-app: var(--ui-surface-app, rgb(14, 15, 17));
  --sfc-panel: var(--ui-surface-panel, rgba(251, 253, 255, 0.05));
  --sfc-raised: var(--ui-surface-panel-raised, rgba(251, 253, 255, 0.1));
  --sfc-sunken: var(--ui-surface-sunken, rgba(0, 0, 0, 0.2));
  --txt-primary: var(--ui-text-primary, rgb(251, 253, 255));
  --txt-body: var(--ui-text-body, rgb(210, 212, 214));
  --txt-muted: var(--ui-text-muted, rgb(147, 149, 151));
  --brd: var(--ui-field-border, rgb(117, 119, 121));
  --accent: var(--ui-action-standard-bg, rgb(111, 189, 100));
  --accent-hover: var(--ui-action-standard-hover, rgb(80, 157, 69));
  --focus-ring: var(--ui-focus-ring, 0 0 0 2px rgb(80, 157, 69));
  --dur: var(--ui-motion-duration-fast, 150ms);
  --ease: var(--ui-motion-ease-out, cubic-bezier(0, 0, 0, 1));
  --font: var(--ui-font-family, Inter), system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono: var(--ui-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  /* Browser-local: makes currentColor icon sets legible on dark thumbs.
     No ui.* token exists for this yet. */
  --mono-invert: invert(92%) hue-rotate(180deg);
}
* { box-sizing: border-box; }
/* The UA's [hidden] { display: none } is a low-specificity rule that ANY
   author `display` declaration silently beats — so `el.hidden = true` on a
   flex/grid/inline-flex element (e.g. .icon-edit__usecase-remove) does
   nothing. Restate it here so the hidden property means hidden everywhere. */
[hidden] { display: none !important; }
/* html fills the viewport; body grows with its content. Using height:100%
   on body would pin its box to one viewport, which caps the sticky
   .app-header's containing block and unsticks it partway down long pages. */
html { height: 100%; }
body { min-height: 100%; }
body {
  margin: 0;
  /* Fallback: solid page color (ui.page.fallback) in case the gradient
     stack cannot render or theme CSS has not loaded. */
  background: var(--ui-page-fallback, rgb(0, 10, 0));
  /* LINQX page background: accent wash (ui.page.accent) layered over the
     base gradient (ui.page.background); flips per brand/mode/style axes;
     fixed so it anchors to the viewport and covers the full scroll height. */
  background:
    var(--ui-page-accent, radial-gradient(140% 100% at 100% 0%, rgba(80, 157, 69, 0.1), rgba(80, 157, 69, 0) 55%)),
    var(--ui-page-background, linear-gradient(135deg, rgb(0, 10, 0), rgb(2, 29, 0)));
  background-attachment: fixed;
  color: var(--txt-body);
  font-family: var(--font);
  font-size: var(--ui-text-body-default-font-size, 0.9375rem);
  line-height: var(--ui-text-body-default-line-height, 1.4);
}
button { font: inherit; color: inherit; }
a { color: var(--ui-link-enabled, rgb(111, 189, 100)); }
a:hover { color: var(--ui-link-hover, rgb(80, 157, 69)); }
:is(a, button, input, select, [tabindex]):focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--ui-radii-field, 0.5rem);
}
.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}
.skip-link {
  position: absolute; left: 0.75rem; top: -3rem; z-index: 500;
  padding: 0.5rem 0.75rem; border-radius: var(--ui-radii-field, 0.5rem);
  background: var(--ui-menu-bg, rgb(37, 38, 40)); color: var(--txt-primary);
  transition: top var(--dur) var(--ease);
}
.skip-link:focus { top: 0.75rem; }

/* ---- Glass panels (containers only get the rim, per card doc) ---- */
.panel {
  position: relative;
  background: var(--sfc-panel);
  -webkit-backdrop-filter: var(--ui-backdrop-filter-panel, blur(1rem));
  backdrop-filter: var(--ui-backdrop-filter-panel, blur(1rem));
  border-radius: var(--ui-radii-card, 1rem);
  box-shadow:
    var(--ui-shadow-inset, inset 0 1px 1px rgba(251, 253, 255, 0.1)),
    var(--ui-shadow-crescent, inset 0 -1px 1px rgba(0, 0, 0, 0.15)),
    var(--ui-shadow-halo, 0 1px 6px rgba(251, 253, 255, 0.16)),
    var(--ui-shadow-card, 0 8px 32px rgba(0, 0, 0, 0.5));
}
.panel::before {
  content: ""; position: absolute; inset: 0; z-index: 0;
  border-radius: inherit; padding: 1px;
  background: var(--ui-gradient-panel-rim, linear-gradient(145deg,
    rgba(251, 253, 255, 0.35) 0%, rgba(251, 253, 255, 0.08) 38%,
    rgba(251, 253, 255, 0.04) 62%, rgba(251, 253, 255, 0.12) 100%));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
}
.panel > * { position: relative; z-index: 1; }

/* ---------------------------- Layout ------------------------------ */
/* Header shell: three detached glass modules (ops-dashboard pattern) —
   brand + search + tools bookends, flush to the top screen edge, with
   gaps between them revealing the app background. See archetype shell:
   components/archetype shells/Operations Shell.standalone.html */
.app-header {
  display: flex; align-items: stretch;
  /* ui.header.height (components/examples/layout-grid/layout-grid.md) —
     single source of the header's height; sidebar/content offsets below
     derive from the same var so they never drift from it. */
  height: var(--ui-header-height, 4rem); gap: 0.875rem; margin: 0 0 0.75rem;
  position: sticky; top: 0; z-index: 200;
}
/* Solid app-background strip behind the detached tiles so scrolled
   content doesn't show through the bookend gaps while stuck. */
.app-header::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  /* Fallback: solid page color (ui.page.fallback), matching the body's
     fallback so the backing strip is seamless even before theme CSS loads. */
  background: var(--ui-page-fallback, rgb(0, 10, 0));
  /* Match the fixed body background (ui.page.background + ui.page.accent)
     exactly so the backing strip is seamless. */
  background:
    var(--ui-page-accent, radial-gradient(140% 100% at 100% 0%, rgba(80, 157, 69, 0.1), rgba(80, 157, 69, 0) 55%)),
    var(--ui-page-background, linear-gradient(135deg, rgb(0, 10, 0), rgb(2, 29, 0)));
  background-attachment: fixed;
}
/* Shared glass-tile + beveled rim, adapted from the ops-shell
   .glass-tile/.glass-rim onto this file's panel tokens/fallbacks
   (card doc: rim is a containers-only treatment). */
.hdr-tile {
  position: relative; overflow: hidden;
  background: var(--sfc-panel);
  -webkit-backdrop-filter: var(--ui-backdrop-filter-panel, blur(1rem));
  backdrop-filter: var(--ui-backdrop-filter-panel, blur(1rem));
  box-shadow:
    var(--ui-shadow-inset, inset 0 1px 1px rgba(251, 253, 255, 0.1)),
    var(--ui-shadow-crescent, inset 0 -1px 1px rgba(0, 0, 0, 0.15)),
    var(--ui-shadow-halo, 0 1px 6px rgba(251, 253, 255, 0.16)),
    var(--ui-shadow-card, 0 8px 32px rgba(0, 0, 0, 0.5));
}
.hdr-tile::before {
  content: ""; position: absolute; inset: 0; z-index: 0; border-radius: inherit; padding: 1px;
  background: var(--ui-gradient-panel-rim, linear-gradient(145deg,
    rgba(251, 253, 255, 0.35) 0%, rgba(251, 253, 255, 0.08) 38%,
    rgba(251, 253, 255, 0.04) 62%, rgba(251, 253, 255, 0.12) 100%));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
}
.hdr-tile > * { position: relative; z-index: 1; }

/* LEFT BOOKEND — flush top+left screen edges; only the free bottom-right
   corner rounds. Rim ring is shifted past both flush edges and clipped by
   the tile's own overflow:hidden (mirrors ops .brand-block). */
.brand-block {
  flex-shrink: 0; display: flex; align-items: center; gap: 0.625rem;
  padding: 0 1rem; border-radius: 0 0 0.875rem 0; text-decoration: none;
}
.brand-block::before { inset: -1px 0 0 -1px; }
.brand-block .brand-mark {
  width: 2rem; height: 2rem; flex-shrink: 0;
  color: var(--txt-primary); /* SVG strokes/fills are currentColor */
}
.brand-block span { color: var(--txt-muted); font-size: 0.8125rem; }

/* CENTER — flush top screen edge only; both bottom corners round. */
.search-block {
  flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 0.5rem;
  padding: 0 1rem; border-radius: 0 0 0.875rem 0.875rem; color: var(--txt-muted);
}
.search-block::before { inset: -1px 0 0 0; }
.search-block input {
  flex: 1; min-width: 0; background: none; border: none; outline: none;
  color: var(--txt-primary); font: inherit;
}
.search-block input::placeholder { color: var(--ui-field-placeholder, var(--txt-muted)); }
.search-block input:focus-visible { box-shadow: none; }
.search-block .hint {
  color: var(--txt-muted); font-size: 0.75rem;
  border: 1px solid var(--ui-menu-border, rgb(62, 64, 65));
  border-radius: var(--ui-radii-chip, 0.25rem); padding: 0.1rem 0.35rem;
}

/* RIGHT BOOKEND — mirror of the brand block: flush top+right screen
   edges; only the free bottom-left corner rounds. Holds the Style
   dropdown + Mode and Brand button-bars (see components/docs/button-bar.md). */
.header-tools {
  flex-shrink: 0; display: flex; align-items: center; gap: 0.75rem;
  padding: 0 1rem; border-radius: 0 0 0 0.875rem;
}
.header-tools::before { inset: -1px -1px 0 0; }
.header-tools label.ctl { display: inline-flex; align-items: center; gap: 0.4rem; color: var(--txt-muted); font-size: 0.8125rem; }

.shell {
  display: grid; grid-template-columns: minmax(240px, 320px) 1fr;
  gap: 0.75rem; padding: 0 0.75rem 0.75rem 0; align-items: start;
  min-height: 0;
}
.sidebar {
  /* The panel rim (::before) is absolutely positioned, so the sidebar
     itself must NOT be the scroll container: an abs-pos ring inside a
     scroller is sized to the scrollport but anchored to the CONTENT's top
     edge, so it scrolls away with the tree — its rounded bottom edge then
     sweeps through the middle of the list as a stray hairline box (and the
     panel's real edges lose their rim). The tree scrolls instead; the
     panel — and its rim — stay put. */
  display: flex; flex-direction: column;
  padding: 0.75rem 0.5rem; overflow: hidden;
  /* Offset below the sticky header: ui.header.height + 0.75rem gap. */
  max-height: calc(100vh - var(--ui-header-height, 4rem) - 1.5rem);
  position: sticky; top: calc(var(--ui-header-height, 4rem) + 0.75rem);
  /* Flush to the viewport's left edge: only the free right corners round. */
  border-radius: 0 var(--ui-radii-card, 1rem) var(--ui-radii-card, 1rem) 0;
}
.sidebar > .tree { overflow: auto; min-height: 0; }
/* Shift the rim ring past the flush left edge so no border paints there;
   the off-viewport strip is clipped by the viewport (mirrors .brand-block). */
.sidebar::before { inset: 0 0 0 -1px; }
.content { padding: 1.25rem 1.5rem; min-width: 0; margin-bottom: 0.75rem; scroll-margin-top: calc(var(--ui-header-height, 4rem) + 0.75rem); }
@media (max-width: 900px) {
  .shell { grid-template-columns: 1fr; }
  .sidebar { position: static; max-height: 40vh; }
}

/* --------------------------- Controls ----------------------------- */
.btn {
  display: inline-flex; align-items: center; gap: var(--ui-button-gap, 0.5rem);
  height: var(--ui-button-compact-height, 2rem);
  padding: 0 var(--ui-button-compact-pad-x, 0.75rem);
  border-radius: var(--ui-button-compact-radii, 0.5rem);
  border: 1px solid var(--brd);
  background: var(--sfc-raised); color: var(--txt-primary);
  cursor: pointer; white-space: nowrap;
  font-size: var(--ui-button-compact-text-font-size, 0.8125rem);
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease),
    transform var(--dur) var(--ease);
}
.btn:hover { border-color: var(--accent-hover); }
.btn:active { transform: translateY(var(--ui-button-compact-press-inset, 1px)); }
.btn.primary {
  background: var(--accent); border-color: transparent;
  color: var(--ui-action-standard-content, rgb(0, 0, 0));
}
.btn.primary:hover { background: var(--accent-hover); }
.btn.primary:active { background: var(--ui-action-standard-pressed-solid, rgb(142, 222, 131)); }
/* Disabled (button.md's disabled-fill/disabled-content pair per hierarchy).
   pointer-events:none backs up the explicit :hover/:active no-ops below so
   neither hierarchy ever shows a hover/press state while disabled. */
.btn:disabled, .btn[disabled] {
  background: var(--ui-button-standard-disabled-fill, rgb(37, 38, 40));
  border-color: transparent;
  color: var(--ui-button-standard-disabled-content, rgb(89, 91, 92));
  cursor: not-allowed; pointer-events: none;
}
.btn:disabled:hover, .btn[disabled]:hover,
.btn:disabled:active, .btn[disabled]:active {
  background: var(--ui-button-standard-disabled-fill, rgb(37, 38, 40));
  border-color: transparent; transform: none;
}
.btn.primary:disabled, .btn.primary[disabled] {
  background: var(--ui-action-standard-disabled, rgb(37, 38, 40));
  color: var(--ui-button-standard-disabled-content, rgb(89, 91, 92));
}
.btn.primary:disabled:hover, .btn.primary[disabled]:hover,
.btn.primary:disabled:active, .btn.primary[disabled]:active {
  background: var(--ui-action-standard-disabled, rgb(37, 38, 40));
}
/* Prominent size modifier (button.md's compact/default/prominent size tier,
   same token family the base .btn rule already draws its compact sizing
   from). Header-level CTAs only — e.g. the icon detail page's "Edit
   curation" trigger next to the title. */
.btn.prominent {
  height: var(--ui-button-prominent-height, 3.75rem);
  padding: 0 var(--ui-button-prominent-pad-x, 1.5rem);
  border-radius: var(--ui-button-prominent-radii, 0.5rem);
  font-size: var(--ui-button-prominent-text-font-size, 1.125rem);
}
.btn.prominent:active { transform: translateY(var(--ui-button-prominent-press-inset, 0.75rem)); }

/* Button bar (components/docs/button-bar.md): stroke-outlined segment
   group — no padded track. Outer edges carry the full stroke width, shared
   inner edges a hairline divider each; selection is surfaced by the sliding
   indicator (overshoot ease). Sizes: default + compact modifier. */
.button-bar {
  display: inline-flex;
  position: relative;
  isolation: isolate;
}
.button-bar__segment {
  position: relative; z-index: 1;
  display: inline-flex; align-items: center; justify-content: center;
  height: var(--ui-button-default-height, 3rem);
  min-width: 75px;
  padding: 0 var(--ui-button-default-pad-x, 1rem);
  font-family: var(--ui-button-default-text-font-family, inherit);
  font-size: var(--ui-button-default-text-font-size, 1rem);
  font-weight: var(--ui-button-default-text-font-weight, 400);
  line-height: var(--ui-button-default-text-line-height, 1.5);
  color: var(--ui-button-standard-secondary-content, rgb(251, 253, 255));
  background: transparent; cursor: pointer;
  border: 0 solid var(--ui-action-standard-stroke, rgb(111, 189, 100));
  border-top-width: var(--ui-button-default-stroke-width, 2px);
  border-bottom-width: var(--ui-button-default-stroke-width, 2px);
  border-left-width: var(--ui-control-segment-divider-width, 1px);
  border-right-width: var(--ui-control-segment-divider-width, 1px);
  transition: color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.button-bar__segment--left {
  border-left-width: var(--ui-button-default-stroke-width, 2px);
  border-top-left-radius: var(--ui-button-default-radii, 0.5rem);
  border-bottom-left-radius: var(--ui-button-default-radii, 0.5rem);
}
.button-bar__segment--right {
  border-right-width: var(--ui-button-default-stroke-width, 2px);
  border-top-right-radius: var(--ui-button-default-radii, 0.5rem);
  border-bottom-right-radius: var(--ui-button-default-radii, 0.5rem);
}
.button-bar__segment[aria-checked="true"] {
  color: var(--ui-action-standard-content, rgb(0, 0, 0));
}
.button-bar__segment:hover {
  background: var(--ui-action-standard-hover, rgb(80, 157, 69));
  color: var(--ui-action-standard-content, rgb(0, 0, 0));
}
.button-bar__segment:focus-visible {
  box-shadow: var(--focus-ring);
}
.button-bar__indicator {
  position: absolute; z-index: 0; top: 0; bottom: 0; left: 0;
  background: var(--ui-control-segment-indicator-fill, rgb(111, 189, 100));
  border-radius: var(--ui-control-segment-indicator-radii, 0.5rem);
  /* Travel overshoots the destination edge and settles back (Figma spec);
     only transform overshoots, width eases normally. */
  transition: transform var(--ui-motion-duration-moderate, 300ms) var(--ui-motion-ease-overshoot, cubic-bezier(0.68, -0.55, 0.265, 1.55)),
    width var(--ui-motion-duration-moderate, 300ms) var(--ui-motion-ease-out, cubic-bezier(0, 0, 0, 1)),
    border-radius var(--dur) var(--ease);
}
/* The indicator fills the selected segment completely: only the group's
   outer corners stay rounded, matching the segment under it. */
.button-bar[data-sel="left"] .button-bar__indicator {
  border-top-right-radius: 0; border-bottom-right-radius: 0;
}
.button-bar[data-sel="middle"] .button-bar__indicator { border-radius: 0; }
.button-bar[data-sel="right"] .button-bar__indicator {
  border-top-left-radius: 0; border-bottom-left-radius: 0;
}
/* Compact size (ui.button.compact.*) */
.button-bar--compact .button-bar__segment {
  height: var(--ui-button-compact-height, 2rem);
  min-width: 50px;
  padding: 0 var(--ui-button-compact-pad-x, 0.75rem);
  font-family: var(--ui-button-compact-text-font-family, inherit);
  font-size: var(--ui-button-compact-text-font-size, 0.75rem);
  font-weight: var(--ui-button-compact-text-font-weight, 400);
  line-height: var(--ui-button-compact-text-line-height, 1.5);
  border-top-width: var(--ui-button-compact-stroke-width, 2px);
  border-bottom-width: var(--ui-button-compact-stroke-width, 2px);
}
.button-bar--compact .button-bar__segment--left {
  border-left-width: var(--ui-button-compact-stroke-width, 2px);
  border-top-left-radius: var(--ui-button-compact-radii, 0.5rem);
  border-bottom-left-radius: var(--ui-button-compact-radii, 0.5rem);
}
.button-bar--compact .button-bar__segment--right {
  border-right-width: var(--ui-button-compact-stroke-width, 2px);
  border-top-right-radius: var(--ui-button-compact-radii, 0.5rem);
  border-bottom-right-radius: var(--ui-button-compact-radii, 0.5rem);
}
.button-bar--compact .button-bar__indicator {
  border-radius: var(--ui-control-segment-indicator-radii-compact, 0.5rem);
}
/* While traveling, the indicator is a fully rounded pill-corner rect; the
   position-aware squaring applies once it settles after the overshoot.
   These come last so they beat the data-sel rules during motion. */
.button-bar.is-moving .button-bar__indicator {
  border-radius: var(--ui-control-segment-indicator-radii, 0.5rem);
}
.button-bar--compact.is-moving .button-bar__indicator {
  border-radius: var(--ui-control-segment-indicator-radii-compact, 0.5rem);
}
/* No indicator motion until the initial placement has painted — the
   default selection must appear already in place, not slide in on load. */
.button-bar:not(.is-ready) .button-bar__indicator { transition: none; }
/* Reduced-motion tier (docs/taxonomy-motion.md §3/§6), aligned to the
   design-system button-bar component (components/examples/button-bar):
   the indicator's slide is a structural/feedback, kinetic (transform +
   width) effect, which the reduced tier forces to its tonal equivalent
   rather than dropping outright — the transform/width travel (with its
   overshoot ease) is replaced by a brief opacity crossfade. `.is-switching`
   (JS-driven, see buttonBar()) drops the indicator to 0 opacity while it
   is repositioned instantly underneath, then fades it back in. There is
   no `data-motion` attribute wired yet (per §6), so this is gated on the
   OS `prefers-reduced-motion` media query; a future data-motion hook can
   slot in alongside it. */
@media (prefers-reduced-motion: reduce) {
  .button-bar__indicator {
    transition: opacity var(--ui-motion-duration-fast, 150ms) var(--ui-motion-ease-standard, cubic-bezier(0.2, 0, 0, 1));
  }
  .button-bar__indicator.is-switching { opacity: 0; }
}

select.ctl-select {
  background: var(--ui-field-bg, rgba(251, 253, 255, 0.05));
  color: var(--txt-primary); border: 1px solid var(--brd);
  border-radius: var(--ui-radii-field, 0.5rem); padding: 0.3rem 0.5rem;
  font: inherit; font-size: 0.8125rem;
}

/* ---------------------------- Badges ------------------------------ */
.badge {
  display: inline-flex; align-items: center; justify-content: center;
  height: calc(var(--ui-badge-height, 32px) * 0.72);
  min-width: 2rem; padding: 0 var(--ui-badge-pad-x, 0.75rem);
  border-radius: var(--ui-badge-radii, 9999px);
  border: var(--ui-badge-stroke-width, 1px) solid;
  font-size: var(--ui-badge-text-font-size, 0.75rem); font-weight: 600;
  line-height: 1; text-transform: lowercase;
}
.badge.success { background: var(--ui-badge-success-fill, rgb(111, 189, 100)); border-color: var(--ui-badge-success-stroke, rgb(111, 189, 100)); color: var(--ui-badge-success-content, rgb(0, 0, 0)); }
.badge.error { background: var(--ui-badge-error-fill, rgb(255, 121, 135)); border-color: var(--ui-badge-error-stroke, rgb(255, 161, 168)); color: var(--ui-badge-error-content, rgb(0, 0, 0)); }
.badge.warning { background: var(--ui-badge-warning-fill, rgb(240, 115, 48)); border-color: var(--ui-badge-warning-stroke, rgb(255, 213, 194)); color: var(--ui-badge-warning-content, rgb(0, 0, 0)); }
.badge.info { background: var(--ui-badge-info-fill, rgb(0, 95, 126)); border-color: var(--ui-badge-info-stroke, rgb(33, 157, 204)); color: var(--ui-badge-info-content, rgb(251, 253, 255)); }
.badge.neutral { background: var(--ui-badge-neutral-fill, rgb(89, 91, 92)); border-color: var(--ui-badge-neutral-stroke, rgb(89, 91, 92)); color: var(--ui-badge-neutral-content, rgb(210, 212, 214)); }

/* ------------------------------ Tree ------------------------------ */
.tree { list-style: none; margin: 0; padding: 0; font-size: 0.875rem; }
.tree ul { list-style: none; margin: 0; padding-left: 1rem; }
.tree .row {
  display: flex; align-items: center; gap: 0.4rem; width: 100%;
  padding: 0.3rem 0.5rem; border: none; background: none; cursor: pointer;
  border-radius: var(--ui-radii-field, 0.5rem); text-align: left;
  color: var(--txt-body);
  transition: background var(--dur) var(--ease);
}
.tree .row:hover { background: var(--ui-menu-item-hover, rgba(111, 189, 100, 0.2)); }
.tree [aria-selected="true"] > .row {
  background: var(--ui-menu-item-selected, rgba(111, 189, 100, 0.2));
  color: var(--txt-primary);
}
.tree .twist { width: 1em; flex: none; color: var(--txt-muted); transition: transform var(--dur) var(--ease); }
.tree [aria-expanded="true"] > .row .twist { transform: rotate(90deg); }
.tree .count { color: var(--txt-muted); font-size: 0.72rem; }
.tree .leaf .twist { visibility: hidden; }
.tree .row .lbl { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Doc/link rows never carry an item count (see Phase 2 rule: counts are
   asset-container-only) and read slightly quieter than container rows. */
.tree .row.is-doc .lbl, .tree .row.is-link .lbl { color: var(--txt-muted); }
.tree .row.is-doc:hover .lbl, .tree .row.is-link:hover .lbl,
.tree [aria-selected="true"] > .row.is-doc .lbl { color: inherit; }

/* --------------------------- Breadcrumbs -------------------------- */
.crumbs { display: flex; align-items: center; flex-wrap: wrap; gap: 0.35rem; margin: 0 0 1rem; font-size: 0.875rem; }
.crumbs a { text-decoration: none; }
.crumbs .sep { color: var(--txt-muted); }
.crumbs .here { color: var(--txt-primary); font-weight: 600; }

/* ------------------------------ Grid ------------------------------ */
.section-head {
  display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap;
  margin: 1.5rem 0 0.75rem;
}
.section-head h2 {
  margin: 0; color: var(--txt-primary);
  font-family: var(--ui-text-heading-3-font-family, inherit), var(--font);
  font-size: var(--ui-text-heading-3-font-size, 1.125rem);
  font-weight: var(--ui-text-heading-3-font-weight, 600);
}
.section-head .meta { color: var(--txt-muted); font-size: 0.8125rem; }
/* Clearable active-filter pill (icon Family chips, first user — see
   renderFamilyFilterPill). Neutral badge palette: informational "this
   filter is on" register, not a status color. Reusable across asset types
   as more filterable dimensions grow their own dismissible pills. */
.filter-pill {
  display: inline-flex; align-items: center; gap: 0.35rem;
  padding: 0.3rem 0.3rem 0.3rem 0.6rem;
  border-radius: var(--ui-badge-radii, 9999px);
  border: var(--ui-badge-stroke-width, 1px) solid var(--ui-badge-neutral-stroke, rgb(89, 91, 92));
  background: var(--ui-badge-neutral-fill, rgb(89, 91, 92));
  color: var(--ui-badge-neutral-content, rgb(210, 212, 214));
  font-size: 0.8125rem;
}
/* 24x24px CSS-pixel hit target (WCAG 2.2 AA 2.5.8) — the box grows via
   padding around the glyph rather than scaling the × up, so the visible
   symbol keeps its optical size inside the enlarged target. */
.filter-pill__clear {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.5rem; height: 1.5rem; padding: 0;
  border: none; border-radius: 50%;
  background: none; color: inherit; font-size: 0.9rem; line-height: 1;
  cursor: pointer;
}
/* No dedicated badge hover token exists in the built tokens (checked
   design-system-tokens/dist/css/tokens.css — --ui-badge-* has no -hover
   variant); mix a fraction of the pill's own content color into
   transparent instead of hardcoding an rgb/black overlay. */
.filter-pill__clear:hover { background: color-mix(in srgb, currentColor 15%, transparent); }
.divider {
  border: none; height: var(--ui-divider-stroke-width, 2px); margin: 1.25rem 0;
  background: var(--ui-gradient-divider-default);
}
/* Icon grid toolbar (Phase 3): two rows of labeled button-bars. Field
   layout (label above control) reuses the .bg-field recipe already used for
   the file-preview stage's background-surface toggle — see that rule's
   selector list further down. */
.grid-toolbar { display: flex; flex-direction: column; gap: 0.85rem; margin: 0 0 1.35rem; }
.tb-row { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 1.5rem; }
/* Grouped grid (Group By != None): header row + divider, no filled section
   band (containers-only rim rule — a stacked grey surface here would
   violate it). Tiles sit directly on the page surface below the divider. */
.grid-group { margin: 1.5rem 0; }
.grid-group__head {
  display: flex; align-items: center; gap: 0.5rem; width: 100%;
  padding: 0.3rem 0; border: none; background: none; cursor: pointer;
  text-align: left; color: var(--txt-primary); font: inherit;
}
.grid-group__twist {
  width: 1em; flex: none; color: var(--txt-muted);
  transition: transform var(--dur) var(--ease);
}
.grid-group__head[aria-expanded="true"] .grid-group__twist { transform: rotate(90deg); }
.grid-group__title {
  font-family: var(--ui-text-heading-3-font-family, inherit), var(--font);
  font-size: var(--ui-text-heading-3-font-size, 1.125rem);
  font-weight: var(--ui-text-heading-3-font-weight, 600);
}
.grid-group__count { color: var(--txt-muted); font-size: 0.8125rem; margin-left: 0.2rem; }
.grid {
  display: grid; gap: 0.6rem;
  grid-template-columns: repeat(auto-fill, minmax(var(--cell, 108px), 1fr));
}
/* Virtualized icon grid (large lists): a full-height spacer holds the scroll
   extent while only the rows intersecting the viewport are materialized into
   the absolutely-positioned inner .grid, translated into place. See
   createVirtualGrid(). */
.vgrid { position: relative; width: 100%; }
.vgrid > .vgrid__grid { position: absolute; top: 0; left: 0; right: 0; will-change: transform; }
/* Inside a virtual grid the DOM only ever holds the ~visible rows, so the
   .cell content-visibility hint gives nothing — and worse, it swaps in the
   110px intrinsic height for any cell that scrolls off, making grid rows
   jump height mid-scroll and breaking the uniform-row-height math (blank
   trailing row). Force real heights here; createVirtualGrid pins
   grid-auto-rows to the measured row height so every row is deterministic. */
.vgrid__grid > .cell { content-visibility: visible; }
/* Tile, grid orientation (components/examples/tile/tile.md) */
.cell {
  position: relative; display: flex; flex-direction: column; align-items: center;
  gap: var(--ui-tile-gap, 0.45rem);
  /* --tile-pad-top overrides the base tile token for icon grids (revision:
     tiles were too small — the top-right status flag/badge overlapped the
     glyph). Threaded in per View Size, see GRID_SIZE_MAP. */
  padding: var(--tile-pad-top, var(--ui-tile-pad-top, 0.5rem)) var(--ui-tile-pad-x, 0.5rem) var(--ui-tile-pad-bottom, 0.6rem);
  background: var(--sfc-raised);
  border: 1px solid transparent;
  border-radius: var(--ui-tile-radii, 0.5rem);
  text-decoration: none; color: var(--txt-body);
  content-visibility: auto; contain-intrinsic-size: var(--cell, 108px) 110px;
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.cell:hover {
  border-color: var(--ui-tile-stroke-hover, rgb(111, 189, 100));
  background: var(--ui-tile-fill-hover, rgba(111, 189, 100, 0.2));
  color: var(--txt-primary);
}
.cell img { width: var(--thumb, 32px); height: var(--thumb, 32px); object-fit: contain; }
[data-mode="dark"] .cell.mono img { filter: var(--mono-invert); }
.cell .nm {
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: var(--ui-tile-label-text-font-family, inherit);
  font-size: var(--ui-tile-label-text-font-size, 0.72rem);
  font-weight: var(--ui-tile-label-text-font-weight, 400);
  line-height: var(--ui-tile-label-text-line-height, 1.3);
  text-align: center;
}
/* Single status slot, top-right corner only (tile.md "Status flag"): every
   condition (avoid/duplicate/no-intent/preferred) renders as the same
   compact badge pill, so a tile never carries two status marks in
   different corners or mixes a bare dot with a labeled pill (revision:
   "preferred" is a real success-badge pill now, not a dot — see
   gridStatusFlag). Needs `!important` on position/z-index because
   `.cell.tile-floating > *` (below) matches every direct child at equal
   specificity and, being later in source order, was winning the cascade —
   it flattened the flag from absolute (corner-pinned) to relative
   (in-flow). */
.cell .flag { position: absolute !important; top: 0.3rem; right: 0.3rem; z-index: 1 !important; }
.cell .flag.badge { height: 1.05rem; min-width: 0; padding: 0 0.4rem; font-size: 0.6rem; }
/* Template-set-only tile anatomy: two quiet corner slots, independent of the
   single .flag status slot above (gridStatusFlag/.flag stays exactly as-is
   for every non-template set — see iconCell). TL carries the corner-suffix
   chip (BR/TL/TR/BL) when the template has one; otherwise TL is free and
   carries the size label instead. TR stacks the no-intent warning badge
   above the size label when a corner chip pushed the size label there and
   the template also lacks intent (badge alone otherwise). Same !important
   corner-pin rationale as .flag above. */
.cell .corner-tl, .cell .corner-tr {
  position: absolute !important; top: 0.3rem; z-index: 1 !important;
  display: flex; flex-direction: column; gap: 0.2rem;
}
.cell .corner-tl { left: 0.3rem; align-items: flex-start; }
.cell .corner-tr { right: 0.3rem; align-items: flex-end; }
/* Corner-suffix chip: neutral badge tokens — quieter than the warning/
   error/success status pills, per the tile anatomy spec. */
.cell .corner-chip {
  display: inline-flex; align-items: center; height: 1.05rem;
  padding: 0 0.4rem; font-size: 0.6rem; font-weight: 600; line-height: 1;
  border-radius: var(--ui-badge-radii, 9999px);
  border: var(--ui-badge-stroke-width, 0.0625rem) solid var(--ui-badge-neutral-stroke, rgb(89, 91, 92));
  background: var(--ui-badge-neutral-fill, rgb(89, 91, 92));
  color: var(--ui-badge-neutral-content, rgb(210, 212, 214));
}
/* Canvas-size label: plain muted tile-label text, quieter still — it never
   competes with the neutral chip or the warning badge it may sit under. */
.cell .corner-size {
  font-family: var(--ui-tile-label-text-font-family, inherit);
  font-size: 0.6rem; line-height: 1; color: var(--txt-muted);
}
.cell .corner-tr .badge { height: 1.05rem; min-width: 0; padding: 0 0.4rem; font-size: 0.6rem; }
/* Two stacked TR items (no-intent badge + size label, corner-suffixed
   templates only — non-corner templates put their size label in TL, so TR
   there is a lone badge) need more headroom above the glyph than the
   single-line case the base --tile-pad-top ramp was tuned for — bumped by
   one dimension step, token-driven, scoped via the adjacent-sibling
   combinator to only the tiles where a badge actually precedes a size label
   (never a lone badge). */
.cell:has(.corner-tr .badge + .corner-size) {
  padding-top: calc(var(--tile-pad-top, var(--ui-tile-pad-top, 0.5rem)) + var(--ui-dimension-m, 1rem));
}
/* Floating tile (examples/tile.md, elevation: floating): shadow + the
   containers-only rim. The icon grid's thumbnails are treated as small
   lifted cards per that doc's floating variant. --tile-pad-top is set per
   View Size (S/M/L, see GRID_SIZE_MAP) so the top-right status flag/badge
   always has clearance above the glyph and never overlaps it — falls back
   to the base tile token when unset (non-icon grids). */
.cell.tile-floating {
  box-shadow: var(--ui-shadow-float, 0 12px 28px rgba(0, 0, 0, 0.35));
}
.cell.tile-floating::before {
  content: ""; position: absolute; inset: 0; z-index: 0; border-radius: inherit; padding: 1px;
  background: var(--ui-gradient-panel-rim, linear-gradient(145deg,
    rgba(251, 253, 255, 0.35) 0%, rgba(251, 253, 255, 0.08) 38%,
    rgba(251, 253, 255, 0.04) 62%, rgba(251, 253, 255, 0.12) 100%));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
}
/* Excludes .flag/.corner-tl/.corner-tr (the status/corner slots) — each sets
   its own position/z-index above with !important so they stay corner-pinned
   regardless of this rule's cascade order relative to their own. */
.cell.tile-floating > *:not(.flag):not(.corner-tl):not(.corner-tr) { position: relative; z-index: 1; }
.foldergrid { display: grid; gap: 0.6rem; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
/* Tile, list orientation (components/examples/tile/tile.md) */
.foldercard {
  display: flex; align-items: center; gap: var(--ui-tile-gap, 0.6rem);
  padding: var(--ui-tile-pad-top, 0.5rem) var(--ui-tile-pad-x, 0.9rem) var(--ui-tile-pad-bottom, 0.6rem);
  background: var(--sfc-raised); border-radius: var(--ui-tile-radii, 0.5rem);
  border: 1px solid transparent; text-decoration: none; color: var(--txt-primary);
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.foldercard:hover {
  border-color: var(--ui-tile-stroke-hover, rgb(111, 189, 100));
  background: var(--ui-tile-fill-hover, rgba(111, 189, 100, 0.2));
  color: var(--txt-primary);
}
.foldercard .count { margin-left: auto; color: var(--txt-muted); font-size: 0.75rem; }
.filelist { list-style: none; margin: 0; padding: 0; }
.filelist li { border-bottom: 1px solid var(--ui-menu-border, rgb(62, 64, 65)); }
.filelist li:last-child { border-bottom: none; }
.filelist a {
  display: flex; align-items: center; gap: 0.75rem; padding: 0.5rem 0.4rem;
  text-decoration: none; color: var(--txt-body);
  border-radius: var(--ui-radii-field, 0.5rem);
  transition: background var(--dur) var(--ease);
}
.filelist a:hover { background: var(--ui-menu-item-hover, rgba(111, 189, 100, 0.2)); color: var(--txt-primary); }
.filelist .ext {
  flex: none; min-width: 3.2rem; text-align: center; padding: 0.15rem 0.4rem;
  border-radius: var(--ui-radii-chip, 0.25rem);
  background: var(--sfc-sunken); color: var(--txt-muted);
  font-family: var(--mono); font-size: 0.7rem; text-transform: uppercase;
}
.filelist .sz { margin-left: auto; color: var(--txt-muted); font-size: 0.78rem; font-family: var(--mono); }
.more { margin: 0.9rem auto 0; display: block; }

/* ----------------------------- Preview ---------------------------- */
.pv-head { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; margin-bottom: 1rem; }
/* Right-aligns the "Edit curation" trigger in the title row — same
   margin-left:auto pattern as .foldercard .count / .na-link elsewhere. */
.pv-head__edit-curation { margin-left: auto; }
.pv-head h1 {
  margin: 0; color: var(--txt-primary); word-break: break-all;
  font-family: var(--ui-text-heading-2-font-family, inherit), var(--font);
  font-size: var(--ui-text-heading-2-font-size, 1.375rem);
  font-weight: var(--ui-text-heading-2-font-weight, 700);
}
/* Actions row: Copy URL (primary) left, Copy SVG source (secondary,
   "Designer use only") right-aligned — see asset-browser-v2-build-plan.md
   Phase 4. */
/* Bottom-align both action groups: the designer group's label sits above
   its button, making that column taller than the primary group's — without
   flex-end here, the two buttons land on different baselines. */
.pv-actions { display: flex; align-items: flex-end; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin: 0.75rem 0 1rem; }
.pv-actions__primary { display: flex; flex-direction: column; gap: 0.4rem; }
.pv-actions__designer { display: flex; flex-direction: column; align-items: flex-end; gap: 0.35rem; }
.pv-actions__designer-label {
  color: var(--ui-action-warning-stroke, rgb(255, 144, 91)); font-size: 0.7rem; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase;
}
/* Restricted-set Copy URL: disabled button + a plain error-text message
   (rejected/templates icons — Phase 4). Plain text, not a filled badge —
   --ui-field-label-error is the design system's standalone error-foreground
   token (same role it plays for field/dropdown validation text). */
.pv-restricted-msg {
  display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;
  margin: -0.5rem 0 1.1rem;
  color: var(--ui-field-label-error, rgb(255, 121, 135));
  font-size: 0.8125rem; font-weight: 600;
}
/* Missing-intent warning callout, above the metadata panels. margin-top
   is the 4px dimension token (revision: Jonathen wanted a small gap above
   the callout, separating it from the button bar immediately above it —
   first spec'd 8px, then revised down to 4px). */
.pv-callout {
  display: flex; align-items: flex-start; gap: 0.6rem; flex-wrap: wrap;
  margin: var(--ui-dimension-2-xs, 4px) 0 1.1rem; padding: 0.6rem 0.9rem;
  border-radius: var(--ui-radii-field, 0.5rem);
  background: var(--ui-badge-warning-fill, rgb(240, 115, 48));
  border: var(--ui-badge-stroke-width, 1px) solid var(--ui-badge-warning-stroke, rgb(255, 213, 194));
  color: var(--ui-badge-warning-content, rgb(0, 0, 0));
  font-size: 0.8125rem;
}
.pv-callout p { margin: 0; flex: 1 1 auto; min-width: 0; }
.pv-callout a { color: inherit; font-weight: 700; text-decoration: underline; }
.pv-callout code { font-family: var(--mono); font-size: 0.85em; }
.stage {
  display: flex; align-items: center; justify-content: center;
  min-height: 240px; padding: 2rem; overflow: auto;
  border-radius: var(--ui-radii-field, 0.5rem);
  background: var(--sfc-sunken);
  border: 1px solid var(--ui-menu-border, rgb(62, 64, 65));
}
/* Contrast surface flips against the active mode: light panel in dark
   mode, dark panel in light mode. */
.stage.bg-contrast { background: rgb(244, 246, 248); color: rgb(20, 24, 28); }
[data-mode="light"] .stage.bg-contrast { background: rgb(20, 24, 28); color: rgb(244, 246, 248); }
.stage.bg-checker {
  background: repeating-conic-gradient(rgba(127,127,127,0.25) 0% 25%, transparent 0% 50%) 0 0 / 20px 20px;
}
/* Opaque app surface: the solid canvas color, no translucency (unlike the
   default sunken surface, which lets the page gradient show through). */
.stage.bg-app { background: var(--sfc-app); }
.stage svg { color: currentColor; }
/* Icon size ramp: bottom-aligned icons, exactly ui.dimension.m (8px) apart;
   labels center under each icon and may overhang narrow ones. */
.svg-ramp {
  display: flex; align-items: flex-end; justify-content: center;
  flex-wrap: wrap; gap: var(--ui-dimension-m, 8px);
}
.svg-spec {
  display: flex; flex-direction: column; align-items: center;
  gap: 0.4rem; flex: none;
}
.svg-spec svg, .svg-spec img { display: block; flex: none; }
.svg-spec .lbl {
  font-family: var(--mono); font-size: 0.7rem; line-height: 1;
  white-space: nowrap; opacity: 0.7;
}
.stage:not(.bg-contrast) { color: var(--txt-primary); }
.stage img.mono-img { filter: none; }
[data-mode="dark"] .stage:not(.bg-contrast) img.mono-img { filter: var(--mono-invert); }
[data-mode="light"] .stage.bg-contrast img.mono-img { filter: var(--mono-invert); }
.stage pre {
  margin: 0; width: 100%; max-height: 480px; overflow: auto;
  font-family: var(--mono); font-size: 0.8125rem; color: var(--txt-body);
  white-space: pre-wrap; word-break: break-word;
}
.stage-tools { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; margin: 0.6rem 0 0; }
.bg-field, .tb-field { display: flex; flex-direction: column; gap: 0.35rem; }
.bg-field__label, .tb-field__label { color: var(--txt-muted); font-size: 0.8125rem; }
.kv { display: grid; grid-template-columns: max-content 1fr; gap: 0.35rem 1rem; margin: 0; font-size: 0.875rem; }
.kv dt { color: var(--txt-muted); }
.kv dd { margin: 0; word-break: break-all; color: var(--txt-body); }
.kv dd code { font-family: var(--mono); font-size: 0.8125rem; }
.chips { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.chip {
  padding: 0.15rem 0.55rem; border-radius: var(--ui-radii-chip, 0.25rem);
  background: var(--sfc-sunken); border: 1px solid var(--ui-menu-border, rgb(62, 64, 65));
  color: var(--txt-body); font-size: 0.75rem;
}
/* Detail-page metadata panels (Icon Details / Usage / File Info): the
   containers-only rim treatment from examples/card, via the shared .panel
   recipe (see .stat-tile/.needs-attention/.doc-content for the same
   "panel + layout modifier class" split). */
/* Icon Details + Usage share a row; File Info stays full-width below it
   (rendered as a separate sibling in showFile). Stacks under the same
   narrow-viewport breakpoint the shell itself collapses at. */
.meta-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-top: 1rem; }
.meta-row .meta-card { margin-top: 0; }
@media (max-width: 700px) {
  .meta-row { grid-template-columns: 1fr; }
}
.meta-card { padding: 1rem 1.1rem; margin-top: 1rem; }
.meta-card h3 {
  margin: 0 0 0.7rem; color: var(--txt-primary); font-size: 0.95rem;
  font-family: var(--ui-text-heading-3-font-family, inherit), var(--font);
  font-weight: var(--ui-text-heading-3-font-weight, 600);
}
.meta-card__sub { margin: 0.9rem 0 0.4rem; color: var(--txt-primary); font-size: 0.85rem; font-weight: 600; }
.pv-url-row { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.pv-url-row .pv-url { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.usecases { margin: 0.4rem 0 0; padding-left: 1.1rem; font-size: 0.85rem; }
.usecases li { margin-bottom: 0.25rem; }
.usecases__source { margin-left: 0.4rem; font-size: 0.6875rem; vertical-align: middle; }
.note-empty { color: var(--txt-muted); font-style: italic; }

/* -------------------- Curate mode: icon edit card ------------------- */
/* Localhost-only inline editor for icon-registry.json overlay entries
   (see scripts/dev-preview-server.mjs --curate + renderIconEditCard in
   app-fresh.js). Swaps in for the read-only .meta-row; reuses .panel
   .meta-card for its own chrome and the .cmp-search/.glossary-filter__input
   field recipe (ui.field.* tokens) for its controls — no new field tokens. */
/* Stacked layout (2026-07-30 mockup, revised): Status and the five CSV list
   fields share one responsive row up top, then the two repeatable-row
   editors — Intention, then use cases (recorded read-only + new rows) — and
   Note last. */
.icon-edit__stack { display: flex; flex-direction: column; gap: 1.1rem; margin: 0 0 1rem; }
/* Column min is 180px, not 150px: the canonical field is 16px type, which
   overflows a 150px track. (2026-07-30: the floated label now stays inside
   the well, so this row no longer needs extra headroom above it.) */
.icon-edit__lists-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 0.85rem 1rem;
}
.icon-edit__section-title { margin: 0 0 0.45rem; }
/* Section header: title on the left, its section-scoped clear action on the
   right (e.g. "Clear intent" — resets the rows only, unlike the card-level
   "Clear entry" in .icon-edit__actions). The title's own bottom margin moves
   to the header row so the two sit on a shared baseline. */
.icon-edit__section-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.6rem; margin: 0 0 0.45rem;
}
.icon-edit__section-head .icon-edit__section-title { margin: 0; }
.icon-edit__section-clear { flex: 0 0 auto; }
.icon-edit__section-hint { margin: 0 0 0.6rem; }
.icon-edit__section--intention {
  border-radius: var(--ui-radii-field, 0.5rem); padding: 0.5rem 0.6rem;
  border: 1px solid transparent;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
/* "shown prominently when status=avoid" — reuses the same error-adjacent
   palette as the avoid badge (.badge.error) elsewhere on this page. */
.icon-edit__section--intention.icon-edit__section--prominent {
  border-color: var(--ui-badge-error-stroke, rgb(255, 161, 168));
  background: var(--ui-badge-error-fill, rgb(255, 121, 135));
}
.icon-edit__section--intention.icon-edit__section--prominent .icon-edit__section-title,
.icon-edit__section--intention.icon-edit__section--prominent .icon-edit__section-hint { color: inherit; }
/* ---- Field / dropdown: the CANONICAL recipes, not a local lookalike ----
   Transcribed from components_mockups/_examples/field/field.html and
   .../dropdown/dropdown.html. The curate card previously hand-rolled its
   inputs against invented variables (--ui-field-fill/-stroke/-pad-y/-radii,
   none of which exist as tokens) and shipped no hover, focus-ring, disabled
   or invalid state. These class names are the example files' own; that is
   safe here because component examples render inside iframes, so app.css
   never reaches them. Markup contract (see fieldWrap/fieldDropdown in
   app.js): input FIRST, label second — the float uses "~".
   2026-07-30 revision: the floated label no longer leaves the well — it
   rises to a reserved line INSIDE the container. See field.html/dropdown.html
   for the full derivation of --field-label-floated-top/--field-value-top. */
.field {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--ui-field-gap);
  height: var(--ui-field-height);
  padding-inline: var(--ui-field-pad-x);
  background: var(--ui-field-bg);
  border: var(--ui-field-border-width) solid var(--ui-field-border);
  border-radius: var(--ui-radii-field);
  transition: border-color var(--ui-motion-duration-fast, .15s) var(--ui-motion-ease-standard, ease);

  --field-label-floated-top: calc(var(--ui-dimension-3-xs) + (var(--ui-field-label-floated-font-size) / 2));
  --field-value-top: calc(2 * var(--ui-dimension-3-xs) + var(--ui-field-label-floated-font-size));
}
.field:hover { border-color: var(--ui-field-border-hover); }
.field:has(.field__input:focus) {
  border-color: var(--ui-field-border-focus);
  box-shadow: var(--ui-focus-ring);
}
.field:has(.field__input:disabled) { background: var(--ui-field-bg-disabled); border-color: var(--ui-field-border); }
.field__input {
  flex: 1;
  min-width: 0;
  height: 100%;
  /* Asymmetric padding reserves the floated label's line above the value —
     constant regardless of density; see --field-value-top above. */
  padding: var(--field-value-top) 0 var(--ui-dimension-2-xs) 0;
  border: none;
  outline: none;
  background: transparent;
  color: var(--ui-text-primary);
  font-family: var(--ui-text-body-default-font-family, inherit), sans-serif;
  font-size: var(--ui-text-body-default-font-size, 1rem);
  line-height: var(--ui-text-body-default-line-height, 1.5);
}
/* The label IS the placeholder — the " " placeholder exists only to drive
   :not(:placeholder-shown), and must never be visible. */
.field__input::placeholder { color: transparent; }
.field__input:disabled { cursor: not-allowed; color: var(--ui-field-content-disabled, var(--ui-text-disabled)); }
.field__label {
  position: absolute;
  left: var(--ui-field-pad-x);
  top: 50%;
  transform: translateY(-50%);
  color: var(--ui-field-label);
  font-family: var(--ui-text-body-default-font-family, inherit), sans-serif;
  font-size: var(--ui-text-body-default-font-size, 1rem);
  pointer-events: none;
  /* transform is NOT in this list by design — only top/font-size/color
     animate; translateY(-50%) stays constant across resting ↔ floated so
     the "top" value alone can interpolate as a length. */
  transition: top var(--ui-motion-duration-quick, .125s) var(--ui-motion-ease-in-out, ease-in-out),
              font-size var(--ui-motion-duration-quick, .125s) var(--ui-motion-ease-in-out, ease-in-out),
              color var(--ui-motion-duration-quick, .125s) var(--ui-motion-ease-in-out, ease-in-out);
}
/* Float on focus, or whenever the input has content (":not(:placeholder-shown)"
   requires a non-empty placeholder attribute, e.g. placeholder=" "). Stays
   INSIDE the well — top moves to a reserved line, transform is untouched. */
.field__input:focus ~ .field__label,
.field__input:not(:placeholder-shown) ~ .field__label {
  top: var(--field-label-floated-top);
  font-family: var(--ui-field-label-floated-font-family, inherit), sans-serif;
  font-size: var(--ui-field-label-floated-font-size, .75rem);
  font-weight: var(--ui-field-label-floated-font-weight, 400);
  /* LOCAL OVERRIDE, not a token change: ui.field.label-floated's own
     line-height is 1.5, which would overlap the value line one reserved
     line below at 12px. Pinned to 1 here — don't change without also
     revisiting --field-value-top above. */
  line-height: 1;
  color: var(--ui-field-label-floated-color);
}
.field__marker { color: var(--ui-field-required-marker); }
.field.is-invalid { border-color: var(--ui-field-border-error); box-shadow: 0 0 0 3px var(--ui-field-glow-required); }
.field.is-invalid:hover { border-color: var(--ui-field-border-hover); box-shadow: 0 0 0 3px var(--ui-field-glow-required); }
.field.is-invalid:has(.field__input:focus) { border-color: var(--ui-field-border-focus); box-shadow: var(--ui-focus-ring), 0 0 0 3px var(--ui-field-glow-required); }
.field.is-invalid .field__label,
.field.is-invalid .field__marker { color: var(--ui-field-label-error); }
/* NOT part of the field anatomy — a section-description paragraph class
   (single dash), reused by .icon-edit__section-hint. Unrelated to the new
   .field__hint (BEM double-underscore) part below; kept as-is. */
.field-hint { color: var(--ui-text-muted); font-size: .75rem; }
/* Hint — value line, inside the well, empty-only. Same font-size as the
   input so nothing shifts when typed content replaces it. */
.field__hint {
  position: absolute;
  left: var(--ui-field-pad-x);
  right: var(--ui-field-pad-x);
  top: var(--field-value-top);
  bottom: var(--ui-dimension-2-xs);
  align-items: center;
  color: var(--ui-field-hint);
  font-family: var(--ui-text-body-default-font-family, inherit), sans-serif;
  font-size: var(--ui-text-body-default-font-size, 1rem);
  line-height: var(--ui-text-body-default-line-height, 1.5);
  pointer-events: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: none;
}
.field__input:placeholder-shown ~ .field__hint { display: flex; }
.field__input:disabled ~ .field__hint { display: none; }
/* Error message — below/outside the well, small gap, distinct color from
   the label-error treatment on the well itself. */
.field__error {
  margin: var(--ui-field-error-gap) 0 0 0;
  color: var(--ui-field-error-text);
  font-family: var(--ui-text-body-default-font-family, inherit), sans-serif;
  font-size: var(--ui-text-body-default-font-size, 1rem);
  line-height: var(--ui-text-body-default-line-height, 1.5);
}
.field-group { display: flex; flex-direction: column; gap: var(--ui-dimension-2-xs); }
/* Local extension: field.md scopes multi-line entry out ("future textarea"),
   so this is the field container with auto height and the label parked
   permanently in its floated position/color — a textarea has no vertical
   center to rest at, so there's no "resting" state to animate from. */
.field--textarea { height: auto; align-items: stretch; padding-block: var(--ui-dimension-xs, 0.5rem); }
.field--textarea .field__input { height: auto; resize: vertical; min-height: 4.5rem; padding-top: 0; }
.field--textarea .field__label {
  top: var(--field-label-floated-top, 0.5rem);
  font-family: var(--ui-field-label-floated-font-family, inherit), sans-serif;
  font-size: var(--ui-field-label-floated-font-size, .75rem);
  font-weight: var(--ui-field-label-floated-font-weight, 400);
  line-height: 1;
  color: var(--ui-field-label-floated-color);
}
/* Dropdown (single-select combobox) — field's container verbatim plus the
   chevron indicator and the ui.menu.* listbox. */
.dropdown { position: relative; }
.dropdown__control {
  display: flex; align-items: center; gap: var(--ui-field-gap);
  width: 100%; min-height: var(--ui-field-height);
  padding-inline: var(--ui-field-pad-x);
  background: var(--ui-field-bg);
  border: var(--ui-field-border-width) solid var(--ui-field-border);
  border-radius: var(--ui-radii-field);
  cursor: pointer;
  transition: border-color var(--ui-motion-duration-fast) var(--ui-motion-ease-standard);

  --field-label-floated-top: calc(var(--ui-dimension-3-xs) + (var(--ui-field-label-floated-font-size) / 2));
  --field-value-top: calc(2 * var(--ui-dimension-3-xs) + var(--ui-field-label-floated-font-size));
  /* Asymmetric block padding reserves the floated label's line above the
     value row — replaces the old symmetric ui.dimension.2XS pair. */
  padding-block: var(--field-value-top) var(--ui-dimension-2-xs);
}
.dropdown__control:hover { border-color: var(--ui-field-border-hover); }
.dropdown__control:focus-visible,
.dropdown__control[aria-expanded="true"] { outline: none; border-color: var(--ui-field-border-focus); box-shadow: var(--ui-focus-ring); }
.dropdown__control[aria-disabled="true"] { border-color: var(--ui-field-border-disabled); cursor: not-allowed; pointer-events: none; }
.dropdown__control[aria-disabled="true"] .dropdown__value,
.dropdown__control[aria-disabled="true"] .field__label,
.dropdown__control[aria-disabled="true"] .dropdown__indicator { color: var(--ui-field-content-disabled); }
.dropdown__value {
  flex: 1; min-width: 0; display: flex; flex-wrap: wrap; gap: var(--ui-field-gap); align-items: center;
  color: var(--ui-text-primary);
  font-family: var(--ui-text-body-default-font-family, inherit), sans-serif;
  font-size: var(--ui-text-body-default-font-size, 1rem);
  line-height: var(--ui-text-body-default-line-height, 1.5);
  min-height: 1.5em;
}
.dropdown__value:empty::before { content: '\00a0'; }
.dropdown__placeholder { color: var(--ui-field-placeholder); }
.dropdown__control.has-value .field__label,
.dropdown__control[aria-expanded="true"] .field__label {
  top: var(--field-label-floated-top);
  font-family: var(--ui-field-label-floated-font-family, inherit), sans-serif;
  font-size: var(--ui-field-label-floated-font-size, .75rem);
  font-weight: var(--ui-field-label-floated-font-weight, 400);
  line-height: 1;
  color: var(--ui-field-label-floated-color);
}
/* Hint — reused verbatim from field, value line inside the well, visible
   only when nothing is selected (mirrors field's "empty" via .has-value
   instead of :placeholder-shown). */
.dropdown__control .field__hint {
  right: calc(var(--ui-field-pad-x) * 2 + 18px);
  display: flex;
}
.dropdown__control.has-value .field__hint { display: none; }
.dropdown__control[aria-disabled="true"] .field__hint { display: none; }
/* Required + error — stroke/glow inherited from field unchanged; the label
   uses ui.field.label-error same as field. */
.dropdown__control.is-invalid { border-color: var(--ui-field-border-error); box-shadow: 0 0 0 3px var(--ui-field-glow-required); }
.dropdown__control.is-invalid .field__label { color: var(--ui-field-label-error); }
.dropdown-group { display: flex; flex-direction: column; gap: var(--ui-dimension-2-xs); }
.dropdown__indicator { display: inline-flex; align-items: center; color: var(--ui-text-muted); flex-shrink: 0; }
.dropdown__indicator svg { width: 18px; height: 18px; }
.dropdown__indicator .chev-up { display: none; }
.dropdown__control[aria-expanded="true"] .dropdown__indicator .chev-down { display: none; }
.dropdown__control[aria-expanded="true"] .dropdown__indicator .chev-up { display: inline-flex; }
.dropdown__menu {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0;
  z-index: var(--ui-zindex-dropdown);
  margin: 0; padding: var(--ui-dimension-2-xs) 0;
  list-style: none;
  background: var(--ui-menu-bg);
  border-radius: var(--ui-radii-field);
  box-shadow: var(--ui-shadow-overlay);
  max-height: 224px; overflow-y: auto;
}
/* Panel rim, containers only (card.md) — the option rows never carry it. */
.dropdown__menu::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit; padding: 1px;
  background: var(--ui-gradient-panel-rim);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor; mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); mask-composite: exclude;
  pointer-events: none;
}
.dropdown__option {
  padding: var(--ui-dimension-xs) var(--ui-field-pad-x) var(--ui-dimension-xs-bottom);
  color: var(--ui-text-body);
  font-family: var(--ui-text-body-default-font-family, inherit), sans-serif;
  font-size: var(--ui-text-body-default-font-size, 1rem);
  border-bottom: 1px solid var(--ui-menu-border);
  cursor: pointer;
}
.dropdown__option:last-child { border-bottom: none; }
.dropdown__option:hover,
.dropdown__option.is-active { background: var(--ui-menu-item-hover); }
.dropdown__option[aria-selected="true"] { background: var(--ui-menu-item-selected); }
.icon-edit__errors {
  margin: 0 0 1rem; padding: 0.6rem 0.9rem;
  border-radius: var(--ui-radii-field, 0.5rem);
  background: var(--ui-badge-error-fill, rgb(255, 121, 135));
  border: var(--ui-badge-stroke-width, 1px) solid var(--ui-badge-error-stroke, rgb(255, 161, 168));
  color: var(--ui-badge-error-content, rgb(0, 0, 0));
  font-size: 0.8125rem;
}
.icon-edit__errors p { margin: 0 0 0.25rem; }
.icon-edit__errors p:last-child { margin-bottom: 0; }
.icon-edit__actions { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
/* Use cases: repeatable row editor (see addUseCaseRow in app.js). Each row
   is its own mini field-grid — app/purpose/component/addedBy/date plus a
   remove control — inside the wide "Use cases" field slot. */
.icon-edit__usecases { display: flex; flex-direction: column; gap: 0.6rem; margin-top: 0.35rem; }
.icon-edit__usecase-row {
  display: grid;
  grid-template-columns: repeat(3, minmax(160px, 1fr)) minmax(150px, 0.8fr) minmax(150px, 0.7fr) auto;
  gap: 0.75rem; align-items: end;
  padding: 0.7rem;
  border: 1px solid var(--ui-field-border);
  border-radius: var(--ui-radii-field);
  background: var(--sfc-raised);
}
/* Same 24x24 CSS-pixel hit-target recipe as .filter-pill__clear (WCAG 2.2
   AA 2.5.8): the box is sized via padding, not by scaling the × glyph. */
.icon-edit__usecase-remove {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.5rem; height: 1.5rem; padding: 0; align-self: end;
  border: none; border-radius: 50%;
  background: none; color: var(--txt-muted); font-size: 0.9rem; line-height: 1;
  cursor: pointer;
}
.icon-edit__usecase-remove:hover, .icon-edit__usecase-remove:focus-visible {
  background: color-mix(in srgb, currentColor 15%, transparent);
  color: var(--txt-body); outline: none;
}
.icon-edit__usecase-empty { margin: 0; }
/* Shared "Add …" control for both repeatable-row editors. The sections are
   block containers, so the standoff from the row stack above is a margin,
   not a flex gap (align-self is a no-op here but harmless if a section ever
   becomes a flex column). */
.icon-edit__usecase-add { align-self: flex-start; margin-top: 0.75rem; }
/* Intention: repeatable row editor (see addIntentionRow in app.js) — same
   chrome recipe as the use-case rows, but each row is a Replacement Icon
   (optional; icon-less row = the entry's own intent) plus its Intention
   (reuses .icon-edit__usecase-remove / -add for the controls). Icon-row
   order is meaningful: the first icon row is the default target. */
.icon-edit__intentions { display: flex; flex-direction: column; gap: 0.6rem; margin-top: 0.35rem; }
.icon-edit__intention-row {
  display: grid;
  grid-template-columns: minmax(180px, 1fr) minmax(180px, 1fr) auto;
  gap: 0.75rem; align-items: end;
  padding: 0.7rem;
  border: 1px solid var(--ui-field-border);
  border-radius: var(--ui-radii-field);
  background: var(--sfc-raised);
}
/* Recorded use cases: read-only (removable) rows above the New use case
   editor — ideally scanner-written; badge marks auto vs manual provenance. */
.icon-edit__usecases-existing { display: flex; flex-direction: column; gap: 0.45rem; }
.icon-edit__usecase-existing {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.45rem 0.7rem;
  border: 1px solid var(--ui-field-border);
  border-radius: var(--ui-radii-field);
  background: var(--sfc-raised);
  font-size: 0.8125rem;
}
.icon-edit__usecase-existing .icon-edit__usecase-text { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.icon-edit__usecase-badge { flex: 0 0 auto; }
.toast {
  position: fixed; bottom: 1.25rem; left: 50%; transform: translateX(-50%) translateY(0.5rem);
  padding: 0.55rem 1rem; border-radius: var(--ui-radii-field, 0.5rem);
  background: var(--ui-menu-bg, rgb(37, 38, 40)); color: var(--txt-primary);
  box-shadow: var(--ui-shadow-overlay, 0 16px 48px rgba(0, 0, 0, 0.5));
  opacity: 0; pointer-events: none; z-index: var(--ui-zindex-dropdown, 200);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.error-card { padding: 1rem 1.2rem; border-radius: var(--ui-radii-field, 0.5rem); background: var(--sfc-raised); }
.error-card code { font-family: var(--mono); }

/* ------------------------------ Home ------------------------------- */
.home-intro { color: var(--txt-muted); margin: -0.5rem 0 1rem; max-width: 60ch; }
.stat-grid {
  display: grid; gap: 0.75rem; margin: 0.25rem 0 1.5rem;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
}
/* Stat tile (examples/stat-tile): non-interactive KPI readout. Container
   chrome borrows the same .panel rim/glass recipe used everywhere else in
   this file — examples/stat-tile.md flags dedicated ui.stat-tile.* container
   tokens (border/radius/padding/elevation) as a gap, so this reuses the
   confirmed containers-only card treatment rather than inventing new chrome. */
.stat-tile {
  padding: 1rem 1.1rem 1.3rem; display: flex; flex-direction: column; gap: 0.2rem;
}
.stat-tile__value {
  font-family: var(--ui-stat-tile-value-text-font-family, inherit), var(--font);
  font-size: var(--ui-stat-tile-value-text-font-size, 2rem);
  font-weight: var(--ui-stat-tile-value-text-font-weight, 700);
  line-height: var(--ui-stat-tile-value-text-line-height, 1.1);
  color: var(--txt-primary);
}
.stat-tile__label {
  font-family: var(--ui-stat-tile-label-text-font-family, inherit), var(--font);
  font-size: var(--ui-stat-tile-label-text-font-size, 0.75rem);
  font-weight: var(--ui-stat-tile-label-text-font-weight, 700);
  line-height: var(--ui-stat-tile-label-text-line-height, 1.3);
  letter-spacing: var(--ui-stat-tile-label-text-letter-spacing, 0.04em);
  text-transform: var(--ui-stat-tile-label-text-text-case, uppercase);
  color: var(--txt-muted);
}
.stat-tile__sublabel {
  font-size: var(--ui-text-body-small-font-size, 0.78rem);
  color: var(--txt-muted);
}
.stat-tile__underline {
  display: block; margin-top: 0.5rem; height: 0.2rem; border-radius: var(--ui-radii-chip, 0.25rem);
  background: var(--ui-stat-tile-accent-teal-underline, var(--accent));
}
.stat-tile--blue .stat-tile__underline { background: var(--ui-stat-tile-accent-blue-underline, var(--accent)); }
.stat-tile--purple .stat-tile__underline { background: var(--ui-stat-tile-accent-purple-underline, var(--accent)); }
.stat-tile--gold .stat-tile__underline { background: var(--ui-stat-tile-accent-gold-underline, var(--accent)); }

.needs-attention { padding: 1.1rem 1.25rem 1.25rem; }
.na-title {
  margin: 0 0 0.75rem; color: var(--txt-primary);
  font-family: var(--ui-text-heading-3-font-family, inherit), var(--font);
  font-size: var(--ui-text-heading-3-font-size, 1.125rem);
  font-weight: var(--ui-text-heading-3-font-weight, 600);
}
.na-row { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; margin: 0.5rem 0; }
.na-text { color: var(--txt-body); font-size: 0.875rem; }
.na-link { margin-left: auto; font-size: 0.8125rem; text-decoration: none; white-space: nowrap; }

/* ------------------------------- Docs ------------------------------ */
/* Card uses most of the reading pane; prose blocks keep a readable measure
   (~90ch) while wide media (diagrams, tables, images, code) fill the card. */
.doc-content { padding: 1.25rem 1.5rem; max-width: min(100%, 1120px); margin-inline: auto; }
/* When a section doc is split into per-section cards, space the stack. */
.doc-content + .doc-content { margin-top: 1.25rem; }
.doc-content .doc-p, .doc-content .doc-h, .doc-content .doc-list, .doc-content .doc-quote { max-width: 90ch; }
.doc-content .doc-h {
  color: var(--txt-primary); line-height: 1.2; letter-spacing: -0.01em;
  font-family: var(--ui-text-heading-3-font-family, inherit), var(--font);
  font-weight: var(--ui-text-heading-3-font-weight, 700);
}
/* Renderer emits h{level+1}: '#'->h2, '##'->h3, '###'->h4. Give each a
   distinct step + rhythm so long docs are scannable. */
.doc-content h2.doc-h {
  font-size: 1.6rem; margin: 2.3rem 0 1rem; padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--ui-border-subtle);
}
.doc-content h3.doc-h { font-size: 1.2rem; margin: 1.9rem 0 0.6rem; }
.doc-content h4.doc-h {
  font-size: 0.82rem; margin: 1.5rem 0 0.5rem; color: var(--accent);
  text-transform: uppercase; letter-spacing: 0.06em;
}
.doc-content .doc-h:first-child { margin-top: 0; }
.doc-content .doc-p { color: var(--txt-body); line-height: 1.55; margin: 0 0 0.85rem; }
.doc-content .doc-p code, .doc-content li code, .doc-content th code,
.doc-content td code, .doc-content .doc-h code {
  font-family: var(--mono);
  font-size: var(--ui-code-font-size, 0.875em); font-weight: var(--ui-code-font-weight, 500);
  color: var(--ui-code-text, var(--accent));
  background: var(--ui-code-fill, var(--sfc-sunken));
  border: 1px solid var(--ui-code-stroke, transparent);
  border-radius: var(--ui-radii-chip, 0.25rem); padding: 0.05rem 0.32rem;
}
.doc-content .doc-list { color: var(--txt-body); line-height: 1.6; margin: 0 0 0.85rem; padding-left: 1.3rem; }
.doc-content .doc-list li { margin-bottom: 0.3rem; }
.doc-content .doc-list li::marker { color: var(--accent); }
/* Nested lists: no extra block margin (they sit inside an <li>), and a
   per-depth marker shape so nesting is legible without relying on indent
   alone. Markers step down in emphasis as depth increases. */
.doc-content .doc-list .doc-list { margin: 0.3rem 0 0; }
.doc-content ul.doc-list { list-style: disc; }
.doc-content ul.doc-list ul.doc-list { list-style: circle; }
.doc-content ul.doc-list ul.doc-list ul.doc-list { list-style: square; }
.doc-content .doc-list .doc-list li::marker { color: var(--txt-muted); }
/* Corrective, not decorative. markdown-it wraps blockquote content and LOOSE
   list-item content in <p class="doc-p"> (correct CommonMark structure; the
   old hand-rolled renderer dropped the wrapper). .doc-p's bottom margin is
   right between paragraphs but reads as stray trailing space at the end of a
   quote or a list item, so drop it on the last one only. */
.doc-content .doc-quote > .doc-p:last-child { margin-bottom: 0; }
.doc-content .doc-list > li > .doc-p:last-child { margin-bottom: 0; }
/* Strikethrough (~~text~~) — parseable for the first time, so it had no
   styling. markdown-it's GFM strikethrough emits <s>; <del> is listed too so
   the rule holds if the markup ever changes. Line-through is the semantic of
   the element itself, not a themed value; the de-emphasis colour is the
   existing muted text tier. */
.doc-content s, .doc-content del { text-decoration: line-through; color: var(--txt-muted); }
/* Hard line breaks (two trailing spaces) also render for the first time.
   <br> needs no rule of its own: it inherits the line-height of whichever
   .doc-p / li / th / td it sits in, which is the intended rhythm. */
/* Inline links (renderer emits bare <a>): accent with a hover underline. */
.doc-content a {
  color: var(--accent); text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  transition: border-color var(--dur) var(--ease);
}
.doc-content a:hover { border-bottom-color: var(--accent); }
.doc-content a:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: 2px; }
.doc-content .doc-quote {
  margin: 0 0 1rem; padding: 0.7rem 1.1rem; color: var(--txt-body);
  background: var(--sfc-sunken);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--ui-radii-field, 0.5rem) var(--ui-radii-field, 0.5rem) 0;
}
.doc-content .doc-code {
  margin: 0 0 1rem; padding: 0.85rem 1rem; overflow-x: auto;
  background: var(--sfc-sunken);
  border: 1px solid var(--ui-menu-border, rgb(62, 64, 65));
  border-radius: var(--ui-radii-field, 0.5rem);
  font-family: var(--mono); font-size: 0.8125rem; line-height: 1.6; color: var(--txt-primary);
}
.doc-content .doc-mermaid {
  display: block; margin: 0 0 0.85rem; padding: 0.85rem 1rem; overflow-x: auto;
  background: transparent; border-radius: var(--ui-radii-field, 0.5rem);
}
.doc-content .doc-mermaid svg { width: 100%; max-width: 100%; height: auto; background: transparent; }
/* Edge colour/width/dash are handled inside mermaid via themeVariables.lineColor
   and themeCSS (see initMermaidTheme) rather than overridden from here. */
/* hover-swap trigger nodes are keyboard-toggle controls: show a focus ring for
   keyboard only (SVG can't render the box-shadow --focus-ring, so use outline),
   and suppress the stray UA outline on mouse click. */
.doc-content .doc-mermaid g.node[tabindex]:focus { outline: none; }
.doc-content .doc-mermaid g.node[tabindex]:focus-visible {
  outline: 3px solid var(--ui-action-standard-bg, rgb(80, 157, 69)); outline-offset: 2px;
}
/* Subgraph container titles: mermaid's default renders them faint on the
   glass panels. Force full-contrast, bold text. */
.doc-content .doc-mermaid .cluster-label,
.doc-content .doc-mermaid .cluster-label span,
.doc-content .doc-mermaid .cluster-label p {
  color: var(--txt-primary) !important; font-weight: 700; font-size: 0.9rem;
}
.doc-content .doc-mermaid-error { margin: 0.4rem 0 0.85rem; font-size: 0.8125rem; color: var(--txt-muted); }
.doc-content .doc-table {
  border-collapse: collapse; width: 100%; margin: 0 0 1.1rem; font-size: 0.85rem;
  border: 1px solid var(--ui-menu-border, rgb(62, 64, 65));
  border-radius: var(--ui-radii-field, 0.5rem); overflow: hidden;
}
.doc-content .doc-table th, .doc-content .doc-table td {
  text-align: left; padding: 0.5rem 0.75rem; border-bottom: 1px solid var(--ui-menu-border, rgb(62, 64, 65));
  vertical-align: top;
}
.doc-content .doc-table th {
  color: var(--txt-primary); font-weight: 700; background: var(--sfc-raised);
  font-family: var(--ui-text-heading-3-font-family, inherit), var(--font);
}
.doc-content .doc-table td { color: var(--txt-body); }
.doc-content .doc-table tbody tr:nth-child(even) td { background: rgba(255, 255, 255, 0.02); }
.doc-content .doc-table tbody tr:hover td { background: var(--ui-menu-item-hover, rgba(111, 189, 100, 0.12)); }
.doc-content .doc-table tbody tr:last-child td { border-bottom: none; }
.doc-empty { display: flex; flex-direction: column; align-items: flex-start; gap: 0.6rem; }
/* Icon Registry table (Phase 5): same recipe as .doc-content .doc-table
   (border/color tokens), duplicated unscoped since this table lives inside
   a .meta-card, not .doc-content. Adds a token-driven row-hover affordance;
   header sort buttons pick up focus-visible/font/color for free from the
   global button/:focus-visible rules near the top of this stylesheet. */
.reg-table { border-collapse: collapse; width: 100%; margin: 0; font-size: 0.85rem; }
.reg-table th, .reg-table td {
  text-align: left; padding: 0.45rem 0.7rem; border-bottom: 1px solid var(--ui-menu-border, rgb(62, 64, 65));
  vertical-align: top;
}
.reg-table th { color: var(--txt-primary); font-weight: 700; }
.reg-table td { color: var(--txt-body); }
.reg-table tbody tr:hover td { background: var(--ui-menu-item-hover, rgba(111, 189, 100, 0.2)); }

/* --------------------------- Doc images (v3) ----------------------- */
.doc-content .doc-img {
  display: block; max-width: 100%; height: auto;
  border-radius: var(--ui-radii-field, 0.5rem);
  margin: 0.25rem 0 0.9rem;
}

/* ---------------------- Hub pages (v3 Phase 2) ---------------------- */
.hub-intro { margin-bottom: 1.25rem; }
.hub-grid {
  display: grid; gap: var(--ui-tile-gap, 0.75rem); margin: 0 0 1.5rem;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
}
/* Floating tile (examples/tile.md elevation: floating) reused with a
   richer content layout (title/description/meta) instead of an icon glyph —
   same precedent as .foldercard varying tile content while keeping the
   tile tokens (see that rule above). */
.hub-tile {
  position: relative; display: flex; flex-direction: column; gap: 0.35rem;
  min-height: 6rem; text-decoration: none; color: var(--txt-body);
  background: var(--sfc-raised);
  border: 1px solid transparent; border-radius: var(--ui-tile-radii, 0.5rem);
  padding: var(--ui-tile-pad-top, 0.85rem) var(--ui-tile-pad-x, 1rem) var(--ui-tile-pad-bottom, 0.9rem);
  box-shadow: var(--ui-shadow-float, 0 12px 28px rgba(0, 0, 0, 0.35));
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.hub-tile::before {
  content: ""; position: absolute; inset: 0; z-index: 0; border-radius: inherit; padding: 1px;
  background: var(--ui-gradient-panel-rim, linear-gradient(145deg,
    rgba(251, 253, 255, 0.35) 0%, rgba(251, 253, 255, 0.08) 38%,
    rgba(251, 253, 255, 0.04) 62%, rgba(251, 253, 255, 0.12) 100%));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
}
.hub-tile > * { position: relative; z-index: 1; }
.hub-tile:hover, .hub-tile:hover .hub-tile__title {
  border-color: var(--ui-tile-stroke-hover, rgb(111, 189, 100));
  background: var(--ui-tile-fill-hover, rgba(111, 189, 100, 0.2));
  color: var(--txt-primary);
}
.hub-tile__title {
  font-family: var(--ui-text-heading-3-font-family, inherit), var(--font);
  font-size: 0.95rem; font-weight: var(--ui-text-heading-3-font-weight, 600);
  color: var(--txt-primary);
}
.hub-tile__desc { color: var(--txt-muted); font-size: 0.8125rem; line-height: 1.4; flex: 1 1 auto; }
.hub-tile__meta { color: var(--txt-muted); font-size: 0.72rem; }
.hub-tile__badge { align-self: flex-start; }
/* v4: per-framework availability chips on a Components-index tile (subtle —
   .badge.neutral's palette at a much smaller scale, since the full .badge
   sizing reads oversized next to hub-tile__meta's small text). Omitted from
   the DOM entirely when a component has no framework artifacts. */
.hub-tile__fw { display: flex; gap: 0.3rem; margin-top: 0.4rem; }
.fw-chip {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.3rem; height: 1.1rem; padding: 0 0.35rem;
  border-radius: var(--ui-badge-radii, 9999px);
  border: 1px solid var(--ui-badge-neutral-stroke, rgb(89, 91, 92));
  background: var(--ui-badge-neutral-fill, rgb(89, 91, 92));
  color: var(--ui-badge-neutral-content, rgb(210, 212, 214));
  font-size: 0.62rem; font-weight: 600; line-height: 1;
}
.hub-tile--empty { opacity: 0.6; box-shadow: none; }
.hub-callout { margin-top: 0.5rem; }
/* Components index (v4): live preview thumbnails on tiles + a filter box,
   mirroring the icon grid's "Previews" gallery. The tile stays a single <a>;
   the scaled example iframe is pointer-events:none so clicks fall through to
   the anchor. The body wrapper restores tile padding the preview box drops. */
/* Fixed height so every virtualized row is uniform (createVirtualGrid pins
   grid-auto-rows to a single measured cell — see that function). */
.hub-tile--preview { padding: 0; overflow: hidden; gap: 0; height: 238px; }
.hub-tile__preview {
  position: relative; width: 100%; height: 150px; overflow: hidden;
  background: var(--sfc-sunken, var(--sfc-raised));
  border-radius: var(--ui-tile-radii, 0.5rem) var(--ui-tile-radii, 0.5rem) 0 0;
  border-bottom: 1px solid var(--ui-border-subtle);
}
.hub-tile__preview iframe {
  position: absolute; top: 0; left: 0; border: 0;
  transform-origin: top left; pointer-events: none; background: transparent;
}
.hub-tile__preview-note {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  color: var(--txt-muted); font-size: 0.75rem; text-align: center; padding: 0.5rem;
}
.hub-tile__body {
  box-sizing: border-box; height: 88px; overflow: hidden;
  display: flex; flex-direction: column; gap: 0.35rem;
  padding: var(--ui-tile-pad-top, 0.85rem) var(--ui-tile-pad-x, 1rem) var(--ui-tile-pad-bottom, 0.9rem);
}
/* Components virtualized gallery: fixed-width columns (not the icon ramp's
   stretch-to-fill minmax) so each preview iframe scales by a constant factor
   — no per-cell resize math as rows recycle on scroll. */
.cmp-vgrid { grid-template-columns: repeat(auto-fill, var(--cell, 280px)); justify-content: start; }
.cmp-search { margin: 0 0 1.25rem; max-width: 22rem; }
.cmp-search input {
  width: 100%; box-sizing: border-box;
  padding: var(--ui-field-pad-y, 0.5rem) var(--ui-field-pad-x, 0.75rem);
  color: var(--ui-field-content, var(--txt-body));
  background: var(--ui-field-fill, var(--sfc-raised));
  border: 1px solid var(--ui-field-stroke, rgba(255, 255, 255, 0.14));
  border-radius: var(--ui-field-radii, 0.5rem);
  font: inherit;
}
.cmp-search input::placeholder { color: var(--ui-field-placeholder, var(--txt-muted)); }
.cmp-search input:focus-visible {
  outline: none;
  border-color: var(--ui-field-stroke-focus, rgb(111, 189, 100));
}

/* ------------------ Section doc TOC (v3 Phase 3) -------------------- */
/* Chip row on wide viewports, collapsible <details> dropdown below the
   `M` breakpoint (1024px, per taxonomy-sizing.md) — --ui-menu-*/--ui-link-*
   patterns, no new tokens. */
.doc-toc { margin: 0 0 1.25rem; }
.doc-toc__summary {
  cursor: pointer; list-style: none; display: inline-flex; align-items: center; gap: 0.35rem;
  color: var(--txt-muted); font-size: 0.8125rem; padding: 0.35rem 0.6rem;
  border: 1px solid var(--ui-menu-border, rgb(62, 64, 65)); border-radius: var(--ui-radii-field, 0.5rem);
}
.doc-toc__summary::-webkit-details-marker { display: none; }
.doc-toc__summary::before { content: '▸'; font-size: 0.7em; transition: transform var(--dur) var(--ease); }
.doc-toc[open] .doc-toc__summary::before { transform: rotate(90deg); }
.doc-toc__list {
  display: none; flex-direction: column; gap: 0.15rem; margin-top: 0.4rem; max-width: 22rem;
  padding: 0.5rem; background: var(--ui-menu-bg, rgb(37, 38, 40));
  border: 1px solid var(--ui-menu-border, rgb(62, 64, 65)); border-radius: var(--ui-radii-field, 0.5rem);
}
.doc-toc[open] .doc-toc__list { display: flex; }
.doc-toc__chip {
  padding: 0.3rem 0.5rem; border-radius: var(--ui-radii-field, 0.5rem);
  color: var(--ui-link-enabled, var(--accent)); text-decoration: none; font-size: 0.8125rem;
}
.doc-toc__chip:hover { background: var(--ui-menu-item-hover, rgba(111, 189, 100, 0.2)); color: var(--ui-link-hover, var(--accent-hover)); }
@media (min-width: 1024px) {
  .doc-toc__summary { display: none; }
  .doc-toc__list {
    display: flex !important; flex-direction: row; flex-wrap: wrap;
    background: none; border: none; padding: 0; max-width: none; margin-top: 0;
  }
  .doc-toc__chip { border: 1px solid var(--ui-menu-border, rgb(62, 64, 65)); }
}

/* ------------------- Prev/next + hub link (v3 Phase 3) -------------- */
.doc-prevnext {
  margin: 2rem auto 0; padding-top: 1rem; max-width: min(100%, 1120px);
  border-top: 1px solid var(--ui-menu-border, rgb(62, 64, 65));
  display: flex; flex-direction: column; gap: 0.75rem;
}
.doc-prevnext a { color: var(--ui-link-enabled, var(--accent)); text-decoration: none; font-size: 0.875rem; }
.doc-prevnext a:hover { color: var(--ui-link-hover, var(--accent-hover)); text-decoration: underline; }
.doc-prevnext__row { display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }

/* -------------------- Glossary filter (v3 Phase 3) ------------------ */
.glossary-filter {
  display: flex; align-items: center; gap: var(--ui-field-gap, 0.5rem);
  height: var(--ui-field-height, 2.75rem); padding-inline: var(--ui-field-pad-x, 0.9rem);
  background: var(--ui-field-bg, rgba(251, 253, 255, 0.05));
  border: var(--ui-field-border-width, 1px) solid var(--ui-field-border, rgb(117, 119, 121));
  border-radius: var(--ui-radii-field, 0.5rem);
  margin: 0 0 1.1rem; max-width: 26rem;
}
.glossary-filter:hover { border-color: var(--ui-field-border-hover, rgb(147, 149, 151)); }
.glossary-filter:has(.glossary-filter__input:focus) { border-color: var(--ui-field-border-focus, var(--accent)); }
.glossary-filter__input {
  flex: 1; min-width: 0; background: none; border: none; outline: none;
  color: var(--txt-primary); font: inherit;
}
.glossary-filter__input::placeholder { color: var(--ui-field-placeholder, var(--txt-muted)); }
.glossary-count { color: var(--txt-muted); font-size: 0.7em; font-weight: 400; }

/* ------------- Component example pages (v3 Phase 4) ----------------- */
.cmp-tools { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin: 0 0 1rem; }
.cmp-standalone { text-decoration: none; }
/* Sized to the content region via a viewport calc (header + crumbs/title/
   tools budget) rather than polled against the iframe's scrollHeight (hard
   constraint) — the example scrolls within this box, the outer page does
   not grow with it. Approximate, not pixel-perfect: the crumbs/title/tools
   block above it is variable height, so the calc uses a fixed budget for
   that zone rather than measuring it live. */
.cmp-frame-wrap {
  position: relative; overflow: hidden;
  height: calc(100vh - 16rem); min-height: 420px;
  border-radius: var(--ui-radii-card, 1rem);
  background: var(--sfc-sunken);
  border: 1px solid var(--ui-menu-border, rgb(62, 64, 65));
}
.cmp-frame-wrap iframe { display: block; width: 100%; height: 100%; border: 0; background: transparent; }
.cmp-frame-wrap--degraded { height: auto; min-height: 0; background: none; border: none; }
.cmp-degraded { display: flex; flex-direction: column; align-items: flex-start; gap: 0.85rem; }

@media (prefers-reduced-motion: reduce) {
  /* .button-bar__indicator is excluded: its own reduced-tier rule (above,
     near .is-switching) needs its real ui.motion.duration.fast crossfade
     to survive — this blanket kill-switch would otherwise force it to
     0.01ms and erase the tonal fade the component defines for this tier. */
  *:not(.button-bar__indicator), *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

/* ==================== Primitive Colors page ======================= */
/* Bespoke doc view built by showPrimitiveColors() in app.js from
   design-system-tokens/dist/json/primitives.json. Appearance lives here, in
   ui.* tokens, exactly like every other view — the JS sets only the three
   things that ARE the data: a swatch's background (the authored oklch), its
   label colour (the build-resolved WCAG-passing ink), and an alpha value. */
/* No .panel and no .doc-content on this page (see showPrimitiveColors): the
   content sits directly on #main-panel's glass, the same surface as the
   breadcrumb and <h1>. Only a small top gap is needed to clear the title —
   #main-panel already supplies the outer padding. */
.pc-page { display: flex; flex-direction: column; gap: 1.75rem; }
.pc-lede { color: var(--ui-text-body); max-width: 68ch; margin: 0; }
.pc-loading { color: var(--ui-text-muted); margin: 0; }

.pc-legend {
  display: flex; flex-direction: column; gap: 0.5rem;
  padding: 0.875rem 1rem;
  background: var(--ui-surface-sunken);
  border: 1px solid var(--ui-border-subtle);
  border-radius: var(--ui-radii-card);
}
.pc-legend p {
  margin: 0; max-width: 78ch;
  color: var(--ui-text-muted);
  font-size: var(--ui-text-body-small-font-size);
  line-height: 1.5;
}

.pc-set { display: flex; flex-direction: column; gap: 0.75rem; }
.pc-set__title {
  margin: 0;
  font-family: var(--ui-text-heading-2-font-family);
  font-size: var(--ui-text-heading-2-font-size);
  font-weight: var(--ui-text-heading-2-font-weight);
  color: var(--ui-text-primary);
}
.pc-set__note { margin: 0; max-width: 78ch; color: var(--ui-text-body); }
.pc-set__note--sub { color: var(--ui-text-muted); font-size: var(--ui-text-body-small-font-size); }
.pc-set__src { margin: 0; }
.pc-set__src code {
  font-family: var(--ui-font-mono);
  font-size: var(--ui-code-font-size);
  color: var(--ui-code-text);
  background: var(--ui-code-fill);
  border: 1px solid var(--ui-code-stroke);
  border-radius: var(--ui-radii-chip);
  padding: 0.1rem 0.4rem;
}

/* A row holds one or more labelled groups. The GROUP’S CARDS are the scroll
   container — deliberately not the row and not the group. The heading has to
   sit outside the scroller, or it slides away the moment you scroll a 13-stop
   ramp and you lose track of which ramp you are reading. Scrolling the cards
   alone keeps the heading pinned while the ramp still moves as one continuous
   light-to-dark progression (wrapping it into two lines would destroy that
   reading, which is why this scrolls rather than wraps).
   The 2rem row gap is what separates two groups sharing a row (LINQX Primary
   / Secondary) — wide enough to read as two clusters, tight enough to read as
   one identity block. */
.pc-row { display: flex; gap: 2rem; align-items: flex-start; }
/* shrink-to-fit + min-width:0 so a group never forces the row wider than the
   content area: a lone group narrows to the row and scrolls its cards inside,
   and two paired groups keep their natural width until the viewport is too
   narrow, then shrink and scroll independently rather than clipping. */
.pc-group { flex: 0 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 0.375rem; }
.pc-group__name {
  display: flex; align-items: baseline; gap: 0.5rem; margin: 0.25rem 0 0;
  font-family: var(--ui-text-heading-3-font-family);
  font-size: var(--ui-text-heading-3-font-size);
  font-weight: var(--ui-text-heading-3-font-weight);
  color: var(--ui-text-primary);
  white-space: nowrap;
}
.pc-group__count {
  font-family: var(--ui-font-family);
  font-size: var(--ui-text-body-small-font-size);
  font-weight: 400;
  color: var(--ui-text-muted);
}
.pc-group__cards {
  display: flex; gap: 2px;
  overflow-x: auto; overflow-y: hidden;
  padding-bottom: 0.375rem;
  scrollbar-width: thin;
}
.pc-group__cards > * { flex: 0 0 auto; }

.pc-card {
  /* 200px is the measured floor: the longest authored value is
     `oklch(0.8536 0.090775 229.72)` at 29 characters, which needs ~174px at
     this mono size plus the 16px of padding. Narrower and the oklch clips —
     which it did at 176px, on 193 of the 212 cards. */
  width: 200px; min-height: 116px;
  display: flex; flex-direction: column; gap: 1px;
  padding: 0.5rem 0.5rem 0.4rem;
  border-radius: var(--ui-radii-card);
  /* No token border: a border in a theme colour would sit between two
     swatches and misrepresent the step-to-step delta. The gap does the
     separating. */
  border: none;
}
.pc-card__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.375rem; margin-bottom: 0.125rem;
}
.pc-card__step {
  font-family: var(--ui-font-family);
  font-size: 1rem; font-weight: 700; letter-spacing: 0.01em;
  color: inherit;
}
.pc-card__gamut {
  font-family: var(--ui-font-mono);
  font-size: 0.5625rem; font-weight: 700; letter-spacing: 0.06em;
  padding: 0.05rem 0.3rem;
  border: 1px solid currentColor;
  border-radius: var(--ui-radii-chip);
  opacity: 0.8;
  color: inherit;
}

/* Value rows are buttons (click to copy) but must not look like chrome — any
   fill or border here would sit on top of the swatch and change the colour
   the reader is judging. */
.pc-val {
  appearance: none; background: none; border: 0; padding: 0;
  display: flex; align-items: baseline; gap: 0.3rem;
  width: 100%; text-align: left; cursor: pointer;
  font-family: var(--ui-font-mono);
  font-size: 0.625rem; line-height: 1.45;
  color: inherit;
}
/* Kept as an ellipsis guard only. At 200px nothing should clip; if a future
   token pushes past that, it degrades to an ellipsis rather than reflowing
   the whole ramp. */
.pc-val__v {
  flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pc-val:hover .pc-val__v { text-decoration: underline; }
.pc-val:focus-visible { outline: var(--ui-focus-ring); outline-offset: 2px; border-radius: 2px; }

.pc-skipped {
  margin: 0.25rem 0 0; padding-left: 1.1rem;
  color: var(--ui-text-muted);
  font-size: var(--ui-text-body-small-font-size);
}
.pc-skipped code { font-family: var(--ui-font-mono); }

/* Alpha strip. The bar is a fixed neutral (from the token data, currently
   #808080) and deliberately NOT a theme token: the entire purpose is to read
   each alpha against a constant, so it must not move when the theme does.

   --pc-alpha-overhang is the distance the swatches extend past the bar at the
   top and the bottom. Those exposed ends show the same alpha over the live
   page surface, which is what the reader will actually be compositing over;
   the covered middle shows it over the known neutral. Both readings at once
   is the reason the bar is inset rather than full height. */
.pc-alpha {
  --pc-alpha-w: 64px;
  --pc-alpha-h: 104px;
  --pc-alpha-overhang: 12px;   /* >= 8px, per spec */
  overflow-x: auto; scrollbar-width: thin;
  padding-bottom: 0.25rem;
}
/* The track holds both rows and is what scrolls, so the bar (absolutely
   positioned inside it) travels with the swatches instead of staying pinned
   to the viewport when the strip overflows. */
.pc-alpha__track { width: max-content; }
.pc-alpha__row { position: relative; }
.pc-alpha__bar {
  position: absolute; z-index: 0;
  left: 0; right: 0;
  top: var(--pc-alpha-overhang); bottom: var(--pc-alpha-overhang);
}
.pc-alpha__swatches { position: relative; z-index: 1; display: flex; gap: 0; }
.pc-alpha__swatch {
  appearance: none; border: 0; padding: 0; margin: 0;
  flex: 0 0 auto; width: var(--pc-alpha-w); height: var(--pc-alpha-h);
  cursor: pointer;
}
.pc-alpha__swatch:focus-visible { outline: var(--ui-focus-ring); outline-offset: -3px; z-index: 2; }
/* Second row, same column width, so labels line up under their swatch
   without a per-cell wrapper that would chop the bar into 25 segments.
   The top margin matters: without it the labels butt against the swatches'
   bottom edge and the lower overhang reads as the end of the bar rather than
   as swatch-over-page, which defeats the inset. */
.pc-alpha__labels { display: flex; gap: 0; margin-top: 0.5rem; }
.pc-alpha__meta {
  flex: 0 0 auto; width: var(--pc-alpha-w);
  display: flex; flex-direction: column; align-items: center; gap: 0;
  padding: 0.3rem 0.15rem;
}
.pc-alpha__pct {
  font-family: var(--ui-font-mono); font-size: 0.6875rem; font-weight: 600;
  color: var(--ui-text-primary);
}
.pc-alpha__step {
  font-family: var(--ui-font-mono); font-size: 0.5625rem;
  color: var(--ui-text-muted);
}

/* No narrow-viewport width override: shrinking the card would clip the oklch
   again, and a clipped colour value is worse than a horizontal scroll. The
   ramp already scrolls, so small screens simply scroll further. */
