/* Built by scripts/gallery.ts from Capsomer: its faces, tokens, base and the components the gallery uses. */

@font-face { font-family: "Schibsted Grotesk"; font-style: normal; font-weight: 400; font-display: swap; src: url("fonts/schibsted-grotesk-latin-400-normal.woff2") format("woff2"); }

@font-face { font-family: "Schibsted Grotesk"; font-style: normal; font-weight: 600; font-display: swap; src: url("fonts/schibsted-grotesk-latin-600-normal.woff2") format("woff2"); }

@font-face { font-family: "Martian Mono"; font-style: normal; font-weight: 400; font-display: swap; src: url("fonts/martian-mono-latin-400-normal.woff2") format("woff2"); }

/* Capsomer's tokens: the colour palette (generated by tokens/palette.mjs) and every other
   scale (generated by tokens/scale.mjs), inside the cap.tokens layer. Import this once,
   before any component CSS: `import "capsomer/tokens.css"`. */
/* The one layer order. Every Capsomer rule sits in one of these layers, and an app's own
   CSS stays unlayered, so an app's rule always wins without !important (design 1.4).

   An app still on Tailwind v4 declares its own order before importing anything, so its
   utilities keep winning while screens move over:
     @layer cap, theme, base, components, utilities;
   Nested names (cap.tokens and the rest) then sort inside "cap". */
@layer cap.reset, cap.tokens, cap.base, cap.components, cap.utilities;

@layer cap.tokens {
/* Generated by tokens/palette.mjs: step 9 #8c5fd2 (oklch 0.587 0.172 299.1), step 11 anchored at #4f2d7f.
   Do not edit: change the script and run `npm run tokens`. `npm run check` refuses a file
   that differs from what the script writes, and any pair below WCAG 2.2's bar. */
:root {
  --ground: #fbf9ff;
  --surface: #fdfdfe;
  --raised: #f7f6fc;
  --sunken: #e9e6ef;
  --line: #dfdce5;
  --line-strong: #918e96;
  --text: #1c1727;
  --muted: #686471;
  --dim: #696571;
  --accent: #8c5fd2;
  --accent-hover: #1d162c;
  --accent-text: #4f2d7f;
  --accent-soft: #ece5ff;
  --accent-line: #9780c3;
  --sel: #f4efff;
  --primary: #7d50c1;
  --primary-hover: #4f2d7f;
  --primary-fg: #fdfdfe;
  --ring: #8c5fd2;
  --ok: #047735;
  --ok-soft: #d6f7dc;
  --warn: #8a5f01;
  --warn-soft: #ffeaca;
  --crit: #ae453f;
  --crit-soft: #fee7e4;
  --info: #2668b6;
  --info-soft: #e2eeff;
  --nodata: #696571;
  --nodata-soft: #ebeaef;
  --accent-1: #fdfcff;
  --accent-2: #faf8fe;
  --accent-3: #f4efff;
  --accent-4: #ece5ff;
  --accent-5: #e4d9fe;
  --accent-6: #d8caf6;
  --accent-7: #c8b6ed;
  --accent-8: #b29ae3;
  --accent-9: #8c5fd2;
  --accent-10: #7d50c1;
  --accent-11: #4f2d7f;
  --accent-12: #1d162c;
  --series-1: #8c5fd2;
  --series-2: #cc4f0c;
  --series-3: #008489;
  --series-4: #749455;
  --series-5: #505ca1;
  --series-6: #046d5e;
  --series-7: #c564a1;
  --series-8: #98445b;
  --series-text-1: #000000;
  --series-text-2: #000000;
  --series-text-3: #000000;
  --series-text-4: #1c1727;
  --series-text-5: #fdfdfe;
  --series-text-6: #fdfdfe;
  --series-text-7: #1c1727;
  --series-text-8: #fdfdfe;
  --ramp-1: #e4dcf8;
  --ramp-2: #c7b6ec;
  --ramp-3: #a98dde;
  --ramp-4: #865cc8;
  --ramp-5: #5d3497;
  --ramp-text-1: #1c1727;
  --ramp-text-2: #1c1727;
  --ramp-text-3: #1c1727;
  --ramp-text-4: #fdfdfe;
  --ramp-text-5: #fdfdfe;
  --shadow-xs: 0 1px 2px 0 rgba(28, 23, 39, 0.05);
  --shadow-s: 0 1px 3px 0 rgba(28, 23, 39, 0.1), 0 1px 2px -1px rgba(28, 23, 39, 0.1);
  --shadow-m: 0 4px 6px -1px rgba(28, 23, 39, 0.1), 0 2px 4px -2px rgba(28, 23, 39, 0.1);
  --shadow-l: 0 10px 15px -3px rgba(28, 23, 39, 0.1), 0 4px 6px -4px rgba(28, 23, 39, 0.1);
  --ring-soft: rgba(140, 95, 210, 0.5);
  --shadow-ring: 0 0 0 var(--ring-width) rgba(140, 95, 210, 0.5);
  --shadow: var(--shadow-l);
  --scrim: rgba(28, 23, 39, 0.28);
  color-scheme: light;
  --ui: "Schibsted Grotesk", "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  --mono: "Martian Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ground: #0e0c12;
    --surface: #18161c;
    --raised: #211f25;
    --sunken: #09070c;
    --line: #343238;
    --line-strong: #6b6970;
    --text: #efedf3;
    --muted: #96949e;
    --dim: #898790;
    --accent: #8c5fd2;
    --accent-hover: #ede8f7;
    --accent-text: #c6b1f2;
    --accent-soft: #2c243c;
    --accent-line: #745e9d;
    --sel: #211c2c;
    --primary: #9b6ee3;
    --primary-hover: #c6b1f2;
    --primary-fg: #0e0c12;
    --ring: #8c5fd2;
    --ok: #4aa462;
    --ok-soft: #15301b;
    --warn: #bc861a;
    --warn-soft: #362508;
    --crit: #d97269;
    --crit-soft: #3e1e1c;
    --info: #5793df;
    --info-soft: #172a41;
    --nodata: #898790;
    --nodata-soft: #272629;
    --accent-1: #0e0c11;
    --accent-2: #151219;
    --accent-3: #211c2c;
    --accent-4: #2c243c;
    --accent-5: #372c4b;
    --accent-6: #44385c;
    --accent-7: #584976;
    --accent-8: #745d9f;
    --accent-9: #8c5fd2;
    --accent-10: #9b6ee3;
    --accent-11: #c6b1f2;
    --accent-12: #ede8f7;
    --series-1: #8c5fd2;
    --series-2: #cc4f0c;
    --series-3: #008489;
    --series-4: #a3c584;
    --series-5: #a4b5ff;
    --series-6: #65cbb6;
    --series-7: #c3629f;
    --series-8: #e4889e;
    --series-text-1: #000000;
    --series-text-2: #000000;
    --series-text-3: #000000;
    --series-text-4: #18161c;
    --series-text-5: #18161c;
    --series-text-6: #18161c;
    --series-text-7: #18161c;
    --series-text-8: #18161c;
    --ramp-1: #33254b;
    --ramp-2: #553e7d;
    --ramp-3: #7b59b2;
    --ramp-4: #a582e5;
    --ramp-5: #cdb5ff;
    --ramp-text-1: #efedf3;
    --ramp-text-2: #efedf3;
    --ramp-text-3: #efedf3;
    --ramp-text-4: #18161c;
    --ramp-text-5: #18161c;
    --shadow-xs: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
    --shadow-s: 0 1px 3px 0 rgba(0, 0, 0, 0.4), 0 1px 2px -1px rgba(0, 0, 0, 0.4);
    --shadow-m: 0 4px 6px -1px rgba(0, 0, 0, 0.45), 0 2px 4px -2px rgba(0, 0, 0, 0.45);
    --shadow-l: 0 10px 15px -3px rgba(0, 0, 0, 0.55), 0 4px 6px -4px rgba(0, 0, 0, 0.55);
    --ring-soft: rgba(140, 95, 210, 0.5);
    --shadow-ring: 0 0 0 var(--ring-width) rgba(140, 95, 210, 0.5);
    --shadow: var(--shadow-l);
    --scrim: rgba(0, 0, 0, 0.55);
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --ground: #0e0c12;
  --surface: #18161c;
  --raised: #211f25;
  --sunken: #09070c;
  --line: #343238;
  --line-strong: #6b6970;
  --text: #efedf3;
  --muted: #96949e;
  --dim: #898790;
  --accent: #8c5fd2;
  --accent-hover: #ede8f7;
  --accent-text: #c6b1f2;
  --accent-soft: #2c243c;
  --accent-line: #745e9d;
  --sel: #211c2c;
  --primary: #9b6ee3;
  --primary-hover: #c6b1f2;
  --primary-fg: #0e0c12;
  --ring: #8c5fd2;
  --ok: #4aa462;
  --ok-soft: #15301b;
  --warn: #bc861a;
  --warn-soft: #362508;
  --crit: #d97269;
  --crit-soft: #3e1e1c;
  --info: #5793df;
  --info-soft: #172a41;
  --nodata: #898790;
  --nodata-soft: #272629;
  --accent-1: #0e0c11;
  --accent-2: #151219;
  --accent-3: #211c2c;
  --accent-4: #2c243c;
  --accent-5: #372c4b;
  --accent-6: #44385c;
  --accent-7: #584976;
  --accent-8: #745d9f;
  --accent-9: #8c5fd2;
  --accent-10: #9b6ee3;
  --accent-11: #c6b1f2;
  --accent-12: #ede8f7;
  --series-1: #8c5fd2;
  --series-2: #cc4f0c;
  --series-3: #008489;
  --series-4: #a3c584;
  --series-5: #a4b5ff;
  --series-6: #65cbb6;
  --series-7: #c3629f;
  --series-8: #e4889e;
  --series-text-1: #000000;
  --series-text-2: #000000;
  --series-text-3: #000000;
  --series-text-4: #18161c;
  --series-text-5: #18161c;
  --series-text-6: #18161c;
  --series-text-7: #18161c;
  --series-text-8: #18161c;
  --ramp-1: #33254b;
  --ramp-2: #553e7d;
  --ramp-3: #7b59b2;
  --ramp-4: #a582e5;
  --ramp-5: #cdb5ff;
  --ramp-text-1: #efedf3;
  --ramp-text-2: #efedf3;
  --ramp-text-3: #efedf3;
  --ramp-text-4: #18161c;
  --ramp-text-5: #18161c;
  --shadow-xs: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
  --shadow-s: 0 1px 3px 0 rgba(0, 0, 0, 0.4), 0 1px 2px -1px rgba(0, 0, 0, 0.4);
  --shadow-m: 0 4px 6px -1px rgba(0, 0, 0, 0.45), 0 2px 4px -2px rgba(0, 0, 0, 0.45);
  --shadow-l: 0 10px 15px -3px rgba(0, 0, 0, 0.55), 0 4px 6px -4px rgba(0, 0, 0, 0.55);
  --ring-soft: rgba(140, 95, 210, 0.5);
  --shadow-ring: 0 0 0 var(--ring-width) rgba(140, 95, 210, 0.5);
  --shadow: var(--shadow-l);
  --scrim: rgba(0, 0, 0, 0.55);
  color-scheme: dark;
}

}
@layer cap.tokens {
/* Generated by tokens/scale.mjs (space, radius, type, density, motion, layers, focus, layout). Do not edit: change the script and run
   `npm run tokens`. `npm run check` refuses a file that differs from what it writes. */
:root {
  /* space */
  --space-1: 2px;
  --space-2: 4px;
  --space-3: 8px;
  --space-4: 12px;
  --space-5: 16px;
  --space-6: 24px;
  --space-7: 32px;
  --space-8: 48px;
  --space-9: 64px;
  /* radius */
  --radius-s: 3px;
  --radius-m: 6px;
  --radius-l: 10px;
  --radius-xl: 14px;
  --radius-full: 999px;
  /* type */
  --fs-h3: 18px;
  --fs-h2: 22px;
  --fs-prose: clamp(17px, 1rem + 0.25vw, 19px);
  --fs-prose-h2: clamp(22px, 1.2rem + 1.2vw, 30px);
  --fs-input-coarse: 16px;
  --prose: "Source Serif 4", "Iowan Old Style", Charter, Georgia, serif;
  /* motion */
  --dur-fast: 100ms;
  --dur-base: 200ms;
  --dur-slow: 400ms;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --ease-spring: linear(0, 0.005 1%, 0.028 2.5%, 0.117 5.5%, 0.572 16%, 0.77 21.5%, 0.839 24%, 0.906 27%, 0.956 30%, 0.992 33%, 1.012 35.5%, 1.026 38%, 1.035 41%, 1.038 44.5%, 1.035 49.5%, 1.012 63.5%, 1.003 72.5%, 0.999 84.5%, 1);
  /* layer */
  --z-base: 0;
  --z-sticky: 20;
  --z-tabbar: 30;
  --z-overlay: 40;
  --z-message: 70;
  --z-tip: 80;
  /* focus */
  --focus-width: 2px;
  --focus-offset: 2px;
  --ring-width: 3px;
  /* layout */
  --measure: 66ch;
  --rail-w: 208px;
  --rail-w-collapsed: 56px;
  --top-h: 44px;
  --header-h: 3.5rem;
  /* touch */
  --link-rule: 0px;
  --icon-radius: 6px;
  --icon-border: 0px;
  --theme-turn: 0deg;
  --arrow-rest: 1;
  --arrow-shift: 0px;
  --control-h-coarse: 44px;
  --face: var(--ui);
}
/* Density: compact is the default, on the root or on any element; comfortable on any element
   changes its subtree. Literals in every block, so a wrapper never inherits a stale value. */
:root,
[data-density="compact"] {
  --control: 32px;
  --control-h: 32px;
  --target: 24px;
  --pad-x: 11px;
  --pad-y: 6px;
  --pad-card: 16px;
  --fs-label: 11px;
  --fs-detail: 12px;
  --fs-body: 13px;
  --fs-lead: 15px;
  --gap: 12px;
  --row-h: 36px;
  --text-size: var(--fs-body);
  --leading: 1.45;
}
[data-density="comfortable"] {
  --control: 36px;
  --control-h: 36px;
  --target: 28px;
  --pad-x: 12px;
  --pad-y: 8px;
  --pad-card: 24px;
  --fs-label: 12px;
  --fs-detail: 13px;
  --fs-body: 14px;
  --fs-lead: 16px;
  --gap: 16px;
  --row-h: 44px;
  --text-size: var(--fs-body);
  --leading: 1.5;
}
[data-context="prose"] {
  --text-size: var(--fs-prose);
  --leading: 1.6;
  --gap: var(--space-6);
  --control: 40px;
  --face: var(--prose);
}
@media (pointer: coarse) {
  :root,
  [data-density] {
    --control: var(--control-h-coarse);
    --control-h: var(--control-h-coarse);
    --target: 44px;
    --row-h: 44px;
  }
  [data-context="prose"] {
    --control: var(--control-h-coarse);
  }
}

}
/* Enarratio's custom properties, mapped onto the tokens above (css/enarratio.css). */
@layer cap.reset, cap.tokens, cap.base, cap.components, cap.utilities;

/* Enarratio's charts in the family's look. Enarratio (https://github.com/DrDustinEdwards/enarratio)
   reads every colour, font, size and timing from --enarratio-* custom properties and never
   imports Capsomer; this section maps each onto a Capsomer token, so an app that loads
   Capsomer's tokens gets charts in the family's look, a change to a token reaches every chart,
   and both themes, every family and both densities follow with no chart code. Load it with
   `capsomer/tokens.css` (which imports it) and Enarratio's `enarratio/base.css`, or draw the
   charts with `stylesheet(capsomerTheme)` from `capsomer/enarratio-theme` instead; the two give
   the same look (the theme's values are generated from the same tokens, tokens/palette.mjs).
   The selector list re-resolves the chain on a wrapper that sets data-family or data-density,
   because a custom property that names another is substituted where it is declared. */
@layer cap.tokens {
  :root,
  [data-family],
  [data-density] {
    /* colours */
    --enarratio-background: var(--surface);
    --enarratio-text: var(--text);
    --enarratio-text-muted: var(--muted);
    --enarratio-grid: var(--line);
    --enarratio-focus: var(--ring);

    /* the eight series, then the label that reads best on each */
    --enarratio-series-1: var(--series-1);
    --enarratio-series-2: var(--series-2);
    --enarratio-series-3: var(--series-3);
    --enarratio-series-4: var(--series-4);
    --enarratio-series-5: var(--series-5);
    --enarratio-series-6: var(--series-6);
    --enarratio-series-7: var(--series-7);
    --enarratio-series-8: var(--series-8);
    --enarratio-series-text-1: var(--series-text-1);
    --enarratio-series-text-2: var(--series-text-2);
    --enarratio-series-text-3: var(--series-text-3);
    --enarratio-series-text-4: var(--series-text-4);
    --enarratio-series-text-5: var(--series-text-5);
    --enarratio-series-text-6: var(--series-text-6);
    --enarratio-series-text-7: var(--series-text-7);
    --enarratio-series-text-8: var(--series-text-8);

    /* the five-step ramp, least to most, in the family's hue */
    --enarratio-sequential-1: var(--ramp-1);
    --enarratio-sequential-2: var(--ramp-2);
    --enarratio-sequential-3: var(--ramp-3);
    --enarratio-sequential-4: var(--ramp-4);
    --enarratio-sequential-5: var(--ramp-5);
    --enarratio-sequential-text-1: var(--ramp-text-1);
    --enarratio-sequential-text-2: var(--ramp-text-2);
    --enarratio-sequential-text-3: var(--ramp-text-3);
    --enarratio-sequential-text-4: var(--ramp-text-4);
    --enarratio-sequential-text-5: var(--ramp-text-5);

    /* status: the same colours as the status component, never the series */
    --enarratio-status-good: var(--ok);
    --enarratio-status-warning: var(--warn);
    --enarratio-status-bad: var(--crit);
    --enarratio-status-unknown: var(--nodata);

    /* fonts: figures are tabular in the interface face */
    --enarratio-font: var(--ui);
    --enarratio-font-numeric: var(--ui);

    /* sizes follow density */
    --enarratio-font-size: var(--text-size);
    --enarratio-font-size-mark: var(--fs-detail);
    --enarratio-font-size-small: var(--fs-detail);
    --enarratio-font-size-tooltip: var(--fs-body);
    --enarratio-line-height-tooltip: var(--leading);

    /* spacing and shape */
    --enarratio-space: var(--space-3);
    --enarratio-gap: var(--space-5);
    --enarratio-gap-row: var(--space-2);
    --enarratio-gap-inline: calc(var(--space-2) + var(--space-1));
    --enarratio-padding-button: var(--space-1) var(--space-2);
    --enarratio-cell-padding: var(--space-2) var(--space-4) var(--space-2) 0;
    --enarratio-radius: var(--radius-s);
    --enarratio-radius-swatch: var(--radius-s);
    --enarratio-radius-tooltip: var(--radius-m);
    --enarratio-swatch-size: var(--space-4);
    --enarratio-tooltip-padding: var(--space-2) var(--space-3);
    --enarratio-tooltip-shadow: var(--shadow-m);
    --enarratio-z-tooltip: var(--z-tip);

    /* focus: the shared ring's width and offset on a focused chart or legend button */
    --enarratio-focus-outline-width: var(--ring-width);
    --enarratio-focus-outline-offset: var(--focus-offset);

    /* timings: 100 and 400 ms, the family's motion; Enarratio stops them under reduced motion */
    --enarratio-duration-hover: var(--dur-fast);
    --enarratio-duration-grow: var(--dur-slow);
    --enarratio-duration-fade: var(--dur-slow);
    --enarratio-duration-draw: var(--dur-slow);
    --enarratio-ease-grow: var(--ease-out);
    --enarratio-ease-out: var(--ease-out);
  }
}



/* The layer order, repeated so this file is safe whatever order an app imports it in: the first statement a browser reads fixes it (css/layers.css has the full note). */
@layer cap.reset, cap.tokens, cap.base, cap.components, cap.utilities;

/* Element defaults every component assumes. Layered, so an app that keeps its own body
   or link styles wins without a fight. */
@layer cap.reset {
  *,
  *::before,
  *::after {
    box-sizing: border-box;
  }
  button,
  input,
  select,
  textarea {
    font: inherit;
    color: inherit;
  }
  [hidden] {
    display: none !important;
  }
}

@layer cap.base {
  body {
    margin: 0;
    background: var(--ground);
    color: var(--text);
    font: 400 var(--text-size) / var(--leading) var(--face);
    -webkit-font-smoothing: antialiased;
  }
  /* The reading context sets its own face and size where it is declared, not only on body. */
  /* A density wrapper sets its own reading size (the root keeps rem sizing). */
  [data-density]:where(:not(:root)) {
    font-size: var(--text-size);
    line-height: var(--leading);
  }
  [data-context] {
    font-family: var(--face);
    font-size: var(--text-size);
    line-height: var(--leading);
  }
  a {
    color: var(--accent-text);
    text-underline-offset: 2px;
    &:hover {
      color: var(--accent-hover);
    }
    @media (prefers-reduced-motion: no-preference) {
      transition: color var(--dur-fast) var(--ease-out);
    }
  }
  /* A trailing arrow in a link or button. A site's --arrow-rest and --arrow-shift (shown and in place
     by default) can hide it until the pointer or focus arrives, when it slides in. */
  .cap-arrow {
    inline-size: 1.1em;
    block-size: 1.1em;
    flex: none;
    opacity: var(--arrow-rest);
    translate: var(--arrow-shift) 0;
    :is(a, button):is(:hover, :focus-visible) > & {
      opacity: 1;
      translate: 0 0;
    }
    @media (prefers-reduced-motion: no-preference) {
      transition:
        opacity var(--dur-base) var(--ease-out),
        translate var(--dur-base) var(--ease-out);
    }
  }
  button {
    cursor: pointer;
  }
  :focus-visible {
    /* The shared focus indicator: a 2 px solid --ring outline, 3:1 against every surface
       (palette.mjs checks it). A field may add --shadow-ring, the soft 3 px glow, beside it. */
    outline: var(--focus-width) solid var(--ring);
    outline-offset: var(--focus-offset);
  }
  h1,
  h2,
  h3 {
    text-wrap: balance;
  }
  /* A touch screen gets 16 px inputs, so iOS does not zoom into the field (decision 5). */
  @media (pointer: coarse) {
    input,
    select,
    textarea {
      font-size: max(var(--fs-input-coarse), 1em);
    }
  }
  /* Still by default (design 4.1): a component's motion is written inside
     prefers-reduced-motion: no-preference, and this catches anything that is not. */
  @media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
      transition-duration: 0s !important;
      transition-delay: 0s !important;
      animation-duration: 0.01ms !important;
      animation-iteration-count: 1 !important;
      scroll-behavior: auto !important;
    }
  }
}

@layer cap.utilities {
  /* Text a screen reader announces and the eye does not see. */
  .cap-sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  /* Identifiers only: ids, shas, commands, paths. Times and counts are .cap-num. */
  .cap-mono {
    font-family: var(--mono);
    font-size: 12px;
    letter-spacing: -0.01em;
  }
  .cap-num {
    font-variant-numeric: tabular-nums;
  }
  .cap-muted {
    color: var(--muted);
  }
  .cap-label {
    font-size: var(--fs-label);
    font-weight: 600;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--muted);
  }
}

/* Between pages, and when the theme changes, the old view fades into the new one in 200 ms
   instead of cutting (a view transition; the browser keeps whatever is the same on both still).
   Same-origin page loads take part on their own (cross-document view transitions); a client-side
   router or theme-switch.ts starts one itself. A page change, typed cap-page, also lets the main
   content rise 6 px into place while the header and menus stay put (Arc's dialog rise, applied to
   the page). Only with motion allowed; a browser without view transitions just shows the new
   page, as before. An app that wants none writes `@view-transition { navigation: none; }`. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
    types: cap-page;
  }
  ::view-transition-group(root) {
    animation-duration: var(--dur-base);
    animation-timing-function: var(--ease-out);
  }
  :active-view-transition-type(cap-page) main {
    view-transition-name: cap-main;
  }
  ::view-transition-group(cap-main) {
    animation: none;
  }
  ::view-transition-old(cap-main) {
    animation: var(--dur-fast) var(--ease-out) both cap-page-out;
  }
  ::view-transition-new(cap-main) {
    animation: var(--dur-base) var(--ease-out) both cap-page-in;
  }
  @keyframes cap-page-out {
    to {
      opacity: 0;
    }
  }
  @keyframes cap-page-in {
    from {
      opacity: 0;
      transform: translateY(6px);
    }
  }
}


/* The layer order, repeated so this file is safe whatever order an app imports it in: the first statement a browser reads fixes it (css/layers.css has the full note). */
@layer cap.reset, cap.tokens, cap.base, cap.components, cap.utilities;

/* Buttons. Matches shadcn/ui Button (variants default, outline, secondary, ghost, destructive,
   link; sizes xs, sm, default, lg and the icon sizes) and ButtonGroup, in Capsomer's tokens.
   Variants map: default = shadcn outline, primary = shadcn default, secondary, quiet = ghost,
   danger = destructive, link. Each variant sets local properties; the states below read them,
   so a state works on every variant. */
@layer cap.components {
  .cap-btn {
    --cap-btn-fg: var(--text);
    --cap-btn-bg: var(--raised);
    --cap-btn-line: var(--line-strong);
    --cap-btn-hover: color-mix(in srgb, var(--raised), var(--text) 7%);
    --cap-btn-press: color-mix(in srgb, var(--raised), var(--text) 12%);
    --cap-btn-h: var(--control);
    --cap-btn-fs: var(--fs-body);
    --cap-btn-icon: 16px;
    --cap-btn-gap: 6px;
    position: relative;
    display: inline-flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    gap: var(--cap-btn-gap);
    box-sizing: border-box;
    min-height: var(--cap-btn-h);
    min-width: var(--target);
    margin: 0;
    padding: 0 var(--pad-x);
    border: 1px solid var(--cap-btn-line);
    border-radius: var(--radius-m);
    background: var(--cap-btn-bg);
    background-clip: padding-box;
    color: var(--cap-btn-fg);
    font-family: var(--ui);
    font-size: var(--cap-btn-fs);
    font-weight: 500;
    line-height: 1.2;
    text-decoration: none;
    white-space: nowrap;
    vertical-align: middle;
    cursor: pointer;
    user-select: none;
    -webkit-tap-highlight-color: transparent;

    & > svg {
      flex: none;
      width: var(--cap-btn-icon);
      height: var(--cap-btn-icon);
      pointer-events: none;
    }
    & kbd {
      margin-inline-start: 2px;
    }
    /* An icon beside the label pulls the padding in on its side, as shadcn's data-icon does. */
    &:has(> svg[data-icon="start"]) {
      padding-inline-start: calc(var(--pad-x) - 3px);
    }
    &:has(> svg[data-icon="end"]) {
      padding-inline-end: calc(var(--pad-x) - 3px);
    }

    /* Sizes. xs and sm never go under the target; on a touch screen --control and --target are 44. */
    &[data-size="xs"] {
      --cap-btn-h: max(var(--target), calc(var(--control) - 8px));
      --cap-btn-fs: var(--fs-detail);
      --cap-btn-icon: 12px;
      --cap-btn-gap: 4px;
      padding-inline: calc(var(--pad-x) - 3px);
    }
    &[data-size="sm"] {
      --cap-btn-h: max(var(--target), calc(var(--control) - 4px));
      --cap-btn-icon: 14px;
      --cap-btn-gap: 4px;
      padding-inline: calc(var(--pad-x) - 1px);
    }
    &[data-size="lg"] {
      --cap-btn-h: calc(var(--control) + 4px);
      --cap-btn-fs: var(--fs-lead);
    }

    &:hover,
    &[data-force="hover"] {
      --cap-btn-bg: var(--cap-btn-hover);
    }
    &:active,
    &[data-force="pressed"] {
      --cap-btn-bg: var(--cap-btn-press);
    }
    /* A button that owns a popup shows it is open, as shadcn's aria-expanded does. */
    &[aria-expanded="true"] {
      --cap-btn-bg: var(--cap-btn-press);
    }

    /* The main action of a view: one per view. shadcn "default". */
    &[data-variant="primary"] {
      --cap-btn-fg: var(--primary-fg);
      --cap-btn-bg: var(--primary);
      --cap-btn-line: var(--primary);
      --cap-btn-hover: var(--primary-hover);
      --cap-btn-press: var(--primary-hover);
      font-weight: 600;
    }

    /* A tinted fill in the accent, for the second action beside a primary. shadcn "secondary".
       Its edge is the accent line (3:1), since a soft fill alone is not an edge. */
    &[data-variant="secondary"] {
      --cap-btn-fg: var(--accent-text);
      --cap-btn-bg: var(--accent-soft);
      --cap-btn-line: var(--accent-line);
      --cap-btn-hover: var(--accent-5);
      --cap-btn-press: var(--accent-6);
    }

    /* Starts something that cannot be undone: it opens the confirm dialog, it does not act.
       shadcn "destructive". The hover is a thicker edge, not a deeper fill: a deeper tint
       would take the label under 4.5:1. */
    &[data-variant="danger"] {
      --cap-btn-fg: var(--crit);
      --cap-btn-bg: var(--crit-soft);
      --cap-btn-line: var(--crit);
      --cap-btn-hover: var(--crit-soft);
      --cap-btn-press: var(--sunken);
      font-weight: 600;

      &:where(:hover, [data-force="hover"]) {
        box-shadow: inset 0 0 0 1px var(--crit);
      }
    }

    /* A secondary action in a toolbar or a row: no border until the pointer is on it. shadcn "ghost". */
    &[data-variant="quiet"] {
      --cap-btn-bg: transparent;
      --cap-btn-line: transparent;
      --cap-btn-hover: var(--raised);
      --cap-btn-press: var(--sunken);

      &:where(:hover, [data-force="hover"]) {
        --cap-btn-line: var(--line);
      }
    }

    /* Reads as a link, still a button. shadcn "link". The underline carries the hover. */
    &[data-variant="link"] {
      --cap-btn-fg: var(--accent-text);
      --cap-btn-bg: transparent;
      --cap-btn-line: transparent;
      --cap-btn-hover: transparent;
      --cap-btn-press: transparent;
      text-underline-offset: 4px;

      &:where(:hover, [data-force="hover"]) {
        --cap-btn-fg: var(--accent-hover);
        text-decoration: underline;
      }
    }

    /* An icon button: square, and it needs an accessible name (aria-label). */
    &[data-icon-only] {
      width: var(--cap-btn-h);
      padding: 0;
    }

    /* An invalid state set by the app (a button that submits an invalid form). */
    &[aria-invalid="true"] {
      --cap-btn-line: var(--crit);
    }

    /* The shared ring: an opaque 3 px in the accent (a 50% ring could not reach 3:1). */
    &:focus-visible {
      outline: var(--ring-width) solid var(--ring);
      outline-offset: 1px;
    }
    &[data-variant="primary"]:focus-visible {
      outline-offset: 2px;
    }

    /* Disabled, with its reason beside it (aria-describedby). It stays focusable so the
       reason can be found by keyboard; a dashed edge says "not now" without colour alone. */
    &:is(:disabled, [aria-disabled="true"]) {
      --cap-btn-fg: var(--muted);
      --cap-btn-bg: var(--surface);
      --cap-btn-line: var(--line-strong);
      border-style: dashed;
      box-shadow: none;
      font-weight: 500;
      text-decoration: none;
      cursor: not-allowed;
    }
    &[data-variant="link"]:is(:disabled, [aria-disabled="true"]) {
      --cap-btn-bg: transparent;
      --cap-btn-line: transparent;
      --cap-btn-fg: var(--dim);
    }

    /* Pending: the label stays (so the width and the accessible name stay) but is not
       painted; the spinner sits over it. */
    & .cap-btn-spinner {
      display: none;
    }
    &[aria-busy="true"] {
      cursor: progress;

      & > :not(.cap-btn-spinner) {
        opacity: 0;
      }
      & > .cap-btn-spinner {
        display: block;
        position: absolute;
        inset: 0;
        width: 14px;
        height: 14px;
        margin: auto;
        border: 2px solid currentColor;
        border-right-color: transparent;
        border-radius: 50%;
      }
    }
  }

  /* A label on its way out (morphLabel in button.ts): over the button, centred, out of the way. */
  .cap-btn > .cap-btn-label[data-leaving] {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    pointer-events: none;
  }

  /* The default look has the quiet shadow shadcn's outline button has in its roomy style. */
  .cap-btn:where(:not([data-variant]), [data-variant="default"]):not(:disabled, [aria-disabled="true"]) {
    box-shadow: var(--shadow-xs);
  }

  /* The reason a button is disabled, written beside it. */
  .cap-btn-reason {
    color: var(--muted);
    font-size: var(--fs-detail);
  }

  /* A button that looks like a link inside a sentence: Undo in a message, Clear beside a filter. */
  .cap-link-btn {
    display: inline-flex;
    align-items: center;
    min-height: var(--target);
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    color: var(--accent-text);
    font: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor: pointer;

    &:hover,
    &[data-force="hover"] {
      color: var(--accent-hover);
      text-decoration-thickness: 2px;
    }
    &:focus-visible {
      outline: var(--ring-width) solid var(--ring);
      outline-offset: 1px;
    }
    &:is(:disabled, [aria-disabled="true"]) {
      color: var(--dim);
      text-decoration: none;
      cursor: not-allowed;
    }
  }

  /* Button group: buttons sharing one edge. shadcn ButtonGroup. */
  .cap-btn-group {
    display: flex;
    width: fit-content;
    align-items: stretch;

    & > .cap-btn:focus-visible,
    & > .cap-btn:hover {
      z-index: 1;
    }
    & > .cap-btn:not(:first-child) {
      margin-inline-start: -1px;
      border-start-start-radius: 0;
      border-end-start-radius: 0;
    }
    & > .cap-btn:not(:last-child) {
      border-start-end-radius: 0;
      border-end-end-radius: 0;
    }
    & > .cap-btn-group-text {
      margin-inline-start: -1px;
    }
    &[data-orientation="vertical"] {
      flex-direction: column;

      & > .cap-btn:not(:first-child) {
        margin-inline-start: 0;
        margin-block-start: -1px;
        border-start-start-radius: 0;
        border-start-end-radius: 0;
      }
      & > .cap-btn:not(:last-child) {
        border-end-start-radius: 0;
        border-end-end-radius: 0;
      }
    }
  }
  /* A label that sits in a group beside the buttons: ButtonGroupText. */
  .cap-btn-group-text {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    box-sizing: border-box;
    min-height: var(--control);
    padding: 0 var(--pad-x);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-m);
    background: var(--sunken);
    color: var(--text);
    font-size: var(--fs-body);
    font-weight: 500;
    white-space: nowrap;
  }
  .cap-btn-group > .cap-btn-group-text:not(:first-child) {
    border-start-start-radius: 0;
    border-end-start-radius: 0;
  }
  .cap-btn-group > .cap-btn-group-text:not(:last-child) {
    border-start-end-radius: 0;
    border-end-end-radius: 0;
  }

  @media (prefers-reduced-motion: no-preference) {
    .cap-btn {
      transition:
        border-color var(--dur-fast) var(--ease-out),
        background-color var(--dur-fast) var(--ease-out),
        color var(--dur-fast) var(--ease-out),
        scale var(--dur-slow) var(--ease-spring);
    }
    /* The press: down to 97% at once, back on a spring when released (Emil Kowalski's 0.97, Arc's
       quick press and spring release). Not on a button that owns a popup: it anchors the popup,
       which must not measure a shrunken trigger. */
    .cap-btn:is(:active, [data-force="pressed"]):not([aria-haspopup]):not([aria-disabled="true"]):not(:disabled) {
      scale: 0.97;
      transition-duration: var(--dur-fast);
      transition-timing-function: var(--ease-out);
    }
    .cap-link-btn {
      transition: color var(--dur-fast) var(--ease-out);
    }
    .cap-btn[aria-busy="true"] > .cap-btn-spinner {
      animation: cap-btn-spin 0.8s linear infinite;
    }
  }

  @keyframes cap-btn-spin {
    to {
      rotate: 1turn;
    }
  }

  @media (forced-colors: active) {
    .cap-btn {
      border-color: ButtonBorder;
    }
    .cap-btn:is(:disabled, [aria-disabled="true"]),
    .cap-link-btn:is(:disabled, [aria-disabled="true"]) {
      color: GrayText;
      border-color: GrayText;
    }
    .cap-btn:focus-visible,
    .cap-link-btn:focus-visible {
      outline-color: Highlight;
    }
  }
}


/* The layer order, repeated so this file is safe whatever order an app imports it in: the first statement a browser reads fixes it (css/layers.css has the full note). */
@layer cap.reset, cap.tokens, cap.base, cap.components, cap.utilities;

/* The one floating surface. `.cap-popover` is the look (background, ring, shadow, radius,
   padding, open and close motion, arrow, the inverted tooltip form). It depends on no
   behaviour, so Base UI's combobox, menu and select popups wear it too: they position
   themselves and set data-side, data-starting-style and data-ending-style, which this file
   reads. On a native popover (`popover` attribute) it also places itself, by CSS anchor
   positioning where the browser has it, otherwise at the numbers popover.ts sets through the
   CSSOM (--cap-popover-x, --cap-popover-y). Reference: shadcn Popover, Tooltip, Hover Card. */
@layer cap.components {
  [data-cap="popover"] {
    display: contents;
  }

  /* Shown as a flex column only while open: a display on a closed native popover would
     show it (Base UI's popups have no popover attribute and are always laid out). */
  .cap-popover:where(:not([popover]), :popover-open) {
    display: flex;
  }
  .cap-popover[data-variant="tooltip"]:where(:not([popover]), :popover-open) {
    display: inline-flex;
  }
  .cap-popover {
    --_side: bottom;
    --_tx: 0px;
    --_ty: 0px;
    --_arrow: 0px;
    --_line: var(--line);
    box-sizing: border-box;
    flex-direction: column;
    gap: var(--space-3);
    max-inline-size: calc(100vw - 16px);
    margin: 0;
    padding: var(--pad-card);
    border: 0;
    border-radius: var(--radius-l);
    background: var(--raised);
    color: var(--text);
    /* The ring is a hairline (decorative); the shadow lifts it. */
    box-shadow:
      0 0 0 1px var(--line),
      var(--shadow-m);
    font-size: var(--fs-body);
    line-height: 1.4;
    text-align: start;
    overflow: visible;
    transform-origin: var(--transform-origin, var(--_origin, center));

    /* Focus moves onto the surface itself when it holds nothing to focus; it is not a
       control, so it takes no ring (shadcn does the same). */
    &:focus {
      outline: none;
    }

    /* Width: a native popover or an explicit size gets shadcn's w-72; a Base UI popup that
       sets no size keeps the width its own CSS gives it. */
    &:where([popover], [data-size]) {
      inline-size: 18rem;
    }
    &[data-size="sm"] {
      inline-size: 14rem;
    }
    &[data-size="lg"] {
      inline-size: 24rem;
    }
    &[data-size="auto"] {
      inline-size: fit-content;
    }

    /* A list popup (menu, select, combobox) pads its own items. */
    &[data-flush] {
      padding: 0;
      gap: 0;
    }
  }

  /* The side the surface really sits on: Base UI writes it as data-side; our module writes
     data-placed (after any flip), which wins over the side an author asked for. It only
     steers the motion (slide in from the trigger's side) and the arrow. */
  .cap-popover:is([data-side="bottom"]:not([data-placed]), [data-placed="bottom"]) {
    --_ty: -4px;
    --_origin: top;
  }
  .cap-popover:is([data-side="top"]:not([data-placed]), [data-placed="top"]) {
    --_ty: 4px;
    --_origin: bottom;
  }
  .cap-popover:is([data-side="left"]:not([data-placed]), [data-side="inline-start"]:not([data-placed]), [data-placed="left"]) {
    --_tx: 4px;
    --_origin: right;
  }
  .cap-popover:is([data-side="right"]:not([data-placed]), [data-side="inline-end"]:not([data-placed]), [data-placed="right"]) {
    --_tx: -4px;
    --_origin: left;
  }

  .cap-popover-header {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
  }
  .cap-popover-title {
    margin: 0;
    font-size: inherit;
    font-weight: 600;
    color: var(--text);
  }
  .cap-popover-description {
    margin: 0;
    color: var(--muted);
  }

  /* The arrow: a square turned 45 degrees, half hidden behind the surface. Its two outer
     edges carry the ring. popover.ts sets --cap-arrow-x / --cap-arrow-y so it points at
     the trigger's centre; Base UI's Arrow sets left/top itself. */
  .cap-popover-arrow {
    position: absolute;
    inline-size: 8px;
    block-size: 8px;
    background: inherit;
    border: 1px solid transparent;
    border-radius: 1px;
    rotate: 45deg;
    pointer-events: none;
  }
  .cap-popover:is([data-side="bottom"]:not([data-placed]), [data-placed="bottom"]) > .cap-popover-arrow {
    top: -5px;
    left: var(--cap-arrow-x, 50%);
    translate: -50% 0;
    border-color: var(--_line) transparent transparent var(--_line);
  }
  .cap-popover:is([data-side="top"]:not([data-placed]), [data-placed="top"]) > .cap-popover-arrow {
    bottom: -5px;
    left: var(--cap-arrow-x, 50%);
    translate: -50% 0;
    border-color: transparent var(--_line) var(--_line) transparent;
  }
  .cap-popover:is([data-side="left"]:not([data-placed]), [data-side="inline-start"]:not([data-placed]), [data-placed="left"]) > .cap-popover-arrow {
    right: -5px;
    top: var(--cap-arrow-y, 50%);
    translate: 0 -50%;
    border-color: var(--_line) var(--_line) transparent transparent;
  }
  .cap-popover:is([data-side="right"]:not([data-placed]), [data-side="inline-end"]:not([data-placed]), [data-placed="right"]) > .cap-popover-arrow {
    left: -5px;
    top: var(--cap-arrow-y, 50%);
    translate: 0 -50%;
    border-color: transparent transparent var(--_line) var(--_line);
  }
  .cap-popover:has(> .cap-popover-arrow) {
    --_arrow: 4px;
  }

  /* The tooltip form: a small raised surface with a visible edge in both themes, so it never
     reads as a selected item (the admin menu's current page is a dark fill in light and a light
     fill in dark; a tooltip is neither). No padding of a card. Keys are key caps. One look for
     every tooltip in Capsomer, the admin shell's included. */
  .cap-popover[data-variant="tooltip"] {
    --_line: var(--line-strong);
    flex-direction: row;
    align-items: center;
    gap: var(--space-3);
    inline-size: fit-content;
    max-inline-size: min(20rem, calc(100vw - 16px));
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-m);
    background: var(--raised);
    color: var(--text);
    box-shadow:
      0 0 0 1px var(--_line),
      var(--shadow-m);
    font-size: var(--fs-detail);
    font-weight: 500;
    white-space: nowrap;

    &:has(kbd) {
      padding-inline-end: var(--space-3);
    }
    & .cap-keys {
      display: inline-flex;
      gap: var(--space-1);
    }
    & kbd {
      display: inline-grid;
      place-items: center;
      min-inline-size: 1.6em;
      padding: 0 var(--space-2);
      border: 1px solid var(--line-strong);
      border-block-end-width: 2px;
      border-radius: var(--radius-s);
      background: var(--surface);
      color: var(--text);
      font: 600 var(--fs-label) / 1.5 var(--ui);
    }
  }

  /* A native popover places itself. The default: below its trigger, centred. */
  .cap-popover[popover] {
    position: fixed;
    inset: auto;
    top: var(--cap-popover-y, 0px);
    left: var(--cap-popover-x, 0px);
    max-block-size: calc(100dvh - 16px);

    &[data-placed-by="pending"] {
      visibility: hidden;
    }
  }

  /* Anchor positioning: popover.ts names the trigger and the popup (CSSOM). The side and
     the alignment pick the position-area; flip-block and flip-inline do the collision
     flip; the offset is a margin on the side facing the trigger. */
  @supports (position-area: bottom) {
    .cap-popover[popover]:not([data-placed-by="js"], [data-placed-by="pending"]) {
      --_ax: ;
      --_ay: ;
      --_al: ;
      --_off: calc(var(--cap-offset, 4px) + var(--_arrow));
      top: auto;
      left: auto;
      position-area: block-end var(--_ax);
      position-try-fallbacks: flip-block, flip-inline;
      margin-block-start: var(--_off);

      &[data-align="start"] {
        --_ax: span-inline-end;
        --_ay: span-bottom;
        --_al: span-block-end;
      }
      &[data-align="end"] {
        --_ax: span-inline-start;
        --_ay: span-top;
        --_al: span-block-start;
      }
      &[data-variant="tooltip"]:not([data-side]),
      &[data-side="top"] {
        position-area: block-start var(--_ax);
        margin-block: 0 var(--_off);
      }
      &[data-side="left"] {
        position-area: left var(--_ay);
        margin-block: 0;
        margin-inline: 0 var(--_off);
      }
      &[data-side="right"] {
        position-area: right var(--_ay);
        margin-block: 0;
        margin-inline: var(--_off) 0;
      }
      &[data-side="inline-start"] {
        position-area: inline-start var(--_al);
        margin-block: 0;
        margin-inline: 0 var(--_off);
      }
      &[data-side="inline-end"] {
        position-area: inline-end var(--_al);
        margin-block: 0;
        margin-inline: var(--_off) 0;
      }
    }
  }

  /* Open and close. Opening, it fades in 100 ms while it grows from 96% and slides 4 px out of
     the trigger's side over 200 ms on the long ease-out; closing is quicker and shorter, 100 ms to
     98% and half the slide, so it gets out of the way (Radix Themes and Arc: what leaves goes
     faster than it came). Transitions, not keyframes, so a close during the open reverses from
     where it is. A native popover animates through @starting-style and allow-discrete (display
     and overlay); Base UI's popups through data-starting-style and data-ending-style. A tooltip
     keeps 100 ms both ways: the next one opens at once anyway. */
  @media (prefers-reduced-motion: no-preference) {
    .cap-popover {
      --_in: var(--dur-base);
      transition:
        opacity var(--dur-fast) var(--ease-out),
        scale var(--dur-fast) var(--ease-out),
        translate var(--dur-fast) var(--ease-out),
        display var(--dur-fast) allow-discrete,
        overlay var(--dur-fast) allow-discrete;

      &[data-variant="tooltip"] {
        --_in: var(--dur-fast);
      }
      &[popover]:not(:popover-open),
      &[data-ending-style] {
        opacity: 0;
        scale: 0.98;
        translate: calc(var(--_tx) / 2) calc(var(--_ty) / 2);
      }
      &[data-starting-style] {
        opacity: 0;
        scale: 0.96;
        translate: var(--_tx) var(--_ty);
      }
      &:is([popover]:popover-open, :not([popover], [data-ending-style])) {
        transition-duration: var(--dur-fast), var(--_in), var(--_in), var(--dur-fast), var(--dur-fast);
      }
      &[popover]:popover-open {
        opacity: 1;
        scale: 1;
        translate: 0 0;

        @starting-style {
          opacity: 0;
          scale: 0.96;
          translate: var(--_tx) var(--_ty);
        }
      }
    }
  }

  /* Forced colours drop shadows and fills: the surface keeps an edge, the arrow its lines. */
  @media (forced-colors: active) {
    .cap-popover {
      border: 1px solid CanvasText;
      background: Canvas;
      color: CanvasText;
    }
    .cap-popover-arrow {
      display: none;
    }
  }
}


/* The layer order, repeated so this file is safe whatever order an app imports it in: the first statement a browser reads fixes it (css/layers.css has the full note). */
@layer cap.reset, cap.tokens, cap.base, cap.components, cap.utilities;

/* The dialog: a native <dialog> opened with showModal(), shared by every Capsomer overlay.
   One modal base (backdrop, surface, header, body, footer, close button) and five
   placements: centre (a dialog), right and left (a side sheet), bottom (a sheet on a phone)
   and top (a palette). The look is rebuilt from shadcn/ui's Dialog, Alert Dialog and Sheet
   (nova): the black-ish scrim with a light blur, the popover surface with a hairline ring and
   the large shadow, the muted band under the footer, zoom-in-95 with a fade at 100 ms for a
   dialog and a short slide with a fade at 200 ms for a sheet. In Capsomer's tokens, so
   density moves the padding. */
@layer cap.components {
  .cap-dialog {
    --cap-dialog-w: 30rem;
    --cap-dialog-gap: max(var(--space-5), env(safe-area-inset-top, 0px), env(safe-area-inset-bottom, 0px));
    --cap-dialog-top: max(var(--space-5), 10dvh);
    --cap-dialog-from: translateY(8px) scale(0.96);
    --cap-dialog-to: translateY(4px) scale(0.98);

    /* Centred by default; the placements below move it. */
    box-sizing: border-box;
    width: min(var(--cap-dialog-w), calc(100% - 2 * var(--cap-dialog-gap)));
    max-width: none;
    /* fit-content, not auto: on a modal `auto` stretches the box between its insets. */
    height: fit-content;
    max-height: calc(100dvh - 2 * var(--cap-dialog-gap));
    margin: auto;
    padding: 0;
    overflow: hidden;
    border: 1px solid var(--line);
    border-radius: var(--radius-l);
    background: var(--raised);
    color: var(--text);
    box-shadow: var(--shadow-l);
    font-size: var(--fs-body);
    overscroll-behavior: contain;

    /* Only when open: a display value on a closed dialog would show it. */
    &[open] {
      display: flex;
      flex-direction: column;
    }
    &:focus-visible {
      outline: 2px solid var(--ring);
      outline-offset: -2px;
    }

    /* An in-page dialog is a form that wraps the parts: it takes no box of its own, so the header,
       body and footer stay the dialog's flex children. Only these dialogs: another dialog's own
       form keeps its box. */
    &[data-cap-inline] > form {
      display: contents;
    }

    /* In the page until script takes it: a dialog written with `open` and `data-cap-inline` is not
       modal, so it flows in the page like a card and its form is there with no script.
       `enhance()` closes it on load and the opener opens it as the modal. */
    &[data-cap-inline]:not(:modal) {
      position: static;
      inset: auto;
      margin-block: var(--space-4);
      margin-inline: auto;
      max-height: none;
      box-shadow: none;
    }

    /* The backdrop: the scrim with a light blur, where the browser can. */
    &::backdrop {
      background: var(--scrim);
      @supports (backdrop-filter: blur(1px)) {
        backdrop-filter: blur(4px);
      }
    }

    &[data-size="sm"] {
      --cap-dialog-w: 24rem;
    }
    &[data-size="lg"] {
      --cap-dialog-w: 42rem;
    }

    /* A side sheet: full height, on the edge, the rounded corner on the inside only. */
    &:is([data-placement="right"], [data-placement="left"]) {
      --cap-dialog-w: 35rem;
      inset-block: 0;
      width: min(var(--cap-dialog-w), 85%);
      height: 100%;
      max-height: 100%;
      margin: 0;
      padding-block: env(safe-area-inset-top, 0px) env(safe-area-inset-bottom, 0px);
      border-radius: 0;

      &[data-size="sm"] {
        --cap-dialog-w: 24rem;
      }
      &[data-size="lg"] {
        --cap-dialog-w: 48rem;
      }
    }
    &[data-placement="right"] {
      inset-inline: auto 0;
      border-width: 0 0 0 1px;
      --cap-dialog-from: translateX(2.5rem);
      --cap-dialog-to: translateX(1.25rem);
    }
    &[data-placement="left"] {
      inset-inline: 0 auto;
      border-width: 0 1px 0 0;
      --cap-dialog-from: translateX(-2.5rem);
      --cap-dialog-to: translateX(-1.25rem);
    }

    /* A bottom sheet: the full width, as tall as its content up to the safe margin. */
    &[data-placement="bottom"] {
      inset-block: auto 0;
      inset-inline: 0;
      width: 100%;
      margin: 0;
      padding-block-end: env(safe-area-inset-bottom, 0px);
      border-width: 1px 0 0;
      border-radius: var(--radius-l) var(--radius-l) 0 0;
      --cap-dialog-from: translateY(2.5rem);
      --cap-dialog-to: translateY(1.25rem);
    }

    /* A palette: centred across, near the top, so what it filters stays in view below. */
    &[data-placement="top"] {
      inset-block: 0;
      margin-block: var(--cap-dialog-top) auto;
      max-height: calc(100dvh - var(--cap-dialog-top) - var(--cap-dialog-gap));
    }
  }

  /* The page behind does not scroll while a dialog is open. */
  :root:has(.cap-dialog:modal) {
    overflow: hidden;
  }

  /* The header: the title, and the description under it. */
  .cap-dialog-header {
    display: flex;
    flex-direction: column;
    flex: none;
    gap: var(--space-3);
    padding: var(--pad-card) var(--pad-card) 0;

    /* With nothing between it and the footer, the header carries the space. */
    &:not(:has(~ .cap-dialog-body)) {
      padding-block-end: var(--pad-card);
    }
    &[data-divider] {
      padding-block-end: var(--pad-card);
      border-block-end: 1px solid var(--line);
    }
    /* The Close button sits in the corner; the title keeps clear of it. */
    .cap-dialog:has(> .cap-dialog-close) > & {
      padding-inline-end: calc(var(--pad-card) + var(--target));
    }
    /* A media tile (an icon) to the left of the title and description. */
    &:has(> .cap-dialog-media) {
      display: grid;
      grid-template-columns: auto minmax(0, 1fr);
      column-gap: var(--space-4);
      align-items: start;

      & > :not(.cap-dialog-media) {
        grid-column: 2;
      }
      & > .cap-dialog-media {
        grid-row: 1 / span 2;
      }
    }
  }

  .cap-dialog-media {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--target);
    height: var(--target);
    border-radius: var(--radius-m);
    background: var(--sunken);
    color: var(--text);

    &[data-tone="crit"] {
      background: var(--crit-soft);
      color: var(--crit);
    }
    &[data-tone="warn"] {
      background: var(--warn-soft);
      color: var(--text);
    }
    & > svg {
      width: 1.5em;
      height: 1.5em;
    }
  }

  .cap-dialog-title {
    margin: 0;
    font-size: var(--fs-lead);
    font-weight: 800;
    line-height: 1.25;
    letter-spacing: -0.02em;
    text-wrap: balance;
    overflow-wrap: anywhere;
  }

  .cap-dialog-description {
    margin: 0;
    color: var(--muted);
    font-size: var(--fs-body);
    text-wrap: pretty;

    & a {
      color: inherit;
      text-decoration: underline;
      text-underline-offset: 0.2em;

      &:hover {
        color: var(--text);
      }
    }
  }

  /* The body is the part that scrolls: header and footer stay. */
  .cap-dialog-body {
    display: grid;
    align-content: start;
    flex: 1 1 auto;
    gap: var(--space-3);
    min-height: 0;
    padding: var(--pad-card);
    overflow-y: auto;
    overscroll-behavior: contain;

    &[data-flush] {
      padding: 0;
    }
    & p {
      margin: 0;
    }
    & ul {
      display: grid;
      gap: var(--space-2);
      margin: 0;
      padding-inline-start: 1.15em;
      list-style: disc;
    }
    /* A scrolling body is a keyboard stop (dialog.ts gives it tabindex); it shows where it is. */
    &:focus-visible {
      outline: 2px solid var(--ring);
      outline-offset: -2px;
    }
  }

  /* The footer: a muted band under a dialog (as Alert Dialog's), a plain row under a sheet.
     Actions stack on a phone with the main one first (column-reverse), and sit in a row
     at the end from there up. data-align="between" sets Cancel apart at the far end. */
  .cap-dialog-footer {
    display: flex;
    flex: none;
    flex-direction: column-reverse;
    gap: var(--space-3);
    padding: var(--pad-card);
    border-block-start: 1px solid var(--line);
    background: var(--sunken);

    .cap-dialog:is([data-placement="right"], [data-placement="left"], [data-placement="bottom"]) > & {
      background: transparent;
    }
    .cap-dialog[data-size="sm"] > & {
      display: grid;
      grid-auto-flow: column;
      grid-auto-columns: 1fr;
    }
  }
  @media (min-width: 40rem) {
    .cap-dialog-footer {
      flex-direction: row;
      justify-content: flex-end;
      align-items: center;

      &[data-align="between"] {
        justify-content: space-between;
      }
      &[data-align="start"] {
        justify-content: flex-start;
      }
      .cap-dialog[data-size="sm"] > & {
        display: flex;
      }
    }
  }

  /* The Close button: in the corner, half a padding in, as Dialog and Sheet place it. */
  .cap-dialog > .cap-dialog-close {
    position: absolute;
    inset-block-start: calc(var(--pad-card) / 2);
    inset-inline-end: calc(var(--pad-card) / 2);
    z-index: 1;
  }

  /* Open and close. A dialog rises 8 px and grows from 96% over 200 ms on the long ease-out
     while it fades in 100 ms (Arc, Radix Themes); a sheet slides 2.5rem in from its edge over
     200 ms. Closing is quicker and shorter: 100 ms, half the distance, to 98% (what leaves goes
     faster than it came). The top palette, opened from the keyboard many times a day, only
     fades, and goes at once (Raycast, by Emil Kowalski's rule: no motion on a keyboard action
     done hundreds of times). The backdrop fades in 200 ms and out in 100 ms. @starting-style
     gives the open frame; display and overlay with allow-discrete keep it on screen through the
     close. Under reduced motion none of this applies: it appears and goes at once. */
  @media (prefers-reduced-motion: no-preference) {
    .cap-dialog {
      opacity: 0;
      transform: var(--cap-dialog-to);
      transition-property: opacity, transform, overlay, display;
      transition-duration: var(--dur-fast);
      transition-timing-function: var(--ease-out);
      transition-behavior: allow-discrete;

      &[data-placement="top"] {
        --cap-dialog-from: none;
        --cap-dialog-to: none;
        transition-duration: 0s;
      }
      &[open] {
        opacity: 1;
        transform: none;
        transition-duration: var(--dur-fast), var(--dur-base);
      }
      &::backdrop {
        opacity: 0;
        transition-property: opacity, overlay, display;
        transition-duration: var(--dur-fast);
        transition-timing-function: var(--ease-out);
        transition-behavior: allow-discrete;
      }
      &[open]::backdrop {
        opacity: 1;
        transition-duration: var(--dur-base);
      }
    }
    /* Drag to dismiss (dialog.ts): a sheet's header is its handle, once script has wired it;
       the backdrop thins as the sheet goes. A bottom sheet shows a grab bar. */
    .cap-dialog:is([data-placement="bottom"], [data-placement="right"], [data-placement="left"])[data-cap-wired]:not([data-cap-modal-lock], [role="alertdialog"]) > .cap-dialog-header {
      touch-action: none;
      -webkit-user-select: none;
      user-select: none;
    }
    .cap-dialog[data-placement="bottom"][data-cap-wired]:not([data-cap-modal-lock], [role="alertdialog"]) > .cap-dialog-header::before {
      content: "";
      align-self: center;
      inline-size: 2.25rem;
      block-size: 4px;
      margin-block: calc(var(--pad-card) / -2) var(--space-1);
      border-radius: 2px;
      background: var(--line-strong);
    }
    .cap-dialog[data-cap-dragging]::backdrop {
      opacity: calc(1 - var(--cap-drag, 0));
      transition-duration: 0s;
    }
    @starting-style {
      .cap-dialog[open] {
        opacity: 0;
        transform: var(--cap-dialog-from);
      }
      .cap-dialog[open]::backdrop {
        opacity: 0;
      }
    }
  }

  @media (forced-colors: active) {
    .cap-dialog {
      border-color: CanvasText;
    }
    .cap-dialog-footer,
    .cap-dialog-header[data-divider] {
      border-color: CanvasText;
    }
  }

  /* A list of links in a sheet (the More sheet): each link is a full-width row, a target. */
  .cap-dialog-body nav ul {
    margin: 0;
    padding: 0;
    list-style: none;
  }
  .cap-dialog-body nav a {
    display: flex;
    align-items: center;
    min-block-size: max(var(--target), var(--row-h));
    padding-inline: var(--pad-x);
    border-radius: var(--radius-m);
    color: var(--text);
    text-decoration: none;

    &:hover {
      background: var(--sunken);
    }
  }
}


/* The layer order, repeated so this file is safe whatever order an app imports it in: the first statement a browser reads fixes it (css/layers.css has the full note). */
@layer cap.reset, cap.tokens, cap.base, cap.components, cap.utilities;

/* The theme switch: Light and Dark as a segmented pair with a sun and a moon, rebuilt from
   shadcn/ui's Toggle Group (outline variant, spacing 0: items flush in one rounded box, the
   chosen one filled) and its Mode Toggle's sun and moon. The inputs are real radios laid over
   each segment, so arrows, Space and forms are the browser's. It sits in the top bar, on the
   ground tone, where --line-strong is 2.9:1, so the control's edge is --dim (5:1 on it in
   both themes). The chosen segment has one marker, a fill: the same dark fill as the admin
   menu's selected page (--accent-12, its word in --ground, set heavier), at well over 3:1
   against the ground, so it needs no second mark. */
@layer cap.components {
  .cap-theme {
    flex: none;
    min-inline-size: 0;
    margin: 0;
    padding: 0;
    border: 0;
  }

  .cap-theme-options {
    display: inline-flex;
    overflow: hidden;
    border: 1px solid var(--dim);
    border-radius: var(--radius-m);
    background: transparent;

    & > label {
      position: relative;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: var(--space-3);
      min-height: max(var(--target), calc(var(--control) - 2px));
      min-width: max(var(--target), calc(var(--control) - 2px));
      padding-inline: var(--pad-x);
      color: var(--muted);
      font-size: var(--fs-body);
      font-weight: 500;
      white-space: nowrap;
      cursor: pointer;

      /* Items are flush; a hairline sets the second apart from the first. */
      & + label {
        border-inline-start: 1px solid var(--line);
      }
      &:hover,
      &[data-force="hover"] {
        background: var(--sunken);
        color: var(--text);
      }
      &:has(input:checked) {
        background: var(--accent-12);
        color: var(--ground);
        font-weight: 600;
      }
      /* Inside the box, so the box's edge does not clip it. */
      &:has(input:focus-visible) {
        outline: var(--focus-width) solid var(--ring);
        outline-offset: -2px;
      }
      &:has(input:disabled) {
        background: none;
        color: var(--dim);
        cursor: not-allowed;
      }
    }

    /* The radio covers its segment, unseen, so a click anywhere on it chooses it. */
    & input {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      margin: 0;
      opacity: 0;
      cursor: inherit;
    }
  }

  .cap-theme-icon {
    flex: none;
    width: 16px;
    height: 16px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-linejoin: round;
  }

  /* The button form (data-variant="button"): one icon button that switches to the other theme,
     the site header's control. A 36px target with an 18px glyph, like every header control. It
     shows a moon while the page is light and a sun while it is dark (both are in the markup, so the
     picture is right before any script runs); its name says what a press does. */
  .cap-theme-button {
    position: relative;
    display: inline-grid;
    place-items: center;
    flex: none;
    width: 36px;
    height: 36px;
    padding: 0;
    border: var(--icon-border) solid var(--line);
    border-radius: var(--icon-radius);
    background: none;
    color: var(--muted);
    cursor: pointer;

    &:hover,
    &[data-force="hover"] {
      background: var(--sunken);
      color: var(--text);
      rotate: var(--theme-turn);
    }
    &:focus-visible {
      outline: var(--focus-width) solid var(--ring);
      outline-offset: var(--focus-offset);
    }
    & .cap-theme-icon {
      width: 18px;
      height: 18px;
      stroke-width: 1.6;
    }
    & [data-show="dark"] {
      display: none;
    }
  }
  :root[data-theme="dark"] .cap-theme-button {
    & [data-show="light"] {
      display: none;
    }
    & [data-show="dark"] {
      display: block;
    }
  }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .cap-theme-button {
      & [data-show="light"] {
        display: none;
      }
      & [data-show="dark"] {
        display: block;
      }
    }
  }

  /* On a phone the top bar has no room for the words: the icons stay, and the words stay for a
     screen reader. */
  @media (max-width: 820px) {
    .cap-theme-text {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
    }
    .cap-theme-options > label {
      min-width: var(--target);
      padding-inline: var(--space-3);
    }
  }

  @media (prefers-reduced-motion: no-preference) {
    .cap-theme-options > label,
    .cap-theme-button {
      transition:
        background-color var(--dur-fast) var(--ease-out),
        color var(--dur-fast) var(--ease-out),
        rotate var(--dur-slow) var(--ease-out);
    }
  }

  @media (forced-colors: active) {
    .cap-theme-options {
      border-color: CanvasText;
    }
    .cap-theme-options > label:has(input:checked) {
      forced-color-adjust: none;
      background: Highlight;
      color: HighlightText;
    }
    .cap-theme-options > label:has(input:disabled) {
      color: GrayText;
    }
  }
}


/* The layer order, repeated so this file is safe whatever order an app imports it in: the first statement a browser reads fixes it (css/layers.css has the full note). */
@layer cap.reset, cap.tokens, cap.base, cap.components, cap.utilities;

/* The site header: one header across the top of every public, app and admin page in the family
   (docs/design/design-site-header.md; Dustin's ten answers of 2026-10-13). Capsomer fixes the
   frame: the order, 56px tall at every width, sticky, full width with the logo 24px from the
   window's edge (16px on a phone), the spacing, the type, the controls and how the panels behave.
   Each site fills its slots: the logo, its colour family, the band (--header-bg, --header-fg,
   --header-fg-muted, --header-line; data-band="dark", data-translucent), its UI face (--font-ui),
   up to two icon extras and one button, where the section links sit (data-links) and what each
   panel holds. On a phone (under 768px) it is one row, with everything else in one sheet. */
@layer cap.components {
  .cap-site-header {
    /* The band: the page's own ground by default. A site sets these four, or data-band="dark". */
    --header-bg: var(--ground);
    --header-fg: var(--text);
    --header-fg-muted: var(--muted);
    --header-line: var(--line);
    /* A hovered control: a clear step off the band, in the band's own colours. */
    --header-hover: color-mix(in srgb, var(--header-bg), var(--header-fg) 12%);
    /* The current link's underline (3:1 against the band), the one button, the avatar. */
    --header-accent: var(--accent);
    --header-cta: var(--primary);
    --header-cta-fg: var(--primary-fg);
    --header-avatar: var(--accent-12);
    --header-avatar-fg: var(--ground);
    --header-fs: 14px;
    --header-side: var(--space-6);
    position: sticky;
    top: 0;
    z-index: calc(var(--z-sticky) + 3);
    display: flex;
    align-items: center;
    box-sizing: border-box;
    inline-size: 100%;
    block-size: var(--header-h);
    padding-inline: var(--header-side);
    background: var(--header-bg);
    color: var(--header-fg);
    border-block-end: 1px solid var(--header-line);
    font-family: var(--font-ui, var(--ui));
    font-size: var(--header-fs);
    line-height: 1.3;

    &[data-sticky="false"] {
      position: relative;
    }
    /* The page's ground at 85%, blurred: the content shows through as it scrolls under. */
    &[data-translucent] {
      background: color-mix(in srgb, var(--header-bg) 85%, transparent);
      backdrop-filter: blur(12px);
    }
    & :focus-visible {
      outline: var(--focus-width) solid var(--ring);
      outline-offset: var(--focus-offset);
    }
  }

  /* Dark in both themes (Foxhound): the darkest step of the site's family in light, the dark
     theme's own surface in dark. The controls on it read in the band's colours. */
  .cap-site-header[data-band="dark"] {
    color-scheme: dark;
    --header-bg: var(--accent-12);
    --header-fg: var(--accent-1);
    --header-fg-muted: color-mix(in srgb, var(--accent-1) 74%, var(--accent-12));
    --header-line: color-mix(in srgb, var(--accent-1) 18%, var(--accent-12));
    --header-avatar: var(--primary);
    --header-avatar-fg: var(--primary-fg);
  }
  :root[data-theme="dark"] .cap-site-header[data-band="dark"] {
    --header-bg: var(--surface);
    --header-fg: var(--text);
    --header-fg-muted: var(--muted);
    --header-line: var(--line);
  }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .cap-site-header[data-band="dark"] {
      --header-bg: var(--surface);
      --header-fg: var(--text);
      --header-fg-muted: var(--muted);
      --header-line: var(--line);
    }
  }

  /* A focused control or an anchor never lands under the header (WCAG 2.4.11). */
  :root:has(.cap-site-header:not([data-sticky="false"])) {
    scroll-padding-top: calc(var(--header-h) + var(--space-3));
  }

  .cap-site-header-skip {
    position: absolute;
    left: var(--space-3);
    top: var(--space-3);
    z-index: var(--z-tip);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-m);
    background: var(--primary);
    color: var(--primary-fg);
    box-shadow: var(--shadow-m);
    font-weight: 600;
    text-decoration: none;
    transform: translateY(-200%);

    &:focus-visible {
      transform: none;
    }
  }

  /* The logo: the site's mark and name, at most 24px tall and 160px wide; it links home. */
  .cap-site-header-logo {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    /* It gives way before the controls do, so the header never runs past a 320px window
       (WCAG 1.4.10, reflow); the mark itself never shrinks. */
    flex: 0 1 auto;
    min-inline-size: 0;
    block-size: 24px;
    max-inline-size: 160px;
    overflow: hidden;
    border-radius: var(--radius-s);
    color: var(--header-fg);
    text-decoration: none;
    white-space: nowrap;

    & :is(svg, img) {
      flex: none;
      block-size: 24px;
      inline-size: auto;
      max-inline-size: 160px;
    }
  }

  /* The switcher: buttons showing where you are, each opening the site's panel. 12px after the
     logo; a thin slash between two (Foxhound: the business, then the product). */
  .cap-site-header-switcher {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-inline-start: var(--space-4);
    min-inline-size: 0;
  }
  .cap-site-header-sep {
    color: var(--header-fg-muted);
    font-size: 18px;
  }
  .cap-site-header-switch {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    block-size: 32px;
    padding: 0 10px;
    border: 1px solid var(--header-line);
    border-radius: var(--radius-m);
    background: none;
    color: var(--header-fg);
    font: inherit;
    white-space: nowrap;
    cursor: pointer;

    &:hover,
    &[aria-expanded="true"],
    &[data-force="hover"] {
      background: var(--header-hover);
    }
    & > svg {
      flex: none;
      inline-size: 16px;
      block-size: 16px;
    }
  }
  /* A square is the business you are in, a circle is you (Dustin's answer 3). */
  .cap-site-header-switch-tile {
    display: grid;
    place-items: center;
    flex: none;
    inline-size: 22px;
    block-size: 22px;
    border-radius: var(--radius-m);
    background: var(--header-cta);
    color: var(--header-cta-fg);
    font-size: 11px;
    font-weight: 600;
  }
  .cap-site-header-chev {
    flex: none;
    inline-size: 12px;
    block-size: 12px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
  }

  /* The section links: 24px after the switcher (left), centred in the header, or on the right
     before the extras after a thin divider; 20px apart. The current page is weight 500 with a 2px
     underline in the accent, so it is not told by colour alone. */
  .cap-site-header-sections {
    margin-inline-start: var(--space-6);
    min-inline-size: 0;

    & > ul {
      display: flex;
      align-items: center;
      gap: 20px;
      margin: 0;
      padding: 0;
      list-style: none;
    }
  }
  .cap-site-header[data-links="center"] .cap-site-header-sections {
    position: absolute;
    left: 50%;
    margin: 0;
    translate: -50% 0;
  }
  .cap-site-header[data-links="right"] .cap-site-header-sections {
    margin-inline-start: auto;
  }
  .cap-site-header-link {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-block-size: 36px;
    padding: 0;
    border: 0;
    border-radius: var(--radius-s);
    background: none;
    color: var(--header-fg-muted);
    font: inherit;
    font-weight: 400;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;

    &:hover,
    &[aria-expanded="true"],
    &[data-force="hover"] {
      color: var(--header-fg);
    }
    /* A site's --link-rule (off by default) grows an underline in under the pointer. */
    &:not([aria-current="page"], [data-current])::after {
      content: "";
      position: absolute;
      inset-inline-start: 0;
      bottom: 4px;
      inline-size: 0;
      block-size: var(--link-rule);
      background: var(--header-accent);
    }
    &:not([aria-current="page"], [data-current]):is(:hover, :focus-visible)::after {
      inline-size: 100%;
    }
    &[aria-current="page"],
    &[data-current] {
      color: var(--header-fg);
      font-weight: 500;

      &::after {
        content: "";
        position: absolute;
        inset-inline: 0;
        bottom: 4px;
        block-size: 2px;
        background: var(--header-accent);
      }
    }
  }

  /* The right side: the site's extras, the theme, the account (always last), 8px apart. After
     section links on the right, a thin divider. */
  .cap-site-header-tools {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-inline-start: auto;
  }
  .cap-site-header[data-links="right"] .cap-site-header-sections + .cap-site-header-tools {
    margin-inline-start: var(--space-4);

    &::before {
      content: "";
      inline-size: 1px;
      block-size: 16px;
      margin-inline-end: var(--space-2);
      background: var(--header-line);
    }
  }
  .cap-site-header-extras {
    display: flex;
    align-items: center;
    gap: var(--space-3);
  }

  /* Every icon control: a 36px target, an 18px glyph from the family's icon set (24 box, 1.6
     stroke), a name and a tip naming it and its key. The theme button wears this too. */
  .cap-site-header .cap-site-header-icon {
    position: relative;
    display: inline-grid;
    place-items: center;
    flex: none;
    inline-size: 36px;
    block-size: 36px;
    padding: 0;
    border: var(--icon-border) solid var(--header-line);
    border-radius: var(--icon-radius);
    background: none;
    color: var(--header-fg-muted);
    font: inherit;
    text-decoration: none;
    cursor: pointer;

    &:hover,
    &[aria-expanded="true"],
    &[data-force="hover"] {
      background: var(--header-hover);
      color: var(--header-fg);
    }
    & > svg {
      inline-size: 18px;
      block-size: 18px;
      fill: none;
      stroke: currentColor;
      stroke-width: 1.6;
      stroke-linecap: round;
      stroke-linejoin: round;
    }
  }

  /* A count on an icon: one bubble, as in the admin shell (16px, 10px figures, 99+ past 99), its
     left edge 2px past the glyph and its middle on the glyph's top edge, ringed in the band. */
  .cap-site-header-badge {
    --_bubble: 16px;
    position: absolute;
    top: calc(50% - 9px - var(--_bubble) / 2);
    left: calc(50% + 9px + 2px);
    display: inline-grid;
    place-items: center;
    box-sizing: border-box;
    min-inline-size: var(--_bubble);
    block-size: var(--_bubble);
    padding-inline: 3px;
    border-radius: var(--radius-full);
    background: var(--primary);
    color: var(--primary-fg);
    box-shadow: 0 0 0 2px var(--header-bg);
    font-size: 10px;
    font-weight: 700;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }

  /* The one button extra (Free audit, Join free). */
  .cap-site-header-cta {
    display: inline-flex;
    align-items: center;
    block-size: 32px;
    padding: 0 14px;
    border: 0;
    border-radius: var(--radius-m);
    background: var(--header-cta);
    color: var(--header-cta-fg);
    font: inherit;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;

    &:hover,
    &[data-force="hover"] {
      background: color-mix(in srgb, var(--header-cta), var(--header-cta-fg) 12%);
    }
  }

  /* The account: last on every site. Signed out, Sign in holds its place. */
  .cap-site-header-account {
    display: flex;
    align-items: center;
  }
  .cap-site-header .cap-site-header-avatar {
    border-radius: var(--radius-full);

    & > .cap-site-header-initials {
      display: grid;
      place-items: center;
      inline-size: 32px;
      block-size: 32px;
      border-radius: var(--radius-full);
      background: var(--header-avatar);
      color: var(--header-avatar-fg);
      font-size: 12px;
      font-weight: 600;
      letter-spacing: 0.02em;
    }
    &:hover > .cap-site-header-initials {
      opacity: 0.88;
    }
  }
  .cap-site-header-signin {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-block-size: 36px;
    padding: 0 var(--space-2);
    border-radius: var(--radius-m);
    color: var(--header-fg);
    text-decoration: none;
    white-space: nowrap;

    &:hover {
      background: var(--header-hover);
    }
    & > svg {
      display: none;
      inline-size: 18px;
      block-size: 18px;
      fill: none;
      stroke: currentColor;
      stroke-width: 1.6;
      stroke-linecap: round;
      stroke-linejoin: round;
    }
  }

  /* The phone's menu button: hidden on a desktop. */
  .cap-site-header .cap-site-header-menu {
    display: none;
  }

  /* A panel: the site's contents under its button, in the top layer, lined up with the button's
     edge and starting under the header's bottom edge, so it never covers the header. */
  .cap-site-header-panel {
    position: fixed;
    inset: auto;
    top: var(--cap-popover-y, 0px);
    left: var(--cap-popover-x, 0px);
    box-sizing: border-box;
    min-inline-size: 220px;
    max-inline-size: calc(100vw - 2 * var(--space-3));
    max-block-size: calc(100dvh - var(--header-h) - var(--space-5));
    margin: 0;
    padding: var(--space-2);
    overflow: auto;
    border: 1px solid var(--line);
    border-radius: var(--radius-l);
    background: var(--surface);
    color: var(--text);
    box-shadow: var(--shadow-l);
    font-family: var(--font-ui, var(--ui));
    font-size: var(--header-fs, 14px);
    line-height: 1.35;
  }
  /* The default contents: who you are, labelled groups of links, Sign out last. */
  .cap-site-header-panel-list {
    display: grid;
    gap: var(--space-1);
    margin: 0;
    padding: 0;
    list-style: none;

    & :is(a, button) {
      display: flex;
      align-items: center;
      gap: var(--space-4);
      inline-size: 100%;
      min-block-size: max(var(--control), var(--target));
      padding: 0 var(--space-4);
      border: 0;
      border-radius: var(--radius-m);
      background: none;
      color: var(--text);
      font: inherit;
      text-align: start;
      text-decoration: none;
      cursor: pointer;

      &:hover,
      &:focus-visible {
        background: var(--sunken);
      }
      &[aria-current] {
        background: var(--sunken);
        font-weight: 600;
      }
      &:focus-visible {
        outline: var(--focus-width) solid var(--ring);
        outline-offset: -2px;
      }
    }
  }
  .cap-site-header-panel-label {
    padding: var(--space-3) var(--space-4) var(--space-2);
    color: var(--muted);
    font-size: var(--fs-detail);
    font-weight: 500;
  }
  .cap-site-header-panel hr {
    margin: var(--space-2) var(--space-2);
    border: 0;
    border-block-start: 1px solid var(--line);
  }
  .cap-site-header-who {
    display: grid;
    gap: var(--space-1);
    padding: var(--space-3) var(--space-4) var(--space-3);

    & small {
      color: var(--muted);
      font-size: var(--fs-detail);
      overflow-wrap: anywhere;
    }
  }
  .cap-site-header-mark {
    display: grid;
    place-items: center;
    flex: none;
    inline-size: 20px;
    block-size: 20px;
    color: var(--accent-text);

    & svg {
      inline-size: 18px;
      block-size: 18px;
    }
  }
  .cap-site-header-count {
    margin-inline-start: auto;
    min-inline-size: 16px;
    block-size: 16px;
    padding-inline: 3px;
    border-radius: var(--radius-full);
    background: var(--primary);
    color: var(--primary-fg);
    font-size: 10px;
    font-weight: 700;
    line-height: 16px;
    text-align: center;
    font-variant-numeric: tabular-nums;
  }

  /* The phone's sheet: the shared dialog from the bottom, holding everything the top row does not:
     the site's own rows first (the family's apps in your admin), the switchers, the section links
     grouped as the desktop menus group them, the button extra full width; the account rows are
     pinned under the list so Sign out is always in view. */
  .cap-site-header-sheet > .cap-dialog-body {
    padding: var(--space-3);
    padding-block-end: var(--space-5);
  }
  .cap-site-header-sheet .cap-site-header-sheet-title {
    font-size: var(--fs-lead);

    /* Focus starts here when the sheet opens, so a screen reader begins at its name; it is not
       a control, so it shows no ring. */
    &:focus {
      outline: none;
    }
  }
  .cap-site-header-sheet-label {
    padding: var(--space-4) var(--space-4) var(--space-2);
    color: var(--muted);
    font-size: var(--fs-detail);
    font-weight: 500;
  }
  .cap-site-header-sheet .cap-site-header-sheet-list {
    display: grid;
    gap: var(--space-1);
    margin: 0;
    padding: 0;
    list-style: none;

    & :is(a, button) {
      display: flex;
      align-items: center;
      gap: var(--space-4);
      inline-size: 100%;
      min-block-size: max(var(--control), var(--target));
      padding: 0 var(--space-4);
      border: 0;
      border-radius: var(--radius-m);
      background: none;
      color: var(--text);
      font: inherit;
      font-size: var(--fs-body);
      text-align: start;
      text-decoration: none;

      &:hover {
        background: var(--sunken);
      }
      &[aria-current="page"],
      &[aria-current="true"] {
        background: var(--accent-12);
        color: var(--ground);
        font-weight: 600;
      }
    }
    & .cap-site-header-count {
      margin-inline-start: auto;
    }
  }
  .cap-site-header-sheet-name {
    flex: 1;
    min-inline-size: 0;
  }
  /* One label column: every row, with an icon or without, starts its label after the same 24px. */
  .cap-site-header-sheet-icon {
    display: grid;
    place-items: center;
    flex: none;
    inline-size: 24px;
    block-size: 24px;
    color: var(--accent-text);

    & svg {
      inline-size: 20px;
      block-size: 20px;
    }
  }
  [aria-current] > .cap-site-header-sheet-icon {
    color: inherit;
  }
  /* Two classes, so the dialog's own link colour (.cap-dialog-body nav a) does not win. */
  .cap-dialog-body .cap-site-header-sheet-cta {
    display: flex;
    justify-content: center;
    align-items: center;
    margin: var(--space-5) var(--space-3) 0;
    min-block-size: max(var(--control), var(--target));
    border-radius: var(--radius-m);
    background: var(--primary);
    color: var(--primary-fg);
    font-weight: 600;
    text-decoration: none;
  }
  .cap-site-header-sheet-account {
    flex: none;
    padding: var(--space-2) var(--space-3) var(--space-3);
    border-block-start: 1px solid var(--line);
  }

  /* The bottom bar of a signed-in area on a phone: up to five of its top pages, and no More,
     because the menu button opens the full list (Dustin's answer 5). Hidden on a desktop. */
  .cap-site-tabs {
    display: none;
  }

  /* Under 360px a logo with a mark shows the mark alone; the link's label keeps the name. */
  @media (max-width: 359.98px) {
    .cap-site-header-logo > :is(svg, img) ~ * {
      display: none;
    }
  }

  @media (max-width: 767.98px) {
    .cap-site-header {
      --header-side: var(--space-5);
    }
    .cap-site-header :is(.cap-site-header-switcher, .cap-site-header-sections, .cap-site-header-cta) {
      display: none;
    }
    .cap-site-header .cap-site-header-tools {
      gap: var(--space-2);

      &::before {
        display: none;
      }
    }
    /* The links on the right are hidden here, so the tools keep to the window's edge. */
    .cap-site-header[data-links="right"] .cap-site-header-sections + .cap-site-header-tools {
      margin-inline-start: auto;
    }
    .cap-site-header .cap-site-header-menu {
      display: inline-grid;
    }
    .cap-site-header-signin {
      inline-size: 36px;
      justify-content: center;
      padding: 0;

      & > svg {
        display: block;
      }
      & > .cap-site-header-signin-text {
        position: absolute;
        inline-size: 1px;
        block-size: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
      }
    }
    .cap-site-tabs {
      position: sticky;
      bottom: 0;
      z-index: var(--z-tabbar);
      display: grid;
      grid-auto-flow: column;
      grid-auto-columns: minmax(0, 1fr);
      padding: var(--space-2) var(--space-2) calc(var(--space-2) + env(safe-area-inset-bottom, 0px));
      background: var(--surface);
      border-block-start: 1px solid var(--line);

      & a {
        position: relative;
        display: grid;
        justify-items: center;
        gap: var(--space-1);
        min-block-size: var(--target);
        padding: var(--space-2) 0;
        border-radius: var(--radius-m);
        color: var(--muted);
        font-size: max(12px, var(--fs-label));
        font-weight: 600;
        text-decoration: none;
        outline-offset: -2px;

        &[aria-current="page"] {
          color: var(--accent-text);
        }
      }
      /* The icon sits in a pill (Material 3's active indicator): quiet until the tab is current,
         where it takes the accent tint as well as the colour. */
      & svg {
        box-sizing: content-box;
        inline-size: 20px;
        block-size: 20px;
        padding: 2px 14px;
        border-radius: var(--radius-full);
        fill: none;
        stroke: currentColor;
        stroke-width: 1.6;
        stroke-linecap: round;
        stroke-linejoin: round;
      }
      & a[aria-current="page"] svg {
        background: var(--accent-4);
      }
      /* The badge rides the pill's top right corner, clear of the glyph. */
      & .cap-site-header-badge {
        top: 0;
        left: calc(50% + 12px);
        box-shadow: 0 0 0 2px var(--surface);
      }
      /* Under 360px a tab is 62px: the badge sits flush on the glyph's edge with tighter padding,
         so the last tab's "99+" stays clear of the glyph and inside the window. */
      @media (max-width: 359.98px) {
        & .cap-site-header-badge {
          left: calc(50% + 10px);
          padding-inline: 2px;
        }
      }
    }
  }

  @media (prefers-reduced-motion: no-preference) {
    .cap-site-header :is(.cap-site-header-icon, .cap-site-header-link, .cap-site-header-switch, .cap-site-header-cta, .cap-site-header-signin),
    .cap-site-header-panel-list :is(a, button),
    .cap-site-header-sheet-list :is(a, button) {
      transition:
        background-color var(--dur-fast) var(--ease-out),
        color var(--dur-fast) var(--ease-out);
    }
    .cap-site-header-link::after {
      transition: inline-size var(--dur-base) var(--ease-out);
    }
    .cap-site-header-panel {
      transition:
        opacity var(--dur-fast) var(--ease-out),
        overlay var(--dur-fast) allow-discrete,
        display var(--dur-fast) allow-discrete;
      @starting-style {
        opacity: 0;
      }
      &:not(:popover-open) {
        opacity: 0;
      }
    }
  }

  @media (forced-colors: active) {
    .cap-site-header {
      border-block-end-color: CanvasText;
    }
    .cap-site-header-link:is([aria-current="page"], [data-current])::after {
      forced-color-adjust: none;
      background: CanvasText;
    }
    .cap-site-header-badge,
    .cap-site-header-count {
      forced-color-adjust: none;
      background: CanvasText;
      color: Canvas;
    }
    .cap-site-header-sheet-list [aria-current],
    .cap-site-tabs a[aria-current] {
      outline: 2px solid CanvasText;
    }
  }
}


/* The layer order, repeated so this file is safe whatever order an app imports it in: the first statement a browser reads fixes it (css/layers.css has the full note). */
@layer cap.reset, cap.tokens, cap.base, cap.components, cap.utilities;

/* The head of a page inside the shell: where you are (an optional breadcrumb), the title, one line
   saying what the page is for, and the page's actions at the right. The frame around it (the
   padding, rhythm and measure) is the shell's page region, `.cap-shell-page` or `.cap-admin-page`.
   Extracted from Carrel's PageHead and the site admin's page head. */
@layer cap.components {
  .cap-page-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-3) var(--space-5);
    min-inline-size: 0;
  }

  .cap-page-head-text {
    display: grid;
    gap: var(--space-2);
    min-inline-size: 0;
  }

  .cap-page-head-title {
    margin: 0;
    font-size: var(--fs-h2);
    font-weight: 800;
    letter-spacing: -0.025em;
    line-height: 1.2;
    overflow-wrap: anywhere;
    text-wrap: balance;
  }

  .cap-page-head-lead {
    margin: 0;
    max-inline-size: 75ch;
    color: var(--muted);
    font-size: var(--text-size);
    line-height: var(--leading);
  }

  .cap-page-head-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
  }
}


/* The layer order, repeated so this file is safe whatever order an app imports it in: the first statement a browser reads fixes it (css/layers.css has the full note). */
@layer cap.reset, cap.tokens, cap.base, cap.components, cap.utilities;

/* A panel: a bordered surface with a header on the raised tone and a padded body.
   Extracted from the Capsid Portal (dashboard/src/styles.css, "panels"). Rebuilt in 0.2 to
   the craft of shadcn's Card: header, title, description, action, content, footer, a small
   size, a first-child picture, and the Portal's padded block inside a flush body. */
@layer cap.components {
  .cap-panel {
    min-width: 0;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--line);
    border-radius: var(--radius-l);
    /* A list's hover tint stays inside the rounded corners. Rings inside a panel are
       drawn inset (row-list, table) so nothing focusable is clipped. */
    overflow: clip;

    /* shadcn's Card size="sm", and its comfortable (vega) shadow. */
    &[data-size="sm"] {
      --pad-card: 12px;
    }
    &[data-density="comfortable"],
    [data-density="comfortable"] & {
      box-shadow: var(--shadow-xs);
    }

    /* A picture first in the panel runs to its edges. */
    & > img:first-child {
      display: block;
      inline-size: 100%;
    }
  }

  /* The header sits on the raised tone instead of a rule under it. */
  .cap-panel-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    padding: calc(var(--pad-y) + 3px) var(--pad-card);
    background: var(--raised);

    & :is(h2, h3) {
      margin: 0;
      font-size: var(--fs-body);
      font-weight: 600;
      letter-spacing: -0.01em;
      line-height: var(--leading);
    }
  }

  /* shadcn's CardDescription: a muted line under the heading, across the header's width. */
  .cap-panel-desc {
    flex: 1 0 100%;
    order: 10;
    margin: calc(var(--space-2) * -1) 0 0;
    color: var(--muted);
    font-size: var(--fs-detail);
  }

  /* shadcn's CardAction: a control for the whole panel, at the header's right. */
  .cap-panel-action {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-inline-start: auto;
    margin-block: calc(var(--space-2) * -1);
  }
  .cap-panel-src + .cap-panel-action,
  .cap-panel-action + .cap-panel-more,
  .cap-panel-action + .cap-panel-src {
    margin-inline-start: 0;
  }

  /* How many the panel holds, beside its heading. */
  .cap-panel-count {
    font-size: var(--fs-detail);
    font-variant-numeric: tabular-nums;
    color: var(--dim);
  }

  /* Where the panel's data comes from and how fresh it is, pushed to the right. */
  .cap-panel-src {
    margin-left: auto;
    font-size: var(--fs-label);
    color: var(--dim);
  }

  /* A link to the full view ("All columns in Sites"), at the right of the header, after the
     source line when there is one. */
  .cap-panel-more {
    margin: calc(var(--space-1) * -1) 0 calc(var(--space-1) * -1) auto;
    font-size: var(--fs-detail);
    font-weight: 500;

    /* The link's own box is at least the target size. */
    & a {
      display: inline-flex;
      align-items: center;
      min-height: var(--target);
    }
  }
  .cap-panel-src + .cap-panel-more {
    margin-left: 0;
  }

  .cap-panel-body {
    display: grid;
    gap: var(--gap);
    padding: var(--gap) var(--pad-card);

    /* A panel holding a list or a table: the rows run to the panel's edges. */
    &[data-flush] {
      display: block;
      padding: 0;
    }
  }

  /* The Portal's padded block inside a flush body: the rows or the table touch the panel's
     edges and a paragraph, a form or a note sits in its own padding. */
  .cap-panel-pad {
    display: grid;
    gap: var(--gap);
    padding: var(--gap) var(--pad-card);

    & > * {
      margin: 0;
    }
  }

  /* shadcn's CardFooter: a tinted band under a rule, for the panel's actions or a link. */
  .cap-panel-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    padding: var(--pad-y) var(--pad-card);
    border-top: 1px solid var(--line);
    background: color-mix(in srgb, var(--sunken) 50%, transparent);
    font-size: var(--fs-detail);
  }

  /* Forced colours remove the header's tint: a rule keeps the header apart. */
  @media (forced-colors: active) {
    .cap-panel-head {
      border-bottom: 1px solid CanvasText;
    }
    .cap-panel-foot {
      border-top-color: CanvasText;
    }
  }
}


/* The layer order, repeated so this file is safe whatever order an app imports it in: the first statement a browser reads fixes it (css/layers.css has the full note). */
@layer cap.reset, cap.tokens, cap.base, cap.components, cap.utilities;

/* Show and hide: a native <details> section (.cap-disclosure), and a group row in a list
   (.cap-group) whose button shows or hides the rows it groups. Two levels at most.
   Rebuilt in 0.2 to the craft of shadcn's Accordion and Collapsible: a smooth height
   animation where the browser supports it, a flat accordion variant (rules between items,
   the chevron at the end), a pressed state and a disabled one. */
@layer cap.components {
  .cap-disclosure {
    border: 1px solid var(--line);
    border-radius: var(--radius-m);
    background: var(--surface);
    color: var(--text);
    interpolate-size: allow-keywords;

    /* Sections stacked together share their edges, as one accordion. */
    &:has(+ .cap-disclosure) {
      border-end-start-radius: 0;
      border-end-end-radius: 0;
    }
    & + .cap-disclosure {
      margin-block-start: -1px;
      border-start-start-radius: 0;
      border-start-end-radius: 0;
    }

    & > summary {
      display: flex;
      align-items: center;
      gap: var(--space-3);
      min-height: max(var(--target), var(--control));
      padding: var(--space-2) var(--pad-x);
      border-radius: inherit;
      font-size: var(--fs-body);
      font-weight: 600;
      list-style: none;
      cursor: pointer;

      &::-webkit-details-marker {
        display: none;
      }
      &::before {
        content: "";
        flex: none;
        width: 7px;
        height: 7px;
        margin-inline: 2px 3px;
        border-right: 1.5px solid currentColor;
        border-bottom: 1.5px solid currentColor;
        transform: rotate(-45deg);
      }
      &:hover,
      &[data-force="hover"] {
        background: var(--raised);
      }
      &:active,
      &[data-force="pressed"] {
        background: var(--sunken);
      }
      &:focus-visible {
        outline-offset: -2px;
      }
    }
    &[open] > summary {
      border-end-start-radius: 0;
      border-end-end-radius: 0;
      border-bottom: 1px solid var(--line);

      &::before {
        transform: rotate(45deg);
      }
    }
  }
  .cap-disclosure-body {
    display: grid;
    gap: var(--space-3);
    padding: var(--gap);
    font-size: var(--fs-body);

    & > * {
      margin: 0;
    }
  }

  /* shadcn's Accordion look: no box, a rule between items, the title underlined on hover and
     the chevron at the end, pointing down when closed and up when open. */
  .cap-disclosure[data-variant="accordion"] {
    border-width: 0 0 1px;
    border-radius: 0;
    background: transparent;

    &:last-of-type {
      border-bottom-width: 0;
    }
    & + .cap-disclosure[data-variant="accordion"] {
      margin-block-start: 0;
    }
    & > summary {
      border-radius: var(--radius-m);
      font-weight: 500;

      &::before {
        order: 2;
        margin-inline: auto 2px;
        color: var(--muted);
        transform: rotate(45deg);
      }
      &:hover,
      &[data-force="hover"] {
        background: transparent;
        text-decoration: underline;
        text-underline-offset: 4px;
      }
    }
    &[open] > summary {
      border-bottom: 0;
      border-radius: var(--radius-m);

      &::before {
        transform: rotate(225deg);
      }
    }
  }

  /* A group row: the button names what it holds and how many ("Notices, 4"). */
  .cap-group {
    display: grid;
  }
  /* The button may sit in a heading, so a person moving by heading finds the groups (the
     Portal's queue groups are an h3 holding the button). The heading adds nothing of its
     own: the button is the band. */
  .cap-group-heading {
    margin: 0;
    font: inherit;
    letter-spacing: inherit;
  }
  .cap-group-toggle {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    min-height: max(var(--target), var(--control));
    padding: var(--space-2) var(--pad-x);
    border: 0;
    border-top: 1px solid var(--line);
    background: var(--raised);
    color: var(--text);
    font-weight: 600;
    text-align: start;

    &::before {
      content: "";
      flex: none;
      width: 7px;
      height: 7px;
      margin-inline: 2px 3px;
      border-right: 1.5px solid currentColor;
      border-bottom: 1.5px solid currentColor;
      transform: rotate(-45deg);
    }
    &[aria-expanded="true"]::before {
      transform: rotate(45deg);
    }
    &:hover,
    &[data-force="hover"] {
      background: var(--sel);
    }
    &:active,
    &[data-force="pressed"] {
      background: var(--accent-soft);
    }
    &:focus-visible {
      outline-offset: -2px;
    }
    &:is(:disabled, [aria-disabled="true"]) {
      opacity: 0.5;
      pointer-events: none;
    }
  }
  .cap-group-count {
    min-width: 2.2em;
    padding: 0 var(--space-2);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-full);
    background: var(--surface);
    color: var(--muted);
    font-size: var(--fs-detail);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    text-align: center;
  }
  .cap-group-rows {
    margin: 0;
    padding: 0;
    list-style: none;

    /* Plain rows until the row-list component styles them. */
    & > li:where(:not([class])) {
      padding: var(--space-2) var(--space-4) var(--space-2) calc(var(--space-4) + 18px);
      border-top: 1px solid var(--line);
    }
  }
  /* The row that holds a second-level group. */
  .cap-group-nest {
    padding: 0;
  }
  /* The second level sits in from the first. */
  .cap-group .cap-group .cap-group-toggle {
    padding-inline-start: calc(var(--space-4) + 18px);
  }
  .cap-group .cap-group .cap-group-rows > li:where(:not([class])) {
    padding-inline-start: calc(var(--space-4) + 36px);
  }

  @media (prefers-reduced-motion: no-preference) {
    .cap-disclosure > summary::before,
    .cap-group-toggle::before {
      transition: transform var(--dur-fast) var(--ease-out);
    }
    .cap-disclosure::details-content {
      opacity: 0;
      transition:
        opacity var(--dur-base) var(--ease-out),
        content-visibility var(--dur-base) allow-discrete;
    }
    .cap-disclosure[open]::details-content {
      opacity: 1;
    }
    /* Smooth height, where the browser can animate to auto (interpolate-size) and styles
       ::details-content: the panel grows and shrinks instead of jumping. Elsewhere it
       opens at once, with the fade. */
    @supports (interpolate-size: allow-keywords) and (selector(::details-content)) {
      .cap-disclosure::details-content {
        block-size: 0;
        overflow: clip;
        transition:
          opacity var(--dur-base) var(--ease-out),
          block-size var(--dur-base) var(--ease-out),
          content-visibility var(--dur-base) allow-discrete;
      }
      .cap-disclosure[open]::details-content {
        block-size: auto;
      }
    }
  }

  @media (forced-colors: active) {
    .cap-disclosure > summary::before,
    .cap-group-toggle::before {
      border-color: CanvasText;
    }
  }
}



.gallery { max-width: 52rem; margin: 0 auto; padding: var(--space-6) var(--space-5) var(--space-9); }
.gallery .cap-page-head { margin-block-end: var(--space-5); }
.gallery-intro { max-width: var(--measure); margin: 0 0 var(--space-5); }
.gallery .cap-panel { margin-block-start: var(--space-6); }
.gallery .cap-panel-head h2 { text-wrap: balance; }
.example-chart { grid-template-columns: minmax(0, 1fr); gap: var(--space-3); background: var(--enarratio-background); color: var(--enarratio-text); }
.example-source { border-block-start: 1px solid var(--line); padding: var(--space-2) calc(var(--pad-card) - var(--pad-x)); }
.example-source pre { overflow-x: auto; margin: 0; padding: var(--space-4); border-radius: var(--radius-m); background: var(--sunken); font: 400 var(--fs-detail) / 1.6 var(--mono); }
.events { min-height: 1.5em; margin: 0; font: 400 var(--fs-detail) / var(--leading) var(--mono); overflow-wrap: anywhere; }
.primitives { display: flex; flex-wrap: wrap; gap: var(--space-6); align-items: center; }
.primitive-tables p { margin: var(--space-3) 0; }
.gallery-logo { display: inline-flex; align-items: center; gap: var(--space-3); font-weight: 600; }
.gallery-logo img { display: block; inline-size: 24px; block-size: 24px; }
.guide h2 { margin: var(--space-7) 0 var(--space-3); }
.guide p, .guide ul { max-width: var(--measure); }
.guide pre { overflow-x: auto; margin: var(--space-3) 0; padding: var(--space-4); border-radius: var(--radius-m); background: var(--sunken); font: 400 var(--fs-detail) / 1.6 var(--mono); }
