/* 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, 0, 0.38, 0.9);
  --ease-in-out: cubic-bezier(0.2, 0, 0.38, 0.9);
  /* 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;
  --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);
    }
  }
  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);
  }
}

/* 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%;
      }
    }
  }

  /* 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),
        transform var(--dur-fast) var(--ease-out);
    }
    /* The press nudge, as shadcn's translate-y-px; not on a button that owns a popup. */
    .cap-btn:is(:active, [data-force="pressed"]):not([aria-haspopup]):not([aria-disabled="true"]):not(:disabled) {
      transform: translateY(1px);
    }
    .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;

/* A data table: a native table in a labelled, focusable region that scrolls sideways when
   the table is wider than its space. Rules between rows, numbers right-aligned in tabular
   figures, sortable columns with a button in the header. Extracted from the Capsid Portal
   (dashboard/src/styles.css: table.list, table.fleet, .scroll-x, .reflow). Rebuilt in 0.2 to
   the craft of shadcn's Table: row hover and selected states, a tinted footer, a bottom
   caption option, cell padding from the density tokens, and a sticky header. */
@layer cap.components {
  /* The region: named by the table's caption or its panel's heading, focusable so a
     keyboard can scroll it (WCAG 2.1.1). The ring is inset, so a panel's clip keeps it. */
  .cap-table-wrap {
    position: relative;
    max-width: 100%;
    overflow-x: auto;
    overscroll-behavior-x: contain;

    &:focus-visible {
      outline: var(--focus-width) solid var(--ring);
      outline-offset: calc(var(--focus-width) * -1);
    }
  }

  .cap-table {
    width: 100%;
    border-collapse: collapse;
    color: var(--text);
    font-size: var(--fs-body);

    & caption {
      padding: var(--space-3) var(--pad-x);
      text-align: start;
      font-weight: 600;
    }

    /* shadcn puts the caption under the table, in muted type: opt in with data-caption. */
    &[data-caption="bottom"] caption {
      caption-side: bottom;
      color: var(--muted);
      font-weight: 400;
    }

    & thead th {
      padding: calc(var(--pad-y) + var(--space-1)) var(--pad-x);
      border-bottom: 1px solid var(--line);
      color: var(--muted);
      font-size: var(--fs-label);
      font-weight: 600;
      letter-spacing: 0.06em;
      text-align: start;
      text-transform: uppercase;
      white-space: nowrap;
      vertical-align: bottom;

      /* The column the rows are sorted by reads in --text. */
      &[aria-sort="ascending"],
      &[aria-sort="descending"] {
        color: var(--text);
      }
    }

    & :is(tbody, tfoot) :is(td, th) {
      padding: calc(var(--pad-y) + var(--space-1)) var(--pad-x);
      border-bottom: 1px solid var(--line);
      text-align: start;
      vertical-align: middle;
      white-space: nowrap;
    }

    /* A checkbox cell hugs its column (shadcn drops its right padding). */
    & :is(td, th):has(> input[type="checkbox"]:only-child) {
      inline-size: 1%;
      padding-inline-end: 0;
    }

    /* shadcn's TableFooter: a tinted band under a rule, medium weight, for totals. */
    & tfoot {
      background: color-mix(in srgb, var(--sunken) 50%, transparent);
      font-weight: 500;

      & :is(td, th) {
        border-top: 1px solid var(--line);
        border-bottom: 0;
        font-weight: inherit;
      }
    }

    /* Row states, as shadcn's TableRow has them: hover, selected, and a row whose button
       reports it is expanded. Selected is never colour alone: a bar on the row's start
       edge says it too. */
    & tbody tr {
      &:hover,
      &[data-force="hover"] {
        background: var(--raised);
      }
      &:has([aria-expanded="true"]) {
        background: color-mix(in srgb, var(--sunken) 50%, transparent);
      }
      &:is([data-state="selected"], [aria-selected="true"], [data-force="selected"]) {
        background: var(--sel);

        & > :first-child {
          box-shadow: inset 3px 0 0 var(--accent);
        }
      }
    }

    /* A row header reads as an ordinary first cell, in the body's weight. */
    & tbody th {
      font-weight: 500;
    }

    & tbody tr:last-child > :is(td, th) {
      border-bottom: 0;
    }

    /* Long text wraps instead of widening the table. */
    & :is(td, th)[data-wrap] {
      min-width: 18ch;
      max-width: 36ch;
      white-space: normal;
      overflow-wrap: anywhere;
    }

    /* Numbers: right-aligned, in tabular figures, so digits line up down the column. */
    & :is(td, th)[data-num] {
      font-variant-numeric: tabular-nums;
      text-align: end;
    }
  }

  /* A sortable column: a real button in the header cell; the cell carries aria-sort. */
  .cap-table-sort {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-height: var(--target);
    margin: calc(var(--space-2) * -1);
    padding: 0 var(--space-2);
    border: 0;
    border-radius: var(--radius-s);
    background: none;
    color: inherit;
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;

    &:hover,
    &[data-force="hover"] {
      background: var(--raised);
      color: var(--text);
    }
    &:active,
    &[data-force="pressed"] {
      background: var(--sunken);
      color: var(--text);
    }

    & svg {
      flex: none;
    }
  }

  /* A sticky header: the region gets a height to scroll inside, and the header row stays
     put. Borders do not travel with a sticky cell in a collapsed table, so the rule under
     the header is drawn as a shadow. */
  .cap-table-wrap[data-sticky] {
    max-height: var(--cap-table-max-h, 24rem);
    overflow-y: auto;

    & .cap-table thead th {
      position: sticky;
      inset-block-start: 0;
      z-index: 1;
      border-bottom: 0;
      background: var(--surface);
      box-shadow: inset 0 -1px 0 var(--line);
    }
  }

  /* A table with nothing to show: one cell across, brief and muted. */
  .cap-table-empty {
    padding: var(--space-6) var(--pad-x);
    color: var(--muted);
    text-align: center;
  }

  /* The sort glyph: both arrows when unsorted, one when sorted. */
  .cap-table-sort-up,
  .cap-table-sort-down {
    fill: currentColor;
  }
  [aria-sort="ascending"] .cap-table-sort-down,
  [aria-sort="descending"] .cap-table-sort-up {
    display: none;
  }

  /* A row that opens a detail: the link in its first cell is stretched over the row, the
     same pattern as the row list. The row is one tab stop; other links, buttons and checkboxes
     (a row select) in it sit above the stretched area. */
  .cap-table tbody tr:has(.cap-table-open) {
    position: relative;

    &:has(.cap-table-open:focus-visible),
    &[data-force="focus"] {
      background: var(--sel);
      outline: var(--focus-width) solid var(--ring);
      outline-offset: calc(var(--focus-width) * -1);
    }

    & :is(a, button, .cap-check):not(.cap-table-open) {
      position: relative;
      z-index: 1;
    }
  }

  @media (prefers-reduced-motion: no-preference) {
    .cap-table tbody tr {
      transition: background-color var(--dur-fast) var(--ease-out);
    }
    .cap-table-sort {
      transition:
        background-color var(--dur-fast) var(--ease-out),
        color var(--dur-fast) var(--ease-out);
    }
  }

  .cap-table-open {
    padding: 0;
    border: 0;
    background: none;
    color: var(--text);
    font: inherit;
    font-weight: 600;
    text-align: start;
    text-decoration: none;
    outline: none;

    &::after {
      content: "";
      position: absolute;
      inset: 0;
    }
  }
  .cap-table tbody tr:hover .cap-table-open {
    text-decoration: underline;
  }

  /* Opt-in: below a container width each row becomes a card, the first cell across the
     top and every other cell under it with its column name above its value (data-label).
     Ported from the Portal's .reflow rules. Use it only once the table has been checked
     with a screen reader (decision 17): a table drawn as blocks can lose its table
     semantics. The default on a phone is the scroll region above. */
  .cap-table-wrap[data-reflow] {
    container: cap-table / inline-size;
  }

  @container cap-table (max-width: 639px) {
    .cap-table-wrap:is([data-reflow=""], [data-reflow="narrow"]) > .cap-table {
      display: block;

      & > tbody {
        display: block;
      }
      & > thead {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
      }
      & > tbody > tr {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(min(100%, 9.5rem), 1fr));
        gap: var(--space-3) var(--space-5);
        align-items: start;
        padding: var(--space-4) var(--space-5);
        border-bottom: 1px solid var(--line);

        &:last-child {
          border-bottom: 0;
        }
        & > :is(td, th) {
          display: block;
          min-width: 0;
          padding: 0;
          border: 0;
          text-align: start;
          white-space: normal;
          overflow-wrap: anywhere;
        }
        & > :first-child {
          grid-column: 1 / -1;
        }
        & > [data-label]::before {
          content: attr(data-label);
          display: block;
          margin-bottom: var(--space-1);
          color: var(--dim);
          font-size: var(--fs-label);
          font-weight: 600;
          letter-spacing: 0.06em;
          text-transform: uppercase;
        }
      }
    }
  }

  @container cap-table (max-width: 1099px) {
    .cap-table-wrap[data-reflow="wide"] > .cap-table {
      display: block;

      & > tbody {
        display: block;
      }
      & > thead {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
      }
      & > tbody > tr {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(min(100%, 9.5rem), 1fr));
        gap: var(--space-3) var(--space-5);
        align-items: start;
        padding: var(--space-4) var(--space-5);
        border-bottom: 1px solid var(--line);

        &:last-child {
          border-bottom: 0;
        }
        & > :is(td, th) {
          display: block;
          min-width: 0;
          padding: 0;
          border: 0;
          text-align: start;
          white-space: normal;
          overflow-wrap: anywhere;
        }
        & > :first-child {
          grid-column: 1 / -1;
        }
        & > [data-label]::before {
          content: attr(data-label);
          display: block;
          margin-bottom: var(--space-1);
          color: var(--dim);
          font-size: var(--fs-label);
          font-weight: 600;
          letter-spacing: 0.06em;
          text-transform: uppercase;
        }
      }
    }
  }

  /* A note under a column's name ("last 24 hours"): in the header's cell, not its name. */
  .cap-table-note {
    display: block;
    font-weight: 400;
    letter-spacing: 0;
    text-transform: none;
    color: var(--dim);
  }

  /* Small print beside a cell's main text: a site's host after its name. */
  .cap-table-aside {
    margin-inline-start: var(--space-3);
    font-size: var(--fs-detail);
    color: var(--muted);
  }

  /* A link under the table, in the panel: "All 12 sites in Sites". */
  .cap-table-foot {
    padding: var(--space-2) var(--space-4);
    border-top: 1px solid var(--line);
    font-size: var(--fs-detail);

    & a {
      display: inline-flex;
      align-items: center;
      min-height: var(--target);
    }
  }

  /* Opt-in: as the table's region narrows, columns go in order, so the table fits without
     scrolling. `data-drop="1"` goes first (below 820 px of the region), `data-drop="2"`
     next (below 620 px). Put it on the header cell and every cell of the column, or on a
     span inside a cell. Only for columns whose values are also in the row's detail: a
     dropped value is gone from the page (the Portal's Overview sites table, whose columns
     are all in the Sites view). The default on a phone is the scroll region. */
  .cap-table-wrap:has([data-drop]) {
    container: cap-table / inline-size;
  }

  @container cap-table (max-width: 820px) {
    .cap-table [data-drop="1"] {
      display: none;
    }
  }

  @container cap-table (max-width: 620px) {
    .cap-table [data-drop] {
      display: none;
    }
    .cap-table:has([data-drop]) :is(td, th) {
      padding-inline: var(--space-3);
    }
  }

  @media (forced-colors: active) {
    .cap-table-sort-up,
    .cap-table-sort-down {
      fill: 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 system map: a family of systems drawn as a capsid. Three stacked drawings share one
   viewBox (the core, the inner rings, the rim), with the lines and the inside above them,
   so going inside moves whole layers by transform and opacity alone, which the compositor
   does without repainting the shell (design section 3, "Rendering"). Chosen is a heavier
   edge and the tint, never the tint alone; private is a dashed edge and the word. */
@layer cap.components {
  .cap-system-map {
    display: grid;
    gap: var(--space-4);
    min-width: 0;
    margin: 0;
    color: var(--text);
  }

  .cap-system-map-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
  }

  .cap-system-map-title {
    font-weight: 600;
  }

  .cap-system-map-caption {
    flex-basis: 100%;
    order: 2;
    max-width: var(--measure);
    margin: 0;
    color: var(--muted);
  }

  .cap-system-map-views {
    display: inline-flex;
    gap: var(--space-2);

    & button[aria-pressed="true"] {
      border-color: var(--accent);
      background: var(--accent-soft);
      color: var(--accent-text);
    }
  }

  /* The core drawing is in the flow and sizes the stage; the others sit on top of it. */
  .cap-system-map-stage {
    position: relative;
    overflow: clip;
    overflow-clip-margin: var(--space-3);
    width: 100%;
    max-width: 36rem;
    margin-inline: auto;
  }

  .cap-system-map-layer {
    display: block;
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
    transform-origin: 50% 50%;
    /* Each drawing covers the whole stage, so the drawing itself takes no pointer: only
       its capsomers do, or the rim on top would take every click meant for the core. */
    pointer-events: none;

    &[data-layer="core"] {
      position: relative;
      height: auto;
    }
    &[data-layer="inside"] {
      opacity: 0;
      visibility: hidden;
      pointer-events: none;
    }
  }

  .cap-system-map-tile {
    outline: none;

    & polygon {
      fill: var(--surface);
      stroke: var(--line-strong);
      stroke-width: 1.2;
    }
    &[data-shared] polygon {
      fill: var(--accent-3);
      stroke: var(--accent-7);
    }
    &[data-core] polygon {
      fill: var(--accent-soft);
      stroke: var(--accent);
      stroke-width: 2;
    }
    &[data-private] polygon {
      stroke-dasharray: 4 3;
    }
    &[data-empty] polygon {
      fill: none;
      stroke: var(--line);
      stroke-dasharray: 2 4;
    }
    &[role="button"] {
      cursor: pointer;
      pointer-events: auto;
    }
    &[role="button"]:hover polygon,
    &[data-force="hover"] polygon {
      fill: var(--accent-4);
    }
    &[data-linked] polygon {
      stroke: var(--accent);
      stroke-width: 2;
    }
    &[aria-pressed="true"] polygon {
      fill: var(--accent-5);
      stroke: var(--accent-text);
      stroke-width: 2.5;
    }
    &:focus-visible polygon,
    &[data-force="focus"] polygon {
      stroke: var(--ring);
      stroke-width: 3.5;
    }
  }

  .cap-system-map-name {
    fill: var(--text);
    font: 600 12.5px var(--ui);
    text-anchor: middle;
    pointer-events: none;
  }

  .cap-system-map-tile[data-core] .cap-system-map-name {
    fill: var(--accent-text);
    font: 600 17px var(--prose);
  }

  .cap-system-map-tag {
    fill: var(--muted);
    font: 500 9.5px var(--ui);
    letter-spacing: 0.06em;
    text-anchor: middle;
    text-transform: uppercase;
    pointer-events: none;
  }

  /* A line runs in the seam between capsomers, beneath them: wide enough to fill the
     seam, so what shows is the gap itself lit up, and no label is ever under it. */
  .cap-system-map-edge {
    fill: none;
    stroke: var(--accent);
    stroke-width: 4;
    stroke-linecap: round;
    stroke-linejoin: round;
    opacity: 0;

    &[data-loose] {
      stroke-dasharray: 5 4;
    }
    &[data-on] {
      opacity: 1;
    }
  }

  .cap-system-map-boundary {
    fill: none;
    stroke: var(--accent);
    stroke-width: 1.5;
    stroke-dasharray: 6 5;
  }

  .cap-system-map-inside-title,
  .cap-system-map-box-kind {
    fill: var(--muted);
    font: 500 9px var(--mono);
    text-anchor: middle;
  }

  .cap-system-map-inside-title {
    letter-spacing: 0.06em;
    text-transform: uppercase;
  }

  .cap-system-map-wire {
    stroke: var(--line-strong);
    stroke-width: 1.2;
  }

  .cap-system-map-box {
    & rect {
      fill: var(--surface);
      stroke: var(--accent);
      stroke-width: 1.4;
    }
    &[data-store] rect {
      fill: var(--accent-3);
      stroke: var(--accent-7);
    }
  }

  .cap-system-map-box-name {
    fill: var(--text);
    font: 600 11px var(--ui);
    text-anchor: middle;
  }

  /* Inside: the core and the inner rings fold into the middle, the rim stays as context. */
  .cap-system-map[data-entered] {
    & [data-layer="core"],
    & [data-layer="inner"] {
      transform: scale(0.2);
      opacity: 0;
      visibility: hidden;
    }
    & [data-layer="rim"] {
      transform: scale(1.3);
      opacity: 0.18;
    }
    & [data-layer="rim"] .cap-system-map-tile {
      pointer-events: none;
    }
    /* The faded rim is shapes only: scaled up and clipped, its names would read as cut. */
    & [data-layer="rim"] text {
      visibility: hidden;
    }
    & [data-layer="edges"] {
      opacity: 0;
    }
    & [data-layer="inside"] {
      opacity: 1;
      visibility: visible;
    }
  }

  /* The detail line keeps its height, so choosing never moves the page below it. */
  .cap-system-map-detail {
    display: grid;
    align-content: start;
    gap: var(--space-2);
    min-height: 5.5rem;
    padding: var(--pad-y) var(--pad-x);
    border: 1px solid var(--line);
    border-radius: var(--radius-l);
    background: var(--surface);

    &[hidden] {
      display: none;
    }
  }

  .cap-system-map-detail-name {
    color: var(--accent-text);
    font: 600 var(--fs-lead) var(--prose);
  }

  .cap-system-map-detail-text {
    color: var(--muted);
  }

  .cap-system-map-detail-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
  }

  .cap-system-map-list > summary {
    width: fit-content;
    min-height: var(--target);
    color: var(--accent-text);
    cursor: pointer;
  }

  .cap-system-map-groups {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
    gap: var(--space-5);
    padding-block: var(--space-3);
  }

  .cap-system-map-group {
    min-width: 0;

    & ul {
      display: grid;
      gap: var(--space-2);
      margin: 0;
      padding: 0;
      list-style: none;
    }
    & li {
      display: grid;
      gap: var(--space-1);
      padding: var(--pad-y) var(--pad-x);
      border: 1px solid var(--line);
      border-radius: var(--radius-m);
      background: var(--surface);
    }
  }

  .cap-system-map-group-name {
    margin: 0 0 var(--space-3);
    color: var(--muted);
    font: 600 var(--fs-label) var(--ui);
    letter-spacing: 0.08em;
    text-transform: uppercase;
  }

  .cap-system-map-item-name {
    font-weight: 600;
  }

  .cap-system-map-item-tag,
  .cap-system-map-item-domain {
    color: var(--muted);
    font-size: var(--fs-detail);
  }

  .cap-system-map-item-text {
    font-size: var(--fs-detail);
  }

  .cap-system-map-item-link {
    width: fit-content;
    color: var(--accent-text);
    font-size: var(--fs-detail);
  }

  /* The plain map: the list, open, as columns; the drawing steps aside. */
  .cap-system-map[data-view="plain"] {
    & .cap-system-map-stage,
    & .cap-system-map-detail,
    & .cap-system-map-list > summary {
      display: none;
    }
  }

  @media (prefers-reduced-motion: no-preference) {
    .cap-system-map-layer {
      transition:
        transform var(--dur-slow) var(--ease-in-out),
        opacity var(--dur-slow) var(--ease-in-out),
        visibility 0s linear var(--dur-slow);
    }
    .cap-system-map[data-entered] [data-layer="inside"],
    .cap-system-map:not([data-entered]) [data-layer="core"],
    .cap-system-map:not([data-entered]) [data-layer="inner"] {
      transition-delay: 0s;
    }
    .cap-system-map-edge,
    .cap-system-map-tile polygon {
      transition:
        opacity var(--dur-base) var(--ease-out),
        fill var(--dur-fast) var(--ease-out);
    }
  }

  @media (forced-colors: active) {
    .cap-system-map-tile polygon,
    .cap-system-map-box rect,
    .cap-system-map-boundary {
      stroke: CanvasText;
    }
    .cap-system-map-tile[aria-pressed="true"] polygon,
    .cap-system-map-tile[data-linked] polygon,
    .cap-system-map-tile:focus-visible polygon,
    .cap-system-map-edge {
      stroke: Highlight;
    }
    .cap-system-map-name,
    .cap-system-map-tag,
    .cap-system-map-box-name,
    .cap-system-map-box-kind,
    .cap-system-map-inside-title {
      fill: CanvasText;
    }
  }
}
