/**
 * KOL components — atom tier
 *
 * Shared chrome primitive for interactive controls. Every chromed control
 * (Input, Stepper, Dropdown trigger, ViewToggle option, etc.) composes
 * from these classes instead of reinventing bg/border/padding/transitions.
 *
 * ─────────────────────────────────────────────────────────────────────
 * .kol-control — control shell primitive
 * ─────────────────────────────────────────────────────────────────────
 *
 * Apply: <div class="kol-control kol-control--{variant} kol-control-{size} kol-mono-{N}">
 *
 *   Always:
 *     .kol-control                 base (transition, radius 4px, layout border)
 *
 *   Variant (visual treatment) — pick one:
 *     .kol-control--filled         persistent solid bg
 *                                  (Input filled, Stepper, ViewToggle active)
 *     .kol-control--ghost          transparent at rest, border reveals on
 *                                  hover / focus-within
 *                                  (Input ghost, hex chip, inline editable)
 *     .kol-control--outline        bordered, transparent bg
 *                                  (Dropdown default trigger, segmented bar,
 *                                  also: forced-reveal "is-edited" state on
 *                                  what would otherwise be a ghost)
 *
 *   Size (padding + matched type class) — pick one:
 *     .kol-control-sm              padding 4px 12px   → pair w/ kol-mono-12
 *     .kol-control-md              padding 6px 16px   → pair w/ kol-mono-14
 *     .kol-control-lg              padding 8px 20px   → pair w/ kol-mono-16
 *
 *   State:
 *     [aria-disabled="true"] /
 *     :disabled                    auto opacity-50 + pointer-events-none
 *
 * Single visible-border tone (--kol-oq-08, opaque — user ruling 2026-08-26; was oq-16). No hover/focus escalation,
 * no `--bordered` modifier. When you want a ghost field to render
 * persistently visible (e.g. "is-edited" indicator), switch the variant
 * to `--outline` — same visual, clearer semantic.
 *
 * Type class is JSX-applied (`kol-mono-12 / -14 / -16`) — the shell controls
 * spacing + chrome, the type class controls font. Two reasons: (1) the type
 * system is a separate locked surface (typography.js), and (2) consumers can
 * override the type class for one-off cases (e.g. uppercase labels in helpers)
 * without forking the size variant.
 *
 * Radius is hard-coded to var(--kol-radius-sm) (4px). Repo invariant — never
 * exceed 4px.
 */

.kol-control {
  display: inline-flex;
  align-items: center;
  border-radius: var(--kol-radius-sm);
  /* paint from the TONE properties (kol-components-molecules.css, TONE):
   * `--filled` / `--outline` are aliases of the primary / outline tones, the
   * fallback is filled, and a toned wrapper reaches a shell with no modifier */
  background-color: var(--kol-tone-bg, var(--kol-surface-secondary));
  color: var(--kol-tone-fg, var(--kol-surface-on-primary));
  border: 1px solid var(--kol-tone-border, transparent);
  transition: background-color 150ms ease,
              border-color     150ms ease,
              color            150ms ease;
  cursor: pointer;
}

/* ─── Variants ─── */

/* `.kol-control--filled` / `.kol-control--outline` — tone aliases, see TONE.
 * `.kol-control--plain` is the bare shell (ViewToggle's inactive text chip):
 * no fill, no border, the meta ink. */

.kol-control--ghost {
  /* DEPRECATED (2026-07-08 chrome law) — ghost folded into outline; Input
   * aliases variant="ghost" → outline. Rule kept only for stray direct
   * class consumers; do not use in new code. Opaque 4% black on surface. */
  background-color: color-mix(in srgb, var(--kol-color-ab-black) 4%, var(--kol-surface-primary));
  color: var(--kol-fg-meta);
}
.kol-control--ghost:hover,
.kol-control--ghost:focus-within {
  border-color: var(--kol-fg-16);
  color: var(--kol-surface-on-primary);
}

/* Textarea modifier — flips the inline-flex shell to block so a multi-line
 * textarea takes full width. Vertical resize is real (2026-07-08): the
 * resize-grip icon is the JS drag handle (both axes); native resize is
 * off (a Firefox grip can't be hidden otherwise). atoms/Textarea.jsx. */
.kol-control.kol-control--textarea {
  /* compound selector: must out-specify the .kol-control-{size} padding
   * rules below, which otherwise win by order and double the padding
   * (shell 6/16 + inner textarea 6/16). */
  display: block;
  position: relative;
  padding: 0; /* outer label is purely a wrapper; inner textarea owns padding */
}
.kol-control--textarea > textarea {
  padding: 4px 12px;
}
.kol-control--textarea.kol-control-md > textarea { padding: 6px 16px; }
.kol-control--textarea.kol-control-lg > textarea { padding: 8px 20px; }
/* The grip — kol-icon-set-v1 `resize-grip` at the native placement. It IS
 * the resize handle (JS corner drag, both axes, in Textarea.jsx); native
 * resize is OFF because Firefox's grip can't be hidden any other way. */
.kol-textarea-resize-icon {
  position: absolute;
  bottom: 4px;
  right: 4px;
  display: inline-flex;
  color: var(--kol-fg-48);
  cursor: nwse-resize;
  touch-action: none;
}
/* Micro-glyph: at size 10 the set's 1.5 keyline scales to 0.625px — mush.
 * non-scaling-stroke pins the stroke at a crisp 1.5 CSS px regardless of
 * render size; the set asset stays keyline-conform. */
.kol-textarea-resize-icon svg {
  overflow: visible; /* round caps sit on the viewBox edge — don't clip them */
}
.kol-textarea-resize-icon svg path {
  vector-effect: non-scaling-stroke;
}
/* ─── Sizes (padding only — type class applied via JSX) ─── */

/* THE XS RUNG (ControlsXsRung, kol-monitor 2026-09-01; user: "DS should ship xs
 * size — I actually often find situations where xs would be helpful"). An
 * instrument panel runs 8px mono in a 16–20px shell and the ladder stopped at
 * sm (12px in 26–28), which is the one reason the rack's TextInput / Dropdown /
 * Selector could not collapse onto Input / Dropdown / Stepper. xs = kol-mono-8
 * (line-height 12) + 4px vertical + the shell's 1px ring = 22px, 8px sides,
 * the xs radius; the icon-only square is 22, the same height (2026-09-02).
 * Opt-in by prop only — the 2026-07-28 "dropdowns are sm at every viewport"
 * law is untouched; nothing ramps to xs by viewport. */
.kol-control-xs { padding: 4px 8px; border-radius: var(--kol-radius-xs); }
.kol-control-sm { padding: 4px 12px; }
.kol-control-md { padding: 6px 16px; }
.kol-control-lg { padding: 8px 20px; }

/* ─── Disabled ─── */

.kol-control[aria-disabled="true"],
.kol-control:disabled,
.kol-control:has(:disabled) {
  opacity: 0.5;
  pointer-events: none;
}


/* ═══════════════════════════════════════════════════════════════
 * FullscreenOverlay — atoms/FullscreenOverlay.jsx
 * The classes existed in JSX with NO rules anywhere (found 2026-07-15 via
 * the chess archive overlay — close button floated in dead space).
 * Anatomy: fixed dim backdrop → full-bleed sheet → absolute corner close.
 * ═══════════════════════════════════════════════════════════════ */

/* FLAT scrim (ColumnBrowser round, user 2026-08-27): the 55% black wash read as a
 * halo around the image in the lightbox — the backdrop is the surface, no wash. */
.kol-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--kol-z-modal, 100); /* the modal tier — the overlay contract (EditorOverlaysOnFullscreenOverlay, 2026-08-27) */
  background: var(--kol-surface-primary);
  display: grid;
  place-items: center;
  overflow-y: auto;
  padding: 24px;
}

/* the sheet HUGS its content (the consumer's panel) — backdrop clicks land
 * on .kol-overlay itself, so outside-dismiss works; the close button rides
 * the panel's corner instead of floating at the viewport edge */
.kol-overlay-sheet {
  position: relative;
  max-width: 100%;
}

/* POSITION ONLY. The control is a DS Button (quiet, icon-only) as of
 * 2026-08-01 — it brings its own box, border, colour set and states, so this
 * class stopped painting one. It used to hand-draw a bordered square around a
 * literal `×` CHARACTER, which is why the button had no hover, no focus ring
 * and no glyph.
 * `right: 0` (FullscreenOverlayCloseIdiom, kol-chess 2026-09-01): the sheet
 * hugs the consumer's panel, so a --kol-spacing-3 inset floated the X 12px
 * inside the content column's right edge — lined up with nothing the fields
 * below it establish. On the edge, the button's box sits on the same vertical
 * as the controls under it. */
/* THE CLOSE MUST OUT-STACK THE SHEET'S OWN CONTENT (BrowsePageRulingsAndSeams,
 * kol-r2b2 2026-09-02; user 2026-08-28: "doesnt work clicking close"). It sat
 * at a bare `10` — the dropdown rung — and it is the FIRST child of the sheet,
 * so any consumer content after it at 10 or above covered the corner and the
 * click landed on the panel behind. Reproduces in the DS with any tall sheet.
 * `--kol-z-overlay` (50), not the reported 60: the ladder IS the z-contract
 * (01-foundations/01-tokens § Stacking) and a hand-typed number here is the
 * thing that law exists to stop. 50 clears dropdown (10) and sticky (20) inside
 * the sheet, and a Dropdown opened from the sheet still covers it — that one
 * portals to <body> at 210, outside this stacking context, which is correct. */
.kol-overlay-close {
  position: absolute;
  top: var(--kol-spacing-3);
  right: 0;
  z-index: var(--kol-z-overlay);
}

/* ═══════════════════════════════════════════════════════════════
 * Button — atoms/Button.jsx
 *
 * Variants:
 *   .kol-btn-primary    subtle bg-fg-04 fill, daily chrome (default)
 *   .kol-btn-secondary  heavy inverted ink, CTA-adjacent (font-weight 500)
 *   .kol-btn-accent     brand-yellow filled CTA (font-weight 500)
 *   .kol-btn-outline    bordered, transparent bg
 *
 * Sizes (padding shared with .kol-control):
 *   .kol-btn-sm  4px 12px → kol-mono-12
 *   .kol-btn-md  6px 16px → kol-mono-14
 *   .kol-btn-lg  8px 20px → kol-mono-16
 *
 * Modifiers:
 *   .kol-btn-animate    disables default hover states (consumer drives)
 *   :disabled           opacity-disabled + not-allowed cursor
 *
 * Type: applied via .kol-mono-{12,14,16} on the size variant in JSX.
 * No uppercase / capitalize — author strings in the case they should
 * render (per ARCHITECTURE.md §2). Radius hard 4px (--kol-radius-sm).
 * ═══════════════════════════════════════════════════════════════ */

.kol-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--kol-radius-sm);
  white-space: nowrap;
  transition: background-color var(--kol-transition-base),
              color var(--kol-transition-base),
              border-color var(--kol-transition-base);
  cursor: pointer;
  /* THE TONE AXIS (tone-is-the-ground-axis, 2026-09-03): the paint is read
   * from `--kol-tone-*` — set on the element by its own variant / tone class,
   * or inherited from a toned wrapper — and the fallbacks are `primary`, so a
   * bare button with no tone anywhere renders exactly what it always did.
   * The bundles live in kol-components-molecules.css (TONE). */
  background-color: var(--kol-tone-bg, var(--kol-surface-secondary));
  background-image: var(--kol-tone-image, none);
  color: var(--kol-tone-fg, var(--kol-surface-on-primary));
  border: 1px solid var(--kol-tone-border, transparent);
}
/* WEIGHT IS THE VARIANT'S, not the tone's: a tone is a ground, and a ground
 * must not re-weigh a label — a sunken secondary keeps its 500 (matrix-verified
 * 2026-09-03, the one regression the first cut had). */
.kol-btn-secondary, .kol-btn-accent, .kol-btn-danger, .kol-btn.kol-tone-inverted { font-weight: 500; }

.kol-btn:focus-visible {
  outline: 2px solid var(--kol-focus-ring);
  outline-offset: 2px;
}

/* Icon-only buttons — was an inline style in Button.jsx, which beat every
 * consumer display utility (`lg:hidden` could never hide an iconOnly button).
 * Chrome belongs here, layered, so utilities stay sovereign. The flex
 * centering comes from .kol-btn; the icon-only extra is only line-height. */
.kol-btn-icon {
  /* A FIXED SQUARE ON EVERY RUNG (IconButtonNoFlexShrink, kol-monitor
   * 2026-09-02): in a shrink-to-fit flex row WebKit measured the icon-only
   * button's contribution as its GLYPH (20 + 2 border = 22), not the 32px
   * rule, so the row came out short and squeezed the square to 22×32 — the
   * gear a sliver beside a 32px Dropdown. `flex: none` fixes the measurement
   * AND the shrink; `flex-shrink: 0` alone fixed only the shrink. */
  flex: none;
  line-height: 0;
}
/* Icon-only is a GEOMETRY condition, not a variant (2026-07-28, user-ordered):
 * a lone glyph gets a PINNED SQUARE per size, equal to that size's button height
 * and independent of glyph size (padding-derived squares drifted with icon size;
 * user law: the box never moves).
 *
 * EVERY FAMILY HITS THE SAME HEIGHT AT THE SAME SIZE (user ruling 2026-09-02:
 * *"xs sm md and lg all have height in pixels that has to match"* · *"so button
 * and dropdown can align and input and whatever else"*). These squares read
 * 20/28/32/36 while the text control on the same row — padding + that size's
 * mono line-height + the 1px ring — measures 22/26/32/40. Only `md` lined up: at
 * `sm` an icon button stood 2px TALLER than the Input beside it, at `lg` 4px
 * shorter, at `xs` 2px shorter. Three families (control shell, text button,
 * segmented) already agreed on 22/26/32/40, so the squares conform to them, not
 * the other way round. Glyphs are untouched — SOLO still resolves 16/20/24. */
.kol-btn-icon.kol-btn-xs { width: 22px; height: 22px; padding: 0; }
.kol-btn-icon.kol-btn-sm { width: 26px; height: 26px; padding: 0; }
.kol-btn-icon.kol-btn-md { width: 32px; height: 32px; padding: 0; }
.kol-btn-icon.kol-btn-lg { width: 40px; height: 40px; padding: 0; }

/* ─── Variants — the ground ones are TONES now (2026-09-03) ───
 * `.kol-btn-primary` · `-secondary` · `-outline` · `-ghost` · `-grey` are
 * aliases of `.kol-tone-*`, and `-nav` · `-danger` · `-accent` carry their own
 * bundles — all in kol-components-molecules.css (TONE). What used to be eight
 * hover rules is one, reading each tone's own rungs. */
@media (hover: hover) {
  .kol-btn:not(.kol-btn-animate):not(.kol-dd-trigger):hover {
    background-color: var(--kol-tone-hover-bg, var(--kol-oq-08));
    background-image: var(--kol-tone-hover-image, none);
    color: var(--kol-tone-hover-fg, var(--kol-surface-on-primary));
    border-color: var(--kol-tone-hover-border, transparent);
  }
}
/* nav's ACTIVE keys off aria-current="page" (NavLink sets it natively) —
 * brightness only (0.11.7, user-ordered): quiet chrome states ride the
 * opacity ladder; a background block is pressed-tool language, not location.
 * Hover keeps its transient wash (feedback, not state). */
.kol-btn-nav[aria-current="page"] {
  color: var(--kol-oq-88);
}

/* xs — the panel rung (ControlsXsRung, 2026-09-01): 22px shell (20 + ring), xs radius */
.kol-btn-xs { padding: 4px 8px; border-radius: var(--kol-radius-xs); }
.kol-btn-sm { padding: 4px 12px; }
.kol-btn-md { padding: 6px 16px; }
.kol-btn-lg { padding: 8px 20px; }

/* Press feedback — one stop past hover (2026-07-08 button-states spec).
 * Outside the hover guard: :active fires on touch too. */
/* NO HOVER, NO PRESS ON A DROPDOWN TRIGGER (user ruling 2026-08-28). Every
 * variant excludes `.kol-dd-trigger` at the SOURCE. It used to be cancelled
 * downstream — kol-components-molecules re-stated each variant's rest colours
 * on :hover — and that restatement was specificity (0,4,0) against the sunken
 * tone's (0,2,0), so hovering a SUNKEN trigger repainted it in the untoned
 * fill: a light box beside two icon controls that stayed dark. */
.kol-btn:not(.kol-btn-animate):not(:disabled):not(.kol-dd-trigger):active {
  background-color: var(--kol-tone-active-bg, var(--kol-oq-16));
  background-image: var(--kol-tone-active-image, none);
}

.kol-btn:disabled,
.kol-btn[disabled] {
  opacity: var(--kol-opacity-disabled);
  cursor: not-allowed;
}

.kol-btn-animate {
  /* Consumer-driven hover behavior; disables default :hover rules above. */
}

/* .kol-btn-quiet — secondary-action icon button: dimmed at rest, brightens
 * on hover, stays dimmed when disabled (no hover lift on a non-actionable
 * state). For icon-only buttons in panel chrome (add layer, delete, etc.)
 * that shouldn't compete with primary content visually. */
.kol-btn-quiet {
  opacity: var(--kol-opacity-disabled);
  transition: opacity 150ms ease;
}
@media (hover: hover) {
  .kol-btn-quiet:not(:disabled):hover {
    opacity: 1;
  }
}

/* Icon-swap animation (Button.jsx iconLeft / iconRight with hover icon) */

.kol-icon-swap-container {
  position: relative;
  display: inline-flex;
  overflow: hidden;
}

.kol-icon-default {
  position: absolute;
  transition: opacity var(--kol-transition-base),
              transform var(--kol-transition-base);
}

.kol-icon-hover {
  position: absolute;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity var(--kol-transition-base),
              transform var(--kol-transition-base);
}

@media (hover: hover) {
  /* the trigger is excluded here too — it IS a Button, so a consumer giving it
   * a hover-icon would animate on hover, and the law is rest and open only
   * (validate-dd-trigger.mjs T1) */
  .kol-btn:not(.kol-dd-trigger):hover .kol-icon-default {
    opacity: 0;
    transform: translateY(-4px);
  }

  .kol-btn:not(.kol-dd-trigger):hover .kol-icon-hover {
    opacity: 1;
    transform: translateY(0);
  }
}


/* ═══════════════════════════════════════════════════════════════
 * Toggle Bracket — molecules/ToggleBracket.jsx
 *
 * Default-variant chrome comes from .kol-control--filled. The active
 * state overrides bg + color via this modifier — preserves the
 * accent-yellow identity while the rest of the chrome stays shell.
 * Plain variant is bare-text with no shell (defined inline in JSX).
 * ═══════════════════════════════════════════════════════════════ */

.toggle-bracket--active {
  background-color: var(--kol-accent-primary);
  color: var(--kol-accent-on-primary);
}


/* ═══════════════════════════════════════════════════════════════
 * Toggle Switch — atoms/ToggleSwitch.jsx
 *
 * Custom-shape control (not shell-based — slidey indicator geometry).
 * Default + plain variants. ON state inverts: pill bg
 * ab-white, indicator ab-black (theme-invariant).
 * ═══════════════════════════════════════════════════════════════ */

/* Toggle Switch — atoms/ToggleSwitch.jsx (2026-07-08 chrome law rewrite).
 * Bare by default: label + track, no box. Shell variants (primary/outline)
 * only when the switch must read as chrome — button geometry exactly
 * (pad 4/12 · 6/16 · 8/20 → heights 26/32/40). Type via kol-mono-{12,14,16}
 * in JSX; the old auto-uppercase label died with the box (no-auto-casing).
 * On = inverted ink (surface-on-primary track / surface-primary thumb) —
 * same identity as .kol-btn-pressed. JSX aliases: plain→bare, default→outline. */

.toggle-switch {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--kol-radius-sm);
  background-color: transparent;
  color: var(--kol-surface-on-primary);
  cursor: pointer;
  position: relative; /* the bare variant's hit extent below anchors here */
  transition: background-color var(--kol-transition-base),
              border-color var(--kol-transition-base);
}

/* THE TOUCH FLOOR (MobileTouchFloor, user ruling 2026-08-26): every pressable
 * control clears a 24px hit box (WCAG 2.5.8 AA) and the DRAWN size never
 * moves. No type floor — 10px chrome is the ruled helper/mono-10 voice. Two
 * controls sat under 24: this bare switch (a 12px track in a 1px border, 14px
 * tall) and the Slider's range input below (a 2px track was the whole target).
 * The shells ride button geometry (26/32/40) and already clear it. The extent
 * is a pseudo-element, so the layout box, the label and the track stay where
 * they are; hit-testing on a pseudo lands on its originating button. */
.toggle-switch--bare::before {
  content: '';
  position: absolute;
  inset-inline: 0;
  top: 50%;
  height: 24px;
  translate: 0 -50%;
}

.toggle-switch:focus-visible {
  outline: 2px solid var(--kol-focus-ring);
  outline-offset: 2px;
}

.toggle-switch--primary { background-color: var(--kol-surface-secondary); }
.toggle-switch--outline { border-color: var(--kol-oq-08); }

/* shells take button padding; bare stays padding 0 */
.toggle-switch--primary.toggle-switch--xs, .toggle-switch--outline.toggle-switch--xs { padding: 4px 8px; }
.toggle-switch--primary.toggle-switch--sm, .toggle-switch--outline.toggle-switch--sm { padding: 4px 12px; }
.toggle-switch--primary.toggle-switch--md, .toggle-switch--outline.toggle-switch--md { padding: 6px 16px; }
.toggle-switch--primary.toggle-switch--lg, .toggle-switch--outline.toggle-switch--lg { padding: 8px 20px; }

.toggle-switch-indicator {
  width: 20px;
  height: 12px;
  border-radius: var(--kol-radius-full);
  background-color: var(--kol-oq-16);
  position: relative;
  flex-shrink: 0;
  transition: background-color var(--kol-transition-base);
}

.toggle-switch-indicator::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 8px;
  height: 8px;
  border-radius: var(--kol-radius-full);
  background-color: var(--kol-surface-primary);
  transition: transform var(--kol-transition-base),
              background-color var(--kol-transition-base);
}

/* track scales with size */
/* disabled (SettingsPanel, 2026-08-26): the switch had no disabled treatment
 * at all — a control that cannot act in this context read exactly like one
 * that can. Same pair .kol-control:disabled wears. */
.toggle-switch:disabled {
  opacity: var(--kol-opacity-disabled);
  cursor: not-allowed;
}

.toggle-switch--xs .toggle-switch-indicator { width: 12px; height: 8px; }
.toggle-switch--xs .toggle-switch-indicator::after { width: 4px; height: 4px; }
.toggle-switch--sm .toggle-switch-indicator { width: 16px; height: 10px; }
.toggle-switch--sm .toggle-switch-indicator::after { width: 6px; height: 6px; }
.toggle-switch--lg .toggle-switch-indicator { width: 24px; height: 14px; }
.toggle-switch--lg .toggle-switch-indicator::after { width: 10px; height: 10px; }

.toggle-switch[data-state='on'] .toggle-switch-indicator {
  background-color: var(--kol-surface-on-primary);
}

.toggle-switch[data-state='on'] .toggle-switch-indicator::after {
  transform: translateX(8px);
  background-color: var(--kol-surface-primary);
}

.toggle-switch--xs[data-state='on'] .toggle-switch-indicator::after { transform: translateX(4px); }
.toggle-switch--sm[data-state='on'] .toggle-switch-indicator::after { transform: translateX(6px); }
.toggle-switch--lg[data-state='on'] .toggle-switch-indicator::after { transform: translateX(10px); }


/* ═══════════════════════════════════════════════════════════════
 * Toggle Checkbox — atoms/ToggleCheckbox.jsx
 *
 * Custom-shape control. Active state inverts: box bg ab-white,
 * no border, check stroke ab-black (theme-invariant).
 * ═══════════════════════════════════════════════════════════════ */

.toggle-checkbox {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
}

.toggle-checkbox input {
  display: none;
}

.toggle-checkbox-indicator {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border-radius: 3px;
  border: 1px solid color-mix(in srgb, var(--kol-surface-on-primary) 25%, transparent);
  background-color: transparent;
  transition: background-color var(--kol-transition-base),
              border-color var(--kol-transition-base);
}

.toggle-checkbox-indicator svg {
  width: 10px;
  height: 8px;
  stroke: var(--kol-color-ab-black);
  opacity: 0;
  transition: opacity var(--kol-transition-base);
}

.toggle-checkbox.is-active .toggle-checkbox-indicator {
  background-color: var(--kol-color-ab-white);
  border-color: transparent;
}

.toggle-checkbox.is-active .toggle-checkbox-indicator svg {
  opacity: 1;
}

/* ON MEDIA (ContentFiltersCollection, kol-r2b2 2026-08-27): a hairline box
 * over a photo nearly vanishes unchecked — the same reason .kol-media-control
 * carries its own fill. The unchecked box takes that plate; checked is
 * unchanged (white plate, black check). */
.toggle-checkbox--media .toggle-checkbox-indicator {
  background-color: var(--kol-oq-12);
  border-color: transparent;
}
.toggle-checkbox--media.is-active .toggle-checkbox-indicator {
  background-color: var(--kol-color-ab-white);
}


/* ═══════════════════════════════════════════════════════════════
 * Slider — molecules/Slider.jsx
 *
 * Range-input with custom track + thumb pseudo-elements. One bare
 * inline-flex row (label · track · editable readout) — the sole slider
 * look. Bordered `default` and chip `subtle` variants retired
 * (2026-07-08); the phantom `.control-slider-minimal` the JSX used to
 * emit never had a rule, so real `variant="minimal"` call sites now
 * resolve here and finally get their intended layout.
 *
 * Track color is exposed as `--kol-slider-track` (set on `.slider-black`)
 * so consumers can override per-instance via inline style or className;
 * default = fg-64 (dim grey).
 * ═══════════════════════════════════════════════════════════════ */

.control-slider {
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  padding: 0;
  border-radius: 0;
  background-color: transparent;
  border: none;
  height: 24px;
  transition: color var(--kol-transition-base);
}

.control-slider label { color: var(--kol-fg-80); }
.control-slider span  {
  color: var(--kol-surface-on-primary);
  padding: 8px 0;
  border-radius: 4px;
  background-color: var(--kol-surface-secondary);
  width: 64px;
  text-align: center;
  flex-shrink: 0;
}

.slider-black {
  /* A FALLBACK, NOT A DECLARATION (SliderTrackVariableNotForwarded, kol-mirror
   * 2026-08-30). This was `--kol-slider-track: var(--kol-fg-64)` set ON the
   * element, and an element's own declaration beats anything it would inherit —
   * so the override the component's docstring promised was unreachable from any
   * ancestor. Consumers were reaching into `.slider-black` by specificity from
   * their own stylesheets to get a track colour. `.kol-slider-dual-playhead`
   * below had it right from the start; this now matches it. */
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  cursor: pointer;
  /* 24px, not 2px (the touch floor, user ruling 2026-08-26 — see ToggleSwitch
   * above). The INPUT is the hit target and it was exactly the track's 2px;
   * the drawing is unchanged: the 2px track centres inside the box by the UA's
   * own `align-self: center` on the runnable track, and the thumb stays pinned
   * to the track. The row (.control-slider) is already 24px, so nothing moves. */
  height: 24px;
  /* pan-y, not none (SliderCoarsePointerHeight, kol-mirror 2026-09-01): inside
   * a scrolling sheet `auto` let the scroller claim any drag a few degrees off
   * horizontal, so the thumb never moved and the sheet scrolled instead. The
   * vertical gesture must still reach the scroller, or the sheet stops
   * scrolling wherever a fader sits under the thumb. Does not inherit — it has
   * to be on the input, and no prop reaches the input. */
  touch-action: pan-y;
}

.slider-black::-webkit-slider-runnable-track {
  background: var(--kol-slider-track, var(--kol-fg-64));
  height: 2px;
  border-radius: 0;
}

.slider-black::-moz-range-track {
  background: var(--kol-slider-track, var(--kol-fg-64));
  height: 2px;
  border-radius: 0;
}

.slider-black::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  background: var(--kol-surface-on-primary);
  height: 12px;
  width: 12px;
  border-radius: 50%;
  margin-top: -5px;
  border: none;
}

.slider-black::-moz-range-thumb {
  appearance: none;
  background: var(--kol-surface-on-primary);
  height: 12px;
  width: 12px;
  border-radius: 50%;
  border: none;
}

/* ── Dual-thumb range (SliderDualThumbAndPlayhead, kol-mirror 2026-08-30) ────
 * Two native ranges stacked on ONE track — the only way two thumbs share a
 * rail. The INPUT takes `pointer-events: none` so the dead space between the
 * thumbs does not swallow a track click, and each THUMB takes it back; without
 * that pair the upper input covers the lower one entirely.
 *
 * The two must be tellable apart at a glance — you have to know which end you
 * grabbed. In-mark is hollow (surface fill, ink ring), out-mark is solid, which
 * is the treatment mirror's local copy proved over months.
 *
 * The playhead colour is a TOKEN. Mirror hardcoded `#2dd4bf`, which is the
 * defect this move exists to end: a literal cannot follow a theme or a
 * consumer's brand. `--kol-slider-playhead` defaults to the accent and is
 * overridable per instance. */
.kol-slider-dual {
  position: relative;
  width: 100%;
  height: 20px;
}

.kol-slider-dual-rail {
  position: absolute;
  left: 0;
  right: 0;
  top: 9px;
  height: 2px;
  background: var(--kol-slider-track, var(--kol-surface-on-primary));
}

.kol-slider-dual-playhead {
  position: absolute;
  top: 0;
  width: 3px;
  height: 20px;
  margin-left: -1.5px;
  border-radius: 1px;
  background: var(--kol-slider-playhead, var(--kol-accent-primary));
  z-index: 4;
  cursor: grab;
}
.kol-slider-dual-playhead:active { cursor: grabbing; }

.kol-slider-range {
  position: absolute;
  inset: 0;
  width: 100%;
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  pointer-events: none;
}
.kol-slider-range::-webkit-slider-runnable-track { background: transparent; height: 2px; }
.kol-slider-range::-moz-range-track { background: transparent; height: 2px; }

.kol-slider-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  pointer-events: auto;
  height: 12px;
  width: 12px;
  border-radius: 50%;
  margin-top: -5px;
  cursor: pointer;
}
.kol-slider-range::-moz-range-thumb {
  appearance: none;
  pointer-events: auto;
  height: 12px;
  width: 12px;
  border-radius: 50%;
  cursor: pointer;
}

/* in = hollow, out = solid */
.kol-slider-range--in::-webkit-slider-thumb {
  background: var(--kol-surface-primary);
  border: 1.5px solid var(--kol-surface-on-primary);
}
.kol-slider-range--in::-moz-range-thumb {
  background: var(--kol-surface-primary);
  border: 1.5px solid var(--kol-surface-on-primary);
}
.kol-slider-range--out::-webkit-slider-thumb {
  background: var(--kol-surface-on-primary);
  border: none;
}
.kol-slider-range--out::-moz-range-thumb {
  background: var(--kol-surface-on-primary);
  border: none;
}


/* ═══════════════════════════════════════════════════════════════
 * Sidenav primitives — promoted from kol-framework.css 2026-04-30.
 *
 * .kol-sidenav-hop          row primitive (icon + label, full-width)
 * .kol-sidenav-hop-icon     icon slot inside the hop
 * .kol-sidenav-hop-label    label slot — collapses to display:none in
 *                           rail mode (responsive cascade in framework.css)
 * .kol-sidenav-link         nav link with active-dot indicator
 *
 * Layout/sizing/responsive collapse rules stay in kol-framework.css
 * (they're page-grid concerns, not component chrome).
 * ═══════════════════════════════════════════════════════════════ */

.kol-sidenav-hop {
  color: var(--kol-fg-80);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  transition: color 150ms ease, background 150ms ease;
}

.kol-sidenav-hop:hover {
  color: var(--kol-fg-96);
}

.kol-sidenav-hop.is-active {
  color: var(--kol-surface-on-primary);
}

.kol-sidenav-hop-icon {
  color: var(--kol-fg-80);
  transition: color 150ms ease;
}

.kol-sidenav-hop:hover .kol-sidenav-hop-icon {
  color: var(--kol-surface-on-primary);
}

.kol-sidenav-hop.is-active .kol-sidenav-hop-icon {
  color: var(--kol-accent-primary);
}

/* .kol-sidenav-group — RETIRED here 0.52.1 (sidenav-nested-groups): the
 * elder's `padding: 4px 0` wrapper, dead since the 2026-08-09 port dropped
 * group rendering. The group's box now lives in kol-framework.css beside the
 * hop and list rules; a consumer that nests the framework import under the
 * theme's layer (the showcase does) had this rule outranking that one. */

/* The rail leaf had NO focus treatment at all (ThreeColumnEditorShell §3,
 * kol-fxr 2026-08-15) — only .is-active and its dot — so it fell through to
 * the browser's default ring, which a user reported as "a weird highlight bug".
 *
 * INSET 1px, matching its sibling .shell-nav-item (kol-components-workshop.css)
 * rather than .kol-btn's `2px / offset 2px`: an offset ring blooms OUTSIDE a
 * dense nav row and collides with the rows above and below. Same element role,
 * same treatment — this closes an inconsistency, it does not mint a policy.
 *
 * NB this is deliberately NOT an answer to FocusRingsInConsumers, which asks
 * whether a consumer can switch the whole focus system off with one token.
 * That is a live ruling; this rule only stops one leaf being the odd one out. */
.kol-sidenav-link:focus-visible {
  outline: 1px solid var(--kol-focus-ring-quiet);
  outline-offset: -1px;
}

.kol-sidenav-link.is-active {
  color: var(--kol-surface-on-primary);
}

.kol-sidenav-link.is-active::before {
  content: "";
  position: absolute;
  left: var(--kol-sidenav-dot-left, 2px);
  top: 50%;
  transform: translateY(-50%);
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--kol-accent-primary);
}


/* ═══════════════════════════════════════════════════════════════
 * Page section divider — promoted from kol-framework.css.
 * Helper class on the Divider atom when used as a PageSection lead-in.
 * Hides on the first PageSection of a page.
 * ═══════════════════════════════════════════════════════════════ */

.kol-page-section-divider {
  margin-top: -64px;
  margin-bottom: 64px;
}

.kol-page-section:first-of-type > .kol-page-section-divider {
  display: none;
}


/* ═══════════════════════════════════════════════════════════════
 * Icon-swap utility — promoted from residual kol-components.css.
 * Used by molecules/SectionLabel.jsx and any <button>/<a> with
 * .icon-default / .icon-hover children.
 * (Distinct from .kol-icon-swap-* used internally by atoms/Button.jsx
 * — see Button section above.)
 * ═══════════════════════════════════════════════════════════════ */

.icon-default {
  transform: translate(0, 0) scale(1);
  opacity: 1;
  transition: transform var(--kol-transition-base) ease,
              opacity var(--kol-transition-base) ease;
}

.icon-hover {
  transform: translate(-100%, -100%) scale(0.8);
  opacity: 0;
  transition: transform var(--kol-transition-base) ease,
              opacity var(--kol-transition-base) ease;
}

button:hover .icon-default,
a:hover .icon-default,
.section-label-wrapper:hover .icon-default {
  transform: translate(100%, 100%) scale(0.8);
  opacity: 0;
}

button:hover .icon-hover,
a:hover .icon-hover,
.section-label-wrapper:hover .icon-hover {
  transform: translate(0, 0) scale(1);
  opacity: 1;
}

/* ─────────────────────────────────────────────────────────────────────
 * CopyButton — atoms/CopyButton.jsx
 *   THE copy-to-clipboard control (2026-08-09 user ruling): the 32×32
 *   icon button CodeBlock carried privately since the 2026-07-28 elder
 *   replication — 16px icon + 0.5rem padding. The old Copy/Copied label
 *   chip (one-off SVGs, auto-uppercase) is retired; CodeBlock composes
 *   this atom and .kol-codeblock-copy only positions it in-frame.
 * ───────────────────────────────────────────────────────────────────── */
.kol-copy-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.5rem;
  background: color-mix(in srgb, var(--kol-surface-on-primary) 2%, transparent);
  border: none;
  border-radius: var(--kol-radius-sm);
  color: color-mix(in srgb, var(--kol-surface-on-primary) 72%, transparent);
  cursor: pointer;
  transition: background-color var(--kol-transition-base), color var(--kol-transition-base);
}

.kol-copy-btn:hover {
  background: color-mix(in srgb, var(--kol-surface-on-primary) 8%, transparent);
  color: var(--kol-surface-on-primary);
}

/* ─────────────────────────────────────────────────────────────────────
 * TWO SEPARATE CONTROLS (user ruling 2026-08-15). They shared .kol-copy-btn
 * as a base and every edit to one moved the other; the split is the point.
 * ───────────────────────────────────────────────────────────────────── */

/* over MEDIA — boxed, opaque plate, full ink. Sits on a photograph, so it
 * needs its own fill to stay legible and never dims at rest. */
.kol-media-control {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: none;
  border-radius: var(--kol-radius-sm);
  background: var(--kol-oq-12);
  color: var(--kol-surface-on-primary);
  cursor: pointer;
  /* the house curve, not --kol-transition-base's generic material ease: it
   * settles instead of stopping, which is what reads as smooth on the press. */
  transition: background-color 1500ms var(--kol-ease-bounce), color 1500ms var(--kol-ease-bounce);
}
/* hover goes LIGHTER, not darker (user ruling 2026-08-15) — oq mixes ink into
 * the surface, so up the scale is up in brightness. */
/* the brand anchor, with the theme's own palette as the fallback — the yellow
 * lives in kol-brand-color.css which the consumer supplies, so the theme must
 * not hard-depend on it. NOT --kol-accent-primary, which resolves to white. */
.kol-media-control:hover {
  background: var(--kol-surface-tertiary);
  color: var(--kol-color-yellow-300, var(--kol-palette-yellow));
}
/* The press RAMPS. The rule that is being entered owns the curve going in, so
 * this one carries a slow ease-in-out; the base rule's bounce owns coming back
 * out. One bezier cannot do both — the bounce covers most of its distance in
 * the first quarter, which is exactly why the press read as instant. */
/* the glyph GROWS on interaction: 18 at rest, 20 on hover and press, inside the
 * unchanged 32px box. The SOLO md rung (20) stays the base and rest scales DOWN
 * to it — 20 × 0.9 = 18 — so the ladder value is still what the component asks
 * for. Scale, not a size swap: the box never moves (2026-07-28 law) and an SVG
 * re-render would not tween. */
.kol-media-control .kol-action-glyph {
  transform: scale(0.9);
  transition: transform 500ms var(--kol-ease-house);
}
.kol-media-control:hover .kol-action-glyph,
.kol-media-control--pressed .kol-action-glyph {
  transform: scale(1);
}

.kol-media-control:active,
.kol-media-control--pressed {
  background: var(--kol-oq-32);
  transition: background-color 1500ms cubic-bezier(0.65, 0, 0.35, 1);
}

/* in TEXT — bare, no box, no fill. Rests on the SAME ink as the copy beside it
 * (--kol-fg-meta = fg-48): these sit on a solid plate, not on a photograph, so
 * the transparent scale is safe here and matching the text is worth more than
 * the opaque rung. `--on` is a toggle's held state, not a hover. */
.kol-inline-control {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--kol-oq-64);
  cursor: pointer;
  transition: color var(--kol-transition-base);
}
/* ONE rest tone for every inline control (user ruling 2026-08-15): the star and
 * the trash beside it sit at the same weight until you reach for one. They part
 * on HOVER — a neutral control brightens, the accent control goes yellow. Yellow
 * is the accent's alone, on hover and on its on-state; spending it on every
 * hover left the on-state with nothing of its own to say. */
.kol-inline-control:hover { color: var(--kol-oq-96); }

.kol-inline-control--accent:hover,
.kol-inline-control--on,
.kol-inline-control--on:hover { color: var(--kol-color-yellow-300, var(--kol-palette-yellow)); }

/* TWO GLYPHS, not one glyph and a fill (user ruling 2026-08-15: *"just create
 * another star"*, *"leave the one alone in cards"*). `star` stays stroke-only
 * and `star-solid` is the filled twin; a control that has an on-state names
 * both and ActionButton crossfades them.
 *
 * The alternative — filling the shared `star.svg` and unfilling it here — was
 * tried and reverted: a glyph that ships filled renders filled in every
 * consumer that never asked for a state, and the DS cannot see those.
 *
 * The `--on` fill below is kept for a control that ships ONE glyph and carries
 * its state in the class instead; it is a no-op on a stroke-only icon. */
.kol-inline-control svg path { transition: fill var(--kol-transition-base); }
.kol-inline-control--on svg path { fill: currentColor; }

/* ─────────────────────────────────────────────────────────────────────
 * Figure — atoms/Figure.jsx
 *   Caption'd media shell for long-form prose (label above, aspect-locked
 *   frame, figcaption below). Ported from the monorepo prose sheet; the
 *   label's text-transform:uppercase was dropped per the KOL no-auto-
 *   casing rule — author labels in their final case.
 * ───────────────────────────────────────────────────────────────────── */
.kol-prose-figure {
  margin-block: 2rem;
}

/* .kol-caption-label / .kol-caption-text retired 2026-07-28 — Figure rides
 * the kol-doc-eyebrow / kol-doc-caption roles (one caption definition). */

/* ── target-file: packages/theme/kol-components-atoms.css ──────────────
 * Append directly after the .kol-btn-quiet block
 * (after `.kol-btn-quiet:not(:disabled):hover { opacity: 1; }`). */

/* .kol-btn-pressed — toggle-on state (Button `pressed` prop, aria-pressed).
 * Solid inverted ink so a pressed toggle reads as unmistakably lit
 * (2026-07-08 button-states spec). Pairs with `quiet`: Button drops
 * kol-btn-quiet while pressed, giving the tool-palette dim-at-rest →
 * lit-when-active idiom. Always composes with a variant class — the
 * variant supplies the border that suppresses UA button chrome. */
.kol-btn-pressed {
  background-color: var(--kol-tone-pressed-bg, var(--kol-surface-on-primary));
  background-image: var(--kol-tone-pressed-image, none);
  color: var(--kol-tone-pressed-fg, var(--kol-surface-primary));
}

/* ─────────────────────────────────────────────────────────────────────
 * Ported from kol-monorepo apps/brand/src/components/framework/
 * kol-framework.css. NOT ported (already in kol-typography.css, richer
 * DS versions win): .kol-prose pre, .kol-prose code, .kol-prose pre code.
 * Also already in kol-components-atoms.css: .kol-prose-figure,
 * .kol-caption-label, .kol-caption-text.
 * No text-transform rules existed in the source CSS; the components'
 * Tailwind `uppercase` classes were dropped per the KOL no-auto-casing
 * rule — author labels/pullouts in their final case.
 * The type-specimen kit rules (TypeSample / TypeSpecCard / TextPressure)
 * moved to kol-components-foundry.css 2026-07-09 with the components'
 * move to @kolkrabbi/kol-foundry.
 * ───────────────────────────────────────────────────────────────────── */

/* ─── ProsePreview — atoms/ProsePreview.jsx ───
   Prose chrome beyond the base .kol-prose contract. */
.kol-prose-indented {
  padding-left: 32px;
  border-left: 1px solid var(--kol-fg-16);
}

.kol-prose-pullout {
  padding: 16px 0;
  margin: 32px 0;
  border-top: 1px solid var(--kol-fg-08);
  border-bottom: 1px solid var(--kol-fg-08);
}

/* ───── IconFrame — a static square frame holding one icon ─────
 * Promoted 2026-07-30 from kol-website's SectionTitle, where the pattern was an
 * anonymous <span> wearing `kol-btn kol-btn-secondary kol-btn-md kol-btn-icon`.
 *
 * These classes exist INSTEAD of reusing kol-btn-*, and that is the whole point
 * of the component. On a <span>, the button's hover/active/focus/disabled rules
 * are suppressed by the ELEMENT — "no states" was an accident of the tag, one
 * element change away from leaking back. Here there are simply no state rules
 * to inherit: no :hover, no :active, no :focus-visible, no :disabled, no
 * transition, no cursor.
 *
 * Background/foreground/geometry are byte-identical to the kol-btn equivalents
 * (declarations mirrored from .kol-btn-{variant} above and the pinned squares
 * at .kol-btn-icon.kol-btn-{size}), so surfaces that migrate don't shift a
 * pixel. `primary` and `secondary` are inverse pairs — they flip with the theme
 * from the tokens alone, no per-theme props. */
.kol-icon-frame {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  line-height: 0;
  border-radius: var(--kol-radius-sm);
  padding: 0;
  /* paint from the TONE properties (kol-components-molecules.css, TONE); the
   * variant classes are aliases of the tones, the fallback is the `secondary`
   * VARIANT's paint (the `inverted` tone) — the frame's default since it was promoted — and a toned wrapper reaches a
   * frame with no variant of its own. No states, as ever. */
  background-color: var(--kol-tone-bg, var(--kol-surface-on-primary));
  color: var(--kol-tone-fg, var(--kol-surface-primary));
  border: 1px solid var(--kol-tone-border, transparent);
}

/* An ACTIONABLE frame — `onClick`/`href` (user ruling 2026-08-01: *"you dont
 * use a button… you use the ICON COMPONENT… it has no interactive states"*).
 *
 * The "no states" property used to be an accident of the element: a <span>
 * cannot be hovered-as-a-control, so the frame's own docstring warned that
 * reusing kol-btn-* on one suppressed states BY TAG rather than by contract.
 * Handing the frame a click makes it a <button>/<a>, which brings the user
 * agent's own chrome back with it — so the reset is declared here and the
 * absence of state rules stays a PROPERTY OF THE CLASS, which is what the
 * original ruling actually asked for. There is deliberately no :hover, no
 * :active and no :focus fill below: a frame that lights up is a Button. */
button.kol-icon-frame,
a.kol-icon-frame {
  appearance: none;
  -webkit-appearance: none;
  font: inherit;
  cursor: pointer;
  text-decoration: none;
}

/* Radius (2026-07-30 ruling): either the system's 4px or a full round, nothing
 * between. `sm` is the default and carries no class — it's on .kol-icon-frame
 * above. `full` is a deliberate exception to the hard 4px repo invariant,
 * because a round frame is its own chrome idiom (edge-straddling controls,
 * avatars, status dots) rather than a radius tweak.
 *
 * Shared with `.kol-btn-radius-full` (component 0.20.0): Button gained the same
 * two-value `radius` prop, and an icon-only Button IS this control with states.
 * ONE rule rather than a second copy of the value — no --kol-* token holds a
 * full round today (checked, zero hits), and inventing one for a single value
 * used by one rule adds a name without adding a source of truth. If such a
 * token ever lands beside --kol-radius-sm, this one selector reads it. */
.kol-icon-frame-radius-full,
.kol-btn-radius-full { border-radius: 9999px; }

/* pinned squares — the box never moves with glyph size (user law 2026-07-28),
 * on the size's HEIGHT so a frame lines up with the Button, Input and Dropdown
 * beside it (user ruling 2026-09-02 — see .kol-btn-icon above). 22/26/32/40. */
.kol-icon-frame-xs { width: 22px; height: 22px; }
.kol-icon-frame-sm { width: 26px; height: 26px; }
.kol-icon-frame-md { width: 32px; height: 32px; }
.kol-icon-frame-lg { width: 40px; height: 40px; }

/* `.kol-icon-frame-{primary,secondary,accent,outline,ghost,nav,grey,danger}` — tone aliases, see TONE */
.kol-icon-frame-plate {
  background: var(--kol-fg-ab-12, rgba(0, 0, 0, 0.4));
  backdrop-filter: blur(4px);
  color: var(--kol-surface-on-primary);
  border: 0; /* not a tone — its own glass, no ring (as before the tone axis) */
}

.kol-icon-frame-grey {
  background: var(--kol-oq-12);
  color: var(--kol-surface-on-primary);
  border: 1px solid transparent;
}
.kol-icon-frame-danger {
  background: var(--ui-error);
  color: var(--kol-color-ab-white);
  border: 1px solid transparent;
}

/* PlayDisc — the Finder play/pause disc on a media tile (PlayDiscAndVideoBar,
 * kol-r2b2 2026-08-27): a round `IconFrame secondary lg radius="full"`, hidden
 * at rest, shown on hover of the tile (`.kol-media-tile`) or on focus, over
 * full-bleed artwork. Declared AFTER the variants so it wins in-layer — the
 * consumer no longer restates bg-fg-inverse-64 / border-fg-96 beside the
 * variant. Absolute colours: it sits on artwork, not on the theme. NO
 * backdrop-filter — it flips the tile onto a composited layer on hover and the
 * artwork's saturation jumps (Chrome). */
.kol-play-disc {
  background-color: color-mix(in srgb, var(--kol-surface-on-inverse) 64%, transparent);
  color: var(--kol-color-ab-white);
  border: 2px solid color-mix(in srgb, var(--kol-surface-on-primary) 96%, transparent);
  box-shadow: 0 0 4px 3.5px rgba(0, 0, 0, 0.4);
  opacity: 0;
  transition: opacity var(--kol-transition-base);
}
.kol-play-disc svg { color: inherit; }
.kol-media-tile:hover .kol-play-disc,
.kol-play-disc:focus-visible { opacity: 1; }

/* ═══════════════════════════════════════════════════════════════
 * ThemeToggle — .kol-theme-toggle
 *
 * A two-position switch, NOT a button (user ruling 2026-07-30): no interactive
 * state on ANY variant — no hover, no active, no focus wash, no aria-current
 * shift. It used to emit `.kol-btn` + `.kol-btn-nav`/`.kol-btn-primary`, which
 * dragged the whole button state machine along. Those three were the only state
 * sources; the SIZE classes are pure padding and stay.
 *
 * Same cure as IconFrame above: own your classes, inherit no states.
 * ═══════════════════════════════════════════════════════════════ */

.kol-theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--kol-radius-sm);
  white-space: nowrap;
  cursor: pointer;
}

/* fill="none" — invisible container. FULL-STRENGTH INK (user ruling): the text
 * and icon drop NO opacity. `.kol-btn-nav` rested at oq-64 only so its hover
 * could brighten to full; with no states that dim was just a faded label. */
/* geometry variants read the TONE (kol-components-molecules.css) and fall
 * back to what each painted before the tone axis (2026-09-03) */
.kol-theme-toggle-none {
  background-color: var(--kol-tone-bg, transparent);
  color: var(--kol-tone-fg, var(--kol-surface-on-primary));
  border: 1px solid var(--kol-tone-border, transparent);
}

/* fill="subtle" — the filled rung */
.kol-theme-toggle-subtle {
  background-color: var(--kol-tone-bg, var(--kol-surface-secondary));
  color: var(--kol-tone-fg, var(--kol-surface-on-primary));
  border: 1px solid var(--kol-tone-border, transparent);
}

/* flush — no pad, no radius, no fill, ever */
.kol-theme-toggle-flush {
  background-color: var(--kol-tone-bg, transparent);
  color: var(--kol-tone-fg, var(--kol-surface-on-primary));
  border: 0;
  border-radius: 0;
  padding: 0;
}


/* ── COARSE-POINTER TEXT FLOOR ──────────────────────────────────────────────
 * iOS Safari zooms the page whenever a focused text field computes under 16px,
 * and it does NOT zoom back out on blur (InputTypeScaleZoomsIOS, kol-website
 * 2026-08-31). The DS's `sm` (12px) and `md` (14px) rungs both trip it, and md
 * is the default — so every consumer with an input has it, on every iPhone.
 *
 * The cost is that it does not look like a font-size bug: the page stays zoomed
 * and every subsequent screenshot reads as a catastrophic gutter failure. A
 * mobile audit chased a non-existent overflow across a dozen routes because of
 * this one rule.
 *
 * NOT a re-litigation of MobileTouchFloor (2026-08-26, "no type floor, 24px hit
 * floor"). That ruling is about legibility — how big text is DRAWN for a human.
 * This is a hard platform behaviour keyed on the computed `font-size` of the
 * focused field, regardless of how legible it is. Pointer devices do not move.
 *
 * `maximum-scale=1` would also stop it and is the wrong fix — it kills
 * pinch-zoom for everyone and fails WCAG 1.4.4.
 *
 * The type class (`kol-mono-*`) sits on the SHELL and the field INHERITS it, so
 * an element+class rule here beats it cleanly rather than tying on specificity
 * (ARCHITECTURE §5). The height pin travels with the size: Input pins its inner
 * field to the token's line-height (h-4 / h-[18px] / h-[22px]), so a 16px face
 * in an unlifted 16px box would clip. */
/* `.kol-control--bare` is a MARKER, not chrome (OverlaySearchFieldZoomsIOS,
 * 2026-09-01): SearchInput's `bare` body plan — the overlay palette's field —
 * sits in a plain <label>, outside both shells, and was the field that put a
 * whole mobile review in a zoomed page. The marker carries no rules of its
 * own; it exists so the floor below keys on every body plan the DS ships,
 * including the chrome-less one. */
@media (pointer: coarse) {
  .kol-control input,
  .kol-control--bare input,
  .kol-expand input {
    font-size: 16px;
    line-height: 22px;
    height: 22px;
  }
  .kol-control textarea {
    font-size: 16px;
    line-height: 22px;
  }
  /* THE FADER (SliderCoarsePointerHeight, kol-mirror 2026-09-01). The jump that
   * took the input 2 → 24 on the desk takes it 24 → 44 on a phone, by its own
   * argument: the track is a 2px pseudo the UA centres in the box and the
   * thumb stays pinned to it, so the drawing does not change. The ROW lifts
   * with it — `.control-slider` is a fixed 24px and a 44px input inside it
   * would overflow; a consumer's `rowHeight` inline style still wins. */
  .control-slider,
  .slider-black {
    height: 44px;
  }
}

/* THE MOMENTARY PRESS (editor-chrome-review, kol-fxr 2026-09-03 — the user's
 * ruling on `AlignmentGrid`: two three-way strips, momentary, no selection
 * state). A STATELESS strip (`value={null}`) never lights a cell, because its
 * cells are actions and not a state the object is in — so the press itself has
 * to be the feedback, and it is `:active` rather than a new prop. `tone` was
 * the candidate and is wrong: tone is the GROUND axis and says nothing about a
 * momentary press. Excluded on an active cell — a selected cell's ink is the
 * selection, and a press must not out-shout it. */
.kol-seg-cell:active:not(.is-active) {
  background: var(--kol-oq-08);
  color: var(--kol-fg-emphasis);
}
