/* Visual source: capysc/keep-app f676547; native Keep kit. System fonts, no remote assets. */
/* Capy design system, owned by Keep since 2026-09-07 (CAP-640). Originally
   extracted from the monorepo's packages/ui/src/lib/tokens.css; Keep's React kit
   (src/components/) is the source of truth now, so this file is edited here
   and nothing regenerates it. */
/* Ported from the Svelte kit's tokens.css in 2026-07 (capy branch
   cvince/cli-ui-all-screens); this Keep copy is the source now (CAP-640). */
/**
 * Design tokens for every Capy CLI browser screen.
 *
 * Single source of truth, imported by base.css, which every screen loads. A
 * screen that reaches for a literal colour, font stack or duration instead of
 * a token is a bug: the next change to the palette will pass it by.
 *
 * The palette mirrors the CLI's own terminal output rather than inventing a
 * second identity: near-black on white, its exact inverse in dark mode,
 * one dim grey for secondary text, and colour used only where the CLI uses
 * it — green for ✓, red for ✗ and destructive choices, amber for ⚠.
 * Selection is inverse video, the same way a highlighted row reads in the TUI.
 *
 * Theme resolution: follow the OS, which is how the CLI serves these pages.
 * `data-theme` on <html> overrides it — nothing sets that in the CLI; the
 * workbench does, so a reviewer can force a theme.
 *
 * Fully offline: system font stack, no remote fonts, no CDNs.
 */

:root {
  color-scheme: light dark;

  /* ── Surface ─────────────────────────────────────────────────────────── */
  /* The page BENEATH the surfaces — never the surface itself. Native apps put
     a dialog on a desk rather than painting the whole window one colour, and
     the relationship (not the absolute value) is what has to survive the theme
     flip: page → paper is a small, equal step up in both modes (~1.1:1), so a
     card reads as "lifted" identically in light and dark. */
  --page: #f7f7f7;
  /* The surface things actually sit on: dialogs, cards, list bodies, fields,
     buttons. Everything with an edge is this colour. */
  --foreground: #ffffff;
  /* Raised surface: option rows, table headers, code blocks. */
  --foreground-raised: #fafafa;
  /* Pressed/selected surface for a non-inverted highlight. */
  --foreground-sunken: #f5f5f5;

  /* ── Text ────────────────────────────────────────────────────────────── */
  --ink: #171717;
  /* Secondary text: hints, captions, metadata. Clears AA on every surface it
     is used on, including --foreground-sunken — #737373 cleared it on white and
     failed at 4.35:1 on the sunken grey, which is exactly where notes sit. */
  --faint: #6b6b6b;
  /* Tertiary: disabled text, placeholder, decorative rules. */
  --fainter: #a3a3a3;

  /* ── Line ────────────────────────────────────────────────────────────── */
  --line: #e5e5e5;
  /* A border that needs to read as deliberate rather than incidental. Both of
     these are structural hairlines — they separate rows and box regions, and
     the CLI's whole look is that they are quiet. */
  --line-strong: #d4d4d4;
  /* The border of an interactive control, where the border IS the affordance:
     an input, a secondary button, a filter box. WCAG requires 3:1 for those
     because nothing else says the control is there — so this is deliberately
     darker than the structural lines, and must not be used for decoration. */
  --line-control: #8f8f8f;

  /* A control's EDGE, which is not one flat colour. A physical button catches
     light along its top and sits in its own shadow along the bottom, and that
     top-light/bottom-dark asymmetry — not the fill — is what reads as "native"
     at a 1px border. Paired with `--control-shadow`, a 2px lift so faint it is
     only felt: enough to seat the control on the page, never enough to look
     like a floating card. */
  /* Lightened on 2026-09-07 (CAP-642): the glass fill made the restyle's edges
     (#efefef / #b4b4b4) read heavy; these sit roughly 60% closer to the fill.
     Darkened a shade the same day, settled by eye with the owner. */
  --control-edge-top: #e8e8e8;
  --control-edge-bottom: #dcdcdc;
  /* A control's FILL, separate from `--foreground`: in dark the button has
     to sit a step lighter than the surface behind it or it disappears into
     it. In light they coincide (both white), which is why one token can
     serve both. */
  --control-fill: #ffffff;
  /* The glass on a white control (CAP-642, 2026-09-07): the fill runs from
     white at the top to a very light grey at the bottom, and a 1px inset ring
     catches light at the bottom-right corner and fades out toward the
     top-left, the way an app icon's glass edge does. */
  --control-gloss-top: #ffffff;
  --control-gloss-bottom: #fafafa;
  --control-inner-highlight: rgb(255 255 255 / 0.95);
  /* Which corner the inner ring fades toward: light = lit at bottom-right,
     dark = lit at top-left (owner 2026-09-07, CAP-642). */
  --control-inner-highlight-corner: top left;
  /* The destructive control's edge, same top-light/bottom-dark logic as
     `--control-edge-*`. Its own tokens rather than a `color-mix` off
     `--danger`, because the two themes move in OPPOSITE directions:
     lighter than the label in light, darker in dark. */
  --danger-edge-top: #f6dada;
  --danger-edge-bottom: #f2cccc;
  /* Softened 2026-09-07 (CAP-642): the 2px/5% lift read as a dark bottom edge
     under the glass fill. */
  --control-shadow: 0 1px 3px 1px rgb(0 0 0 / 0.06);
  /* A callout's lift off the page: very light (owner, 2026-09-07, CAP-642). */
  --callout-shadow: 0 1px 3px rgb(0 0 0 / 0.05);
  /* A callout's fill is its tint at this alpha (owner, 2026-09-07, CAP-642). */
  --callout-alpha: 50%;
  /* The 1px inner ring on a callout: lit at the top-left and bottom-right
     corners, clear between (owner, 2026-09-07, CAP-642). */
  --callout-inner-highlight: rgb(255 255 255 / 0.9);

  /* ── Status ──────────────────────────────────────────────────────────── */
  /* The CLI's ✓ (ANSI 32), ✗ (31) and ⚠ (33), matched in a web-legible ramp. */
  --ok: #25a99b;
  /* The tints are the ONLY status values that changed with the restyle, and
     they had to: they were mixed against a pure-white page, and once the page
     became `--page` they separated from it by 1.02–1.03 — which is to say,
     not at all. Lifted to 1.04–1.07 — clearly separated from the page, but
     deliberately short of the 1.11–1.22 first tried, which read as heavy
     washes rather than tints.

     Deepening a tint costs contrast for the text ON it, so each tone also
     gets an `-ink` so text can be tuned independently from status marks.
     `--danger-ink` is why `--danger` itself was
     free to become lighter for the destructive button — the two jobs stopped
     sharing one value. The success ramp uses a mint-teal hue in both themes,
     with darker light-theme ink and brighter dark-theme ink for contrast. */
  --ok-bg: #f4fffc;
  --ok-ink: #25a99b;
  --danger: #dc2626;
  --danger-bg: #fdecec;
  --danger-ink: #c62828;
  --warn: #b45309;
  --warn-bg: #fdf3d3;
  --warn-ink: #b45309;
  --info: #1d4ed8;
  --info-bg: #e9f1fe;
  --info-ink: #1d4ed8;
  /* A badge's fill is its tint at this alpha rather than the flat tint, so the
     surface behind shows through (owner, 2026-09-07, CAP-642). */
  --badge-alpha: 100%; /* owner 2026-09-07: flat tints again; the selected-row badge keeps --badge-select-alpha */
  /* On an inverse-video (selected) row the badge is the row's foreground at
     this alpha — a stronger chip than the tint badges (owner, 2026-09-07). */
  --badge-select-alpha: 80%;

  /* The one chromatic accent, and it is the CLI's: rotate's route diagram
     draws its stations in cyan (ANSI 36) and the "current" marker uses
     256-colour 43. Reserved for those two jobs — a route you are travelling,
     and the place you are now. */
  /* Light mode reads this as a blue, not a teal — the turquoise sat oddly
     against the light page. Deliberately the SAME value as `--info` today —
     the two stay separate tokens because they answer different questions (a
     route you are travelling vs. a neutral status), but the blue that reads
     best on this page is the same blue either way, and one of them drifting
     by accident would be worse than them matching on purpose. Clears AA
     comfortably (8.6:1). Dark mode's cyan below is deliberately unchanged —
     it was already right. */
  --accent: #3b82f6;
  /* The same accent one step down, for accent-coloured TEXT. `--accent` is a
     sky blue picked for the trainstop node and the sandbox code-cell ring —
     both GRAPHICS, which clear WCAG at 3:1 (measured 3.43). The
     current marker and result links are compact text and need
     4.5; this measures 4.82. Splitting them is what lets the dot keep the
     blue that was chosen without shipping a label nobody can read. */
  --accent-text: #2563eb;

  /* ── Selection ───────────────────────────────────────────────────────── */
  /* Inverse video, exactly like a highlighted row in the TUI. */
  --select-bg: var(--ink);
  --select-fg: var(--foreground);
  /* Focus ring — the one place a non-monochrome accent is allowed, so keyboard
     focus is never confused with selection. */
  --focus: #2563eb;

  /* The logo artwork is black-on-transparent; dark mode inverts it. */
  --logo-invert: 0;

  /* ── Type ────────────────────────────────────────────────────────────── */
  --font-sans: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  /* owner 2026-09-07 (CAP-642): the only monospace left — recovery phrase
     words, copy fields, invite codes, CLI commands */
  --font-code: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --capy-fur-a: #825d44;
  --capy-fur-b: #966f50;
  --capy-fur-c: #aa8260;
  --text-2xs: 0.6875rem;
  --text-xs: 11px;
  --text-sm: 12px;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.25rem;
  --text-5xl: 3rem;

  --leading-tight: 1.25;
  --leading-normal: 1.5;

  /* ── Space ───────────────────────────────────────────────────────────── */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.25rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-12: 3rem;

  /* Nearly square: the CLI draws boxes, not pills — but a small break on every
     edge is what separates "a native control" from "a terminal rule".
     `--radius` is the default for anything with an edge (buttons, fields, list
     bodies, cards); `--radius-lg` stays for surfaces that genuinely float. */
  --radius-none: 0;
  --radius-sm: 0.25rem; /* owner 2026-09-07: small chips such as key hints */
  --radius-md: 0.375rem; /* owner 2026-09-07: the default edge radius */
  --radius: var(--radius-md);
  --radius-lg: 0.5rem;

  /* ── Motion ──────────────────────────────────────────────────────────── */
  /* One easing for everything that moves, so nothing feels like a different
     product. Matches the capybara's rise. */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --dur-fast: 0.12s;
  --dur: 0.22s;
  --dur-slow: 0.45s;

  /* ── Celebration ─────────────────────────────────────────────────────── */
  /* The confetti palette is deliberately NOT the product palette: the rest of
     the system is monochrome with three status colours, and a burst drawn from
     those would read as six simultaneous status messages. It lives here rather
     than inline so it is still one definition, and so both themes get a say. */
  --confetti-1: #f59e0b;
  --confetti-2: #6debd7;
  --confetti-3: #3b82f6;
  --confetti-4: #ef4444;
  --confetti-5: #a855f7;
  --confetti-6: #ec4899;

  /* ── Layout ──────────────────────────────────────────────────────────── */
  /* The CLI opens a narrow popup window; every screen reads as one column. */
  --measure: 36rem;
  /* For screens whose content is a dense listing rather than a question. */
  --measure-wide: 52rem;
  --shadow-pop: 0 8px 32px rgb(0 0 0 / 0.12);

  /* The peeking capybara, and the room `Page` keeps clear for it.

     Two numbers that have to agree. The sprite is fixed to the viewport, so
     the only thing keeping it off the last line of a page is the clearance the
     document reserves at its foot — and those were two unrelated literals, 15px
     of art in `Capy` against 80px of clearance in `Page`, with a comment in
     `Page` claiming they matched and nothing enforcing it. Derived here, so a
     taller capybara cannot quietly start sitting on the footnote. */
  --capy-peek-size: 15px;
  --capy-peek-rows: 5;
  --capy-peek-height: calc(var(--capy-peek-size) * var(--capy-peek-rows));

  /* The slot a `Field` cuts out of its own left padding for a vendor mark.
     Fixed rather than sized to the art: wordmarks run from a square glyph to a
     wide lockup, and a slot that fitted each one would start the value's text
     in a different column on every row of the same list. */
  --field-lead: 1.5em;
}

/* OS dark, unless the workbench has forced light. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']):not(:has([data-kit-theme='light'])) {
    /* Near-black desk, very dark grey surfaces — the same one-step lift the
       light theme makes, not a bigger one. The whole ramp moves up with
       `--foreground`: raised/sunken are relative to the surface they sit on, so
       leaving them at their old values would have put a "raised" row BELOW
       the card it is raised out of. */
    --page: #0a0a0a;
    --foreground: #1a1a1a;
    --foreground-raised: #242424;
    --foreground-sunken: #2c2c2c;

    --ink: #ffffff;
    --faint: #a3a3a3;
    --fainter: #737373;

    /* Lifted with the surfaces: #262626 was a visible hairline on a #0a0a0a
       card and is invisible on a #1a1a1a one. */
    --line: #303030;
    --line-strong: #454545;
    --line-control: #6b6b6b;

    /* Same direction as light: lighter along the top. Darkened 2026-09-07
       (CAP-642), settled by eye with the owner. */
    --control-edge-top: #545454;
    --control-edge-bottom: #1c1c1c;
    --control-fill: #262626;
    --control-gloss-top: #2c2c2c;
    --control-gloss-bottom: #1f1f1f;
    --control-inner-highlight: transparent; /* owner 2026-09-07: no inner ring in dark */
    --control-inner-highlight-corner: bottom right;
    --danger-edge-top: #7a3434;
    --danger-edge-bottom: #2c1010;
    --control-shadow: 0 1px 2px rgb(0 0 0 / 0.38);
    --callout-shadow: 0 1px 3px rgb(0 0 0 / 0.35);
    --callout-inner-highlight: rgb(255 255 255 / 0.12);

    --ok: #6debd7;
    --ok-bg: #0b5e4a;
    --ok-ink: #6debd7;
    --danger: #f87171;
    --danger-bg: #300f0f;
    --danger-ink: #f87171;
    --warn: #fbbf24;
    --warn-bg: #2c1c04;
    --warn-ink: #fbbf24;
    --info: #93c5fd;
    --info-bg: #0c1a35;
    --info-ink: #93c5fd;

    --accent: #22d3ee;
    /* Dark needs no split: the cyan already clears AA for text on this ground
       (10.96:1), so this aliases it and dark renders exactly as before. */
    --accent-text: #22d3ee;
    --focus: #60a5fa;
    --logo-invert: 1;
    --shadow-pop: 0 8px 32px rgb(0 0 0 / 0.6);
  }
}

/* Explicit override, used by the workbench's theme buttons. */
:root[data-theme='dark'],
:root:has([data-kit-theme='dark']) {
  /* Kept byte-for-byte in step with the media-query block above. */
  --page: #0a0a0a;
  --foreground: #1a1a1a;
  --foreground-raised: #242424;
  --foreground-sunken: #2c2c2c;

  --ink: #ffffff;
  --faint: #a3a3a3;
  --fainter: #737373;

  --line: #303030;
  --line-strong: #454545;
  --line-control: #6b6b6b;

  /* Same direction as light: lighter along the top. Darkened 2026-09-07
     (CAP-642), settled by eye with the owner. */
  --control-edge-top: #545454;
  --control-edge-bottom: #1c1c1c;
  --control-fill: #262626;
  --control-gloss-top: #2c2c2c;
  --control-gloss-bottom: #1f1f1f;
  --control-inner-highlight: transparent; /* owner 2026-09-07: no inner ring in dark */
  --control-inner-highlight-corner: bottom right;
  --danger-edge-top: #7a3434;
  --danger-edge-bottom: #2c1010;
  --control-shadow: 0 1px 2px rgb(0 0 0 / 0.38);
  --callout-shadow: 0 1px 3px rgb(0 0 0 / 0.35);
  --callout-inner-highlight: rgb(255 255 255 / 0.12);

  --ok: #6debd7;
  --ok-bg: #0b5e4a;
  --ok-ink: #6debd7;
  --danger: #f87171;
  --danger-bg: #300f0f;
  --danger-ink: #f87171;
  --warn: #fbbf24;
  --warn-bg: #2c1c04;
  --warn-ink: #fbbf24;
  --info: #93c5fd;
  --info-bg: #0c1a35;
  --info-ink: #93c5fd;

  --accent: #22d3ee;
  /* Dark needs no split: the cyan already clears AA for text on this ground
     (10.96:1), so this aliases it and dark renders exactly as before. */
  --accent-text: #22d3ee;
  --focus: #60a5fa;
  --logo-invert: 1;
  --shadow-pop: 0 8px 32px rgb(0 0 0 / 0.6);
}

/* Honour the OS setting globally rather than per-component: a screen that adds
   an animation later should not have to remember to opt out. Components still
   swap to a genuinely different render where "instant" is not the right
   answer (the celebration capybara holds a static pose rather than snapping
   through its dance). */
@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-fast: 0s;
    --dur: 0s;
    --dur-slow: 0s;
  }
}

/* Capy design system, owned by Keep since 2026-09-07 (CAP-640). Originally
   extracted from the monorepo's packages/ui/src/lib/kit-base.css; Keep's React kit
   (src/components/) is the source of truth now, so this file is edited here
   and nothing regenerates it. */
/* Ported from the Svelte kit's base.css in 2026-07 (capy branch
   cvince/cli-ui-all-screens); this Keep copy is the source now (CAP-640). */
/* The sheet every screen loads: design tokens plus the handful of document
   rules that cannot live in a component's scoped styles.
   Fully offline: system font stack, no remote fonts, no CDNs. */


* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  height: 100%;
}

/* The desk, not the sheet: `--page`, so the `.sheet` inside `Page` (painted
   `--foreground`) reads as a surface lifted off it. Deliberately NOT a flex
   container — `Page` centres with its own `.page.center`, so this rule stays
   something a host can override without breaking any screen's layout. */
body {
  background: var(--page);
  color: var(--ink);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
}

:where(button, input, textarea, select) {
  font: inherit;
}

/* Visible focus that never doubles as the selection style: selection is
   inverse video, focus is a ring. Keeping them distinct matters here because
   these screens are keyboard-driven by design.

   No `border-radius` here. It rounded the element itself, not the ring, so a
   list row, an input or a button stopped being square for exactly as long as
   it was focused — square corners are the product's, and arrowing down a list
   is the moment a reviewer is most likely to be looking at one. The 2px offset
   already separates the ring from an inverse-video selection. */
:where(a, button, input, textarea, select, [tabindex]):focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* Inline machine state inside a sentence — a variable, a file, a command.
   Here rather than in each component's scoped styles because the identical
   declaration had been copied into a dozen `<style>` blocks, and the next
   change to inline-code type would have passed every copy by. A component with
   a reason to differ (a `Callout` title, whose code sits in a bold line) still
   overrides it; a screen has no such reason and should not try.

   `break-word`, not `anywhere`: a token breaks only when it cannot fit a line
   of its own, rather than wherever the column happens to end. An identifier
   split across two lines is not the identifier. */
code {
  font-family: var(--font-mono);
  /* A chip, so a command inside a sentence reads as a thing you could copy
     rather than as a different font someone picked. `color-mix` off `--ink`
     instead of `--foreground-sunken`: the tint then works on ANY ground —
     the page, a card, a callout's own wash — and follows the theme without a
     second token. Padding is horizontal only; vertical padding on an inline
     box does not affect line height and would just overlap the line above.
     `--space-1` rather than a bare `0.25rem`: same value, but it stays on
     the spacing scale instead of becoming this file's own literal. */
  background: color-mix(in srgb, var(--ink) 8%, transparent);
  padding: 0 var(--space-1);
  border-radius: var(--radius);
  overflow-wrap: break-word;
}

/* Ported from packages/ui/src/lib/Button.svelte's <style> block on 2026-08-30 (this React file is the source now, CAP-640). */
.button {
  font: inherit;
  font-weight: 600;
  text-transform: capitalize;
  line-height: 1;
  border: 1px solid transparent;
  border-radius: var(--radius);
  padding: var(--space-2) var(--space-4);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  position: relative;
  transition:
    background-color var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease),
    opacity var(--dur-fast) var(--ease);
}

.full {
  width: 100%;
}

.button:disabled {
  cursor: default;
  opacity: 0.45;
}

.button.busy:disabled {
  opacity: 1;
}

.button.busy {
  padding-right: calc(var(--space-4) + 1ch + var(--space-2));
}

.spinner {
  position: absolute;
  top: 50%;
  right: var(--space-4);
  transform: translateY(-50%);
}

.primary {
  background:
    linear-gradient(var(--control-gloss-top), var(--control-gloss-bottom)) padding-box,
    linear-gradient(to bottom right, var(--control-edge-top), var(--control-edge-bottom))
      border-box;
  color: var(--ink);
  box-shadow: var(--control-shadow);
}

.primary:hover:not(:disabled) {
  background:
    linear-gradient(
        color-mix(in srgb, var(--control-gloss-top) 97%, var(--ink)),
        color-mix(in srgb, var(--control-gloss-bottom) 97%, var(--ink))
      )
      padding-box,
    linear-gradient(to bottom right, var(--control-edge-top), var(--control-edge-bottom))
      border-box;
}

.primary:active:not(:disabled) {
  background:
    linear-gradient(
        color-mix(in srgb, var(--control-gloss-top) 93%, var(--ink)),
        color-mix(in srgb, var(--control-gloss-bottom) 93%, var(--ink))
      )
      padding-box,
    linear-gradient(to bottom right, var(--control-edge-bottom), var(--control-edge-top))
      border-box;
  box-shadow: none;
}

.secondary {
  background: linear-gradient(var(--control-gloss-top), var(--control-gloss-bottom));
  color: var(--faint);
  /* Half-strength line (CAP-642): the structural --line is too heavy next to the glass fill. */
  border-color: color-mix(in srgb, var(--line) 50%, var(--control-gloss-top));
}

/* The glass rim (CAP-642): a 1px ring just inside the border, white where it
   catches light and clear toward the opposite corner. Light is lit at the
   bottom-right; dark inverts to the top-left (owner 2026-09-07, CAP-642) via
   `--control-inner-highlight-corner`. A masked pseudo-element rather than an
   inset box-shadow, because a shadow cannot fade along its length. Sits under
   nothing interactive: pointer-events off. */
.primary::after,
.secondary::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: calc(var(--radius) - 1px);
  padding: 1px;
  pointer-events: none;
  background: linear-gradient(
    to var(--control-inner-highlight-corner),
    var(--control-inner-highlight),
    transparent 65%
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
}

.secondary:hover:not(:disabled) {
  color: var(--ink);
  border-color: color-mix(in srgb, var(--line-strong) 50%, var(--control-gloss-top));
}

.quiet {
  background: none;
  color: var(--faint);
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.quiet:hover:not(:disabled) {
  color: var(--ink);
}

.danger {
  background:
    linear-gradient(
        color-mix(in srgb, var(--danger) 6%, var(--control-fill)),
        color-mix(in srgb, var(--danger) 6%, var(--control-fill))
      )
      padding-box,
    linear-gradient(to bottom right, var(--danger-edge-top), var(--danger-edge-bottom))
      border-box;
  color: var(--danger);
  box-shadow: var(--control-shadow);
}

.danger:hover:not(:disabled) {
  background:
    linear-gradient(var(--danger), var(--danger)) padding-box,
    linear-gradient(to bottom right, var(--danger), var(--danger)) border-box;
  color: var(--foreground);
}

.danger:active:not(:disabled) {
  box-shadow: none;
}

.link { text-decoration: none; }

/* Ported from packages/ui/src/lib/Field.svelte's <style> block on 2026-08-30 (this React file is the source now, CAP-640). */
.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.field label {
  font-weight: 500;
  color: var(--faint);
}

/* Side padding is half the Button's, per owner ruling 2026-09-07 (CAP-642):
   vertical stays --space-2 so control height still matches Button. */
.field input,
.field textarea {
  font: inherit;
  line-height: var(--leading-normal);
  padding: var(--space-2) var(--space-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--foreground);
  color: var(--ink);
  width: 100%;
}

.box {
  position: relative;
  display: flex;
}

.field input {
  line-height: 1;
}

.field textarea {
  line-height: var(--leading-normal);
}

.boxHasLeading input {
  padding-left: calc(var(--space-2) * 2 + var(--field-lead));
}

.leading {
  position: absolute;
  left: var(--space-2);
  top: 50%;
  transform: translateY(-50%);
  width: var(--field-lead);
  height: 1.1em;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink);
  pointer-events: none;
}

.leading :is(svg) {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
}

.mono {
  font-family: var(--font-mono);
}

.field textarea {
  resize: vertical;
}

.field input::placeholder,
.field textarea::placeholder {
  color: var(--fainter);
}

/* owner 2026-09-07 (CAP-639): disabled reads as clear box, 70% ink */
.field input:disabled,
.field textarea:disabled {
  background: transparent;
  color: color-mix(in srgb, var(--ink) 70%, transparent);
}

/* A read-only fact (an organization name the person did not type) sits on
   the page with no fill; a disabled control's sunken box means "unavailable
   right now", which a fact never is. Both clear states share the same 70%
   ink text colour so a clear disabled box and a clear read-only fact read
   identically. Owner ruling 2026-09-07 (CAP-639). */
.readOnly input,
.readOnly textarea {
  background: transparent;
  color: color-mix(in srgb, var(--ink) 70%, transparent);
}

.hasError input,
.hasError textarea {
  border-color: var(--danger);
}

.foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
}

.hint {
  margin: 0;
  line-height: var(--leading-normal);
  color: var(--fainter);
}

.hint :is(code) {
  white-space: nowrap;
}

.count {
  margin: 0;
  font-size: var(--text-sm);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  line-height: var(--leading-normal);
  color: var(--fainter);
  white-space: nowrap;
}

.over {
  color: var(--danger);
}

.error {
  margin: 0;
  line-height: var(--leading-normal);
  color: var(--danger);
}

.error :is(code) {
  font-family: var(--font-mono);
  white-space: nowrap;
}


/* Layout and field/message geometry from Keep's Page, Controls, Field and MessageBox kit. */
body { line-height: var(--leading-normal); }
main, footer { width: 100%; max-width: var(--measure); margin: 0 auto; padding: var(--space-12) var(--space-5); }
main { padding-top: max(var(--space-12), calc(env(safe-area-inset-top, 0px) + var(--space-4))); display: flex; flex-direction: column; gap: var(--space-4); }
header { display: flex; align-items: center; justify-content: flex-start; gap: var(--space-4); margin-bottom: var(--space-2); }
h1 { font: inherit; font-weight: 600; line-height: var(--leading-tight); margin: 0; }
h2 { font: inherit; font-weight: 600; margin: 0; }
p { margin: 0; }
.step { margin-top: var(--space-2); padding-top: var(--space-4); border-top: 1px solid var(--line); }
.test-id { overflow-wrap: anywhere; }
.controls { display: flex; flex-direction: column; align-items: stretch; gap: var(--space-4); }
.field textarea { min-width: 0; font-family: var(--font-code); font-size: max(1rem, 16px); overflow-wrap: anywhere; }
.message { border: 1px solid var(--line); border-radius: var(--radius); padding: var(--space-4); }
.message > :not([hidden]) ~ :not([hidden]) { margin-top: var(--space-6); }
.message p + p { margin-top: var(--space-3); }
ul { padding-left: var(--space-6); margin: 0; }
li + li { margin-top: var(--space-2); }
footer { padding-top: var(--space-4); padding-bottom: max(var(--space-12), env(safe-area-inset-bottom, 0px)); color: var(--faint); display: flex; flex-direction: column; gap: var(--space-4); }
summary { cursor: pointer; }
details p { margin-top: var(--space-3); }
.usage-guide { padding-block: var(--space-4); }
.usage-guide summary { font-weight: 600; }
.usage-steps { margin: var(--space-3) 0 0; padding-left: var(--space-5); }

/* SquareLogo / Mark: the actual Keep square mark, at the header’s leading edge. */
.capy-mark { flex: 0 0 auto; filter: invert(var(--logo-invert)); }

.overall-pass { color: var(--ok-ink); }
.overall-fail { color: var(--danger-ink); }

/* Results and navigation share accessible blue/teal link colours. */
:root {
  --link-color: #1d4ed8;
  --result-pass-ink: #087461;
  --result-pass-fill: #ecfdf5;
  --result-fail-ink: #b42318;
  --result-fail-fill: #fff1f0;
  --results-text: var(--text-base);
  --results-measure: 84rem;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']):not(:has([data-kit-theme='light'])) {
    --link-color: #5eead4;
    --result-pass-ink: #6debd7;
    --result-pass-fill: #12362e;
    --result-fail-ink: #fda29b;
    --result-fail-fill: #3b1919;
  }
}
:root[data-theme='dark'],
:root:has([data-kit-theme='dark']) {
  --link-color: #5eead4;
  --result-pass-ink: #6debd7;
  --result-pass-fill: #12362e;
  --result-fail-ink: #fda29b;
  --result-fail-fill: #3b1919;
}
a { color: var(--link-color); text-decoration: none; }
.header-links { display: flex; align-items: center; gap: var(--space-4); margin-left: auto; }
.header-link { white-space: nowrap; font-weight: 600; }
.header-link span { margin-left: var(--space-1); }
.github-link { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--faint); }
.github-mark { flex: 0 0 auto; }
.results-page { gap: var(--space-8); }
.results-page header { padding-bottom: var(--space-6); border-bottom: 1px solid var(--line); }
.results-section { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
.contribution-section { max-width: var(--measure-wide); }
.section-heading { display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: var(--space-2); }
.section-heading h2 { font-size: var(--text-base); }
.section-description { color: var(--ink); max-width: var(--measure-wide); }
.table-scroll { overflow-x: auto; background: var(--foreground); }
/* Prose keeps the homepage width; the table alone extends symmetrically. */
.results-page .table-scroll { align-self: center; width: min(var(--results-measure), calc(100vw - 2 * var(--space-5))); }
.results-page table { width: 100%; border-spacing: 0; font-size: var(--text-sm); text-align: left; }
.results-page caption { text-align: left; color: var(--faint); padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--line); }
.results-page th, .results-page td { padding: var(--space-4); vertical-align: top; border-bottom: 1px solid var(--line); }
.results-page thead th { background: var(--foreground-raised); font-weight: 600; border-bottom-color: var(--line-strong); }
.results-page tbody th { font-weight: 600; }
.results-page tbody tr:last-child > * { border-bottom: 0; }
.results-page tbody tr:hover > * { background: var(--foreground-raised); }
.comparison-table { min-width: 70rem; }
.comparison-table th:first-child { min-width: 3rem; }
.comparison-table td:nth-child(2), .comparison-table td:nth-child(3) { min-width: 10.75rem; }
.comparison-table th:nth-child(4) { min-width: 9.5rem; }
.comparison-table th:nth-child(7) { min-width: 7rem; }
.cell-primary { display: block; font-weight: 500; }
.cell-secondary, .column-detail { display: block; color: var(--faint); font-weight: 400; }
.cell-secondary { margin-top: var(--space-1); }
.column-detail { margin-top: var(--space-1); }
.comparison-table tbody th { font-variant-numeric: tabular-nums; scroll-margin-top: var(--space-6); }
.verification-required { color: var(--result-fail-ink); font-weight: 600; }
.results-page code { font-size: inherit; }
.key-cell { white-space: nowrap; }
.vendor-action-cell { min-width: 15rem; max-width: 21rem; }
.verdict { display: inline-flex; align-items: center; gap: var(--space-1); border-radius: var(--radius); padding: 0.15em 0.6em; white-space: nowrap; font-weight: 600; }
.verdict-pass { color: var(--result-pass-ink); background: var(--result-pass-fill); }
.verdict-fail { color: var(--result-fail-ink); background: var(--result-fail-fill); }
.verdict-changes { color: var(--warn-ink); background: var(--warn-bg); }
.results-note { color: var(--ink); font-size: var(--results-text); max-width: var(--measure-wide); }
.results-note p + p { margin-top: var(--space-3); }
.results-page .results-note a, .results-page .contribution-section a { color: var(--ink); }
.key-legend { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--space-2) var(--space-4); margin: var(--space-3) 0; }
.key-legend dt, .key-legend dd { margin: 0; }
.section-divider { width: 100%; margin: 0; border: 0; border-top: 1px solid var(--line-strong); }
.results-footer { color: var(--ink); }
.footer-links { display: flex; flex-wrap: wrap; gap: var(--space-5); }
.footer-links a { color: var(--faint); }
.results-footer .footer-links a { color: var(--ink); }
@media (max-width: 52rem) {
  .comparison-table th:first-child { position: sticky; left: 0; z-index: 1; background: var(--foreground); border-right: 1px solid var(--line); }
  .comparison-table thead th:first-child { background: var(--foreground-raised); }
}
