@font-face { font-family: "Geist Mono"; font-style: normal; font-weight: 400; font-display: swap; src: url(/fonts/geist-mono-400.woff2) format("woff2"); }
@font-face { font-family: "Geist Mono"; font-style: normal; font-weight: 500; font-display: swap; src: url(/fonts/geist-mono-500.woff2) format("woff2"); }
@font-face { font-family: "Geist Mono"; font-style: normal; font-weight: 700; font-display: swap; src: url(/fonts/geist-mono-700.woff2) format("woff2"); }
@font-face { font-family: "Ripper Docs Inter"; font-style: normal; font-weight: 100 900; font-display: optional; src: url(/fonts/inter-latin-wght-normal.woff2) format("woff2-variations"); unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: "Ripper Docs JetBrains Mono"; font-style: normal; font-weight: 100 800; font-display: swap; src: url(/fonts/jetbrains-mono-latin-wght-normal.woff2) format("woff2-variations"); unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@media (max-width: 720px) {
/*
 * Drawer — edge-anchored dialog. The panel slides in from the chosen side; the
 * slide distance + duration reference --rip-* tokens, so reduced-motion (0ms)
 * renders it instantly. All colours are tokens; theme-agnostic.
 */
.rip-drawer__backdrop {
  position: fixed;
  inset: 0;
  display: flex;
  background: color-mix(in srgb, var(--rip-bg) 70%, transparent);
  animation-name: rip-drawer-fade;
  animation-duration: var(--rip-animation-duration);
  animation-timing-function: ease-out;
  animation-fill-mode: both;
  z-index: 1000;
}

.rip-drawer__panel {
  background: var(--rip-component-card-bg);
  color: var(--rip-text);
  border: 1px solid var(--rip-component-card-border);
  padding: calc(var(--rip-spacing-unit) * 5 * var(--rip-spacing-scale));
  font-family: var(--rip-body-font);
  overflow: auto;
  animation-timing-function: ease-out;
  animation-fill-mode: both;
}

/* Right (default): full height, anchored right, slides from the right edge. */
.rip-drawer__panel[data-side="right"] {
  margin-left: auto;
  height: 100%;
  width: min(420px, 100%);
  border-radius: var(--rip-radius) 0 0 var(--rip-radius);
  animation-name: rip-drawer-slide-right;
}

.rip-drawer__panel[data-side="left"] {
  margin-right: auto;
  height: 100%;
  width: min(420px, 100%);
  border-radius: 0 var(--rip-radius) var(--rip-radius) 0;
  animation-name: rip-drawer-slide-left;
}

.rip-drawer__panel[data-side="bottom"] {
  margin-top: auto;
  width: 100%;
  max-height: 80vh;
  border-radius: var(--rip-radius) var(--rip-radius) 0 0;
  animation-name: rip-drawer-slide-bottom;
}

.rip-drawer__panel:focus-visible {
  outline: 2px solid var(--rip-accent);
  outline-offset: 2px;
}

.rip-drawer__title {
  margin: 0 0 calc(var(--rip-spacing-unit) * 3) 0;
  font-size: 16px;
  font-weight: 700;
  color: var(--rip-text);
}

.rip-drawer__body {
  font-size: 14px;
  line-height: 1.6;
}

@keyframes rip-drawer-fade {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes rip-drawer-slide-right {
  from {
    transform: translateX(100%);
  }
  to {
    transform: translateX(0);
  }
}

@keyframes rip-drawer-slide-left {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(0);
  }
}

@keyframes rip-drawer-slide-bottom {
  from {
    transform: translateY(100%);
  }
  to {
    transform: translateY(0);
  }
}

@media (forced-colors: active) {
  .rip-drawer__panel {
    border: 1px solid CanvasText;
  }
  .rip-drawer__panel:focus-visible {
    outline: 2px solid Highlight;
  }
}

}
/* The docs site — a calm reading column with the nav beside it; the runtime never renders here.
   APPEARANCE: two treatments from the design system, Terminal (the default) and Light. Every colour below is a
   token vendored from @ripper-ai/design-tokens into src/appearance-tokens.json, which records the source commit
   and the sha256 of the file the values were read from. tests/unit/appearance.test.ts holds this file to that
   record, computes AA contrast for every token in both appearances, and refuses a colour written directly. */
:root { --docs-bg: #090909; --docs-code-bg: #0f0f0f; --docs-text: #ececec; --docs-muted: #9a9a9e; --docs-border: #1b1b1d; --docs-accent: #3ffc7e; --docs-syn-string: #f59e0b; --docs-syn-key: #2dd4bf; --docs-syn-number: #c084fc; --docs-syn-keyword: #f472b6; --docs-syn-title: #3ffc7e; --docs-syn-comment: #9a9a9e; }
:root[data-appearance="light"] { --docs-bg: #ffffff; --docs-code-bg: #f4f6f8; --docs-text: #1f2933; --docs-muted: #5b6675; --docs-border: #d8dde4; --docs-accent: #15803d; --docs-syn-string: #b45309; --docs-syn-key: #0f766e; --docs-syn-number: #7e22ce; --docs-syn-keyword: #be185d; --docs-syn-title: #15803d; --docs-syn-comment: #5b6675; }
/* Prose in Inter; the system sans behind it is what a first visit shows if Inter is not ready in time (font-display:
   optional), so it is never a reflow. The faces themselves are generated into docs.css by scripts/build-site.mjs. */
html, body { margin: 0; background: var(--docs-bg); color: var(--docs-text); font-family: "Ripper Docs Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; line-height: 1.55; }
/* WRAPS. The header holds three things since the Appearance control arrived, and on a phone they do not fit one
   row. Without wrapping they overlapped instead of shrinking: at 390px "Open the console" sat on top of the Light
   radio and took its taps, so the control was visible and could not be used (caught by the e2e on chromium-mobile). */
.docs-header { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; row-gap: 8px; column-gap: 16px; padding: 12px 24px; border-bottom: 1px solid var(--docs-border); }
.docs-brand { font-weight: 700; color: var(--docs-text); text-decoration: none; }
.docs-sign-in { color: var(--docs-accent); }
/* The Appearance control, in the header so it is findable on every page. `:not([hidden])` is load-bearing: the
   HTML ships it hidden so that with scripting off there is no radio that changes nothing, and an unconditional
   display rule here would override the hidden attribute and show that dead control anyway. */
/* ONE OVERRIDE ON THE VENDORED FILE, and the reason is a paint-order ambiguity rather than a taste.
   The design system's panel is a normal-flow child of a fixed, z-indexed backdrop whose background is
   translucent. Browsers paint the child above the parent's background, but a contrast checker reading the DOM
   cannot tell that: axe reported 19 failures, computing every colour inside the panel as if the scrim were in
   front of it. Giving the panel its own stacking context says explicitly what the browser already did, and
   took the count to 10 — the remaining ten were the PAGE behind the scrim, which docs.js now marks inert while
   the drawer is open, as a modal should. */
@media (max-width: 720px) {
  .rip-drawer__panel { position: relative; z-index: 1; }
}

/* The drawer's close button. The design system's Drawer has none — its React callers close it from the
   backdrop or Escape — but at a phone width the panel IS the viewport: no backdrop to tap, and no Escape key
   on a touch device. Additive, in the drawer's own tokens, and hidden in every state but an open drawer. */
.docs-drawer__close:not([hidden]) { position: absolute; top: 12px; right: 12px; min-height: 44px; min-width: 44px; font: inherit; color: var(--rip-text); background: none; border: 1px solid var(--rip-component-card-border); border-radius: var(--rip-radius); cursor: pointer; }
.docs-drawer__close:focus-visible { outline: 3px solid var(--rip-accent); outline-offset: 2px; }

/* THE DRAWER'S THREE STATES. docs.js adds the design system's rip-drawer__* classes when it opens the drawer
   and removes them when it closes, so these :not() rules stop applying exactly when the drawer takes over —
   no second switch to keep in step. Without a script, and at any desktop width, the wrappers generate no boxes
   and the nav is what it always was. */
.docs-drawer:not(.rip-drawer__backdrop),
.docs-drawer__panel:not(.rip-drawer__panel),
.docs-drawer__body:not(.rip-drawer__body) { display: contents; }
.docs-drawer__title:not(.rip-drawer__title) { display: none; }

/* THE DRAWER'S TOKENS — the join between the design system's vocabulary and this site's.
 *
 * src/vendor/drawer.css is the design system's own file, vendored rather than installed (src/vendor/
 * provenance.json says why, with the measurement). It is written entirely in --rip-* tokens and DEFINES none
 * of them: the definitions live in @ripper-ai/design-tokens, a package this site deliberately does not take.
 * So the ten it needs are defined here, in terms of what this site already has. Every value below is an
 * existing docs value or a number already used by the cards, not a new opinion about how it should look.
 *
 * REDUCED MOTION IS CARRIED HERE and must stay: upstream, --rip-animation-duration drops to 0ms under
 * prefers-reduced-motion, in the token package. Without this block the drawer would animate for a reader who
 * asked it not to — a regression that arrives with a VALUE rather than with code, and shows in no test.
 */
:root {
  --rip-bg: var(--docs-bg);
  --rip-text: var(--docs-text);
  --rip-accent: var(--docs-accent);
  --rip-component-card-bg: var(--docs-code-bg);
  --rip-component-card-border: var(--docs-border);
  --rip-body-font: inherit;
  --rip-radius: 10px;          /* the route cards' radius */
  --rip-spacing-unit: 4px;     /* 4 x 5 x 1 = the 20px padding the route cards use */
  --rip-spacing-scale: 1;
  --rip-animation-duration: 200ms;
}
@media (prefers-reduced-motion: reduce) {
  :root { --rip-animation-duration: 0ms; }
}

/* THE MENU BUTTON, narrow widths only. Measured at 375px before it existed: the nav is 1426px tall with 28 links
   and the page's own <h1> began 1594px down, so a reader scrolled past the whole site map to reach the page they
   had opened. The button is shipped hidden and revealed by docs.js, the same contract as the Appearance control:
   with scripting off there is no dead control, and the nav stays exactly as it is today — a long list, reachable.
   44px tall because a finger, not a pointer, is the input here. */
.docs-nav-toggle { display: none; font: inherit; min-height: 44px; padding: 0 14px; color: var(--docs-text); background: var(--docs-bg); border: 1px solid var(--docs-border); border-radius: 6px; cursor: pointer; }
.docs-nav-toggle:focus-visible { outline: 3px solid var(--docs-accent); outline-offset: 2px; }
.docs-appearance:not([hidden]) { display: flex; align-items: center; gap: 10px; }
.docs-appearance { margin: 0; padding: 0; border: 0; min-width: 0; font-size: 0.85rem; }
.docs-appearance legend { float: left; padding: 0; margin-right: 2px; color: var(--docs-muted); }
.docs-appearance label { display: inline-flex; align-items: center; gap: 4px; cursor: pointer; color: var(--docs-text); }
.docs-appearance input { margin: 0; accent-color: var(--docs-accent); }
/* :focus, not :focus-visible, for the same WebKit reason as the picker below: it drops :focus-visible from a radio
   the arrow keys move focus to. The accent outline passes the 3:1 a focus indicator needs in both appearances. */
.docs-appearance label:has(input:focus) { outline: 3px solid var(--docs-accent); outline-offset: 2px; border-radius: 4px; }
.docs-shell { display: grid; grid-template-columns: 220px minmax(0, 760px); gap: 40px; max-width: 1080px; margin: 0 auto; padding: 24px; }
.docs-nav ul { list-style: none; margin: 0; padding: 0; }
/* the IA skeleton: section and group labels (text, not headings — the page's own h1 is the first heading) */
.docs-nav__section { margin: 20px 0 4px; font-size: 0.75rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--docs-muted); }
.docs-nav__section:first-child { margin-top: 0; }
.docs-nav__group { margin: 10px 0 2px; font-size: 0.9rem; font-weight: 600; color: var(--docs-text); }
/* the skip link: off-screen until it has focus, then the first thing on the page */
.docs-skip { position: absolute; left: -9999px; top: 8px; }
.docs-skip:focus { left: 8px; z-index: 10; padding: 8px 12px; background: var(--docs-bg); color: var(--docs-text); border: 2px solid var(--docs-accent); border-radius: 6px; }
.docs-main:focus { outline: none; }
/* a heading's link to itself: shown on hover or when it has focus, never removed from the tab order */
/* UNDERLINED, not merely a different colour. axe reported link-in-text-block on /js/collect: the anchor's
   muted colour against the heading text is 2.53:1 in Light and 2.37:1 in Terminal, where 3:1 is the minimum
   for telling a link apart by colour alone (WCAG 1.4.1). It fails in BOTH appearances, so the remedy has to
   work in both: raising the contrast would mean a link colour that fights the heading it sits in, and an
   underline distinguishes it by something that is not colour at all, which is what the rule asks for. */
.docs-anchor { margin-left: 6px; color: var(--docs-muted); text-decoration: underline; opacity: 0; }
.docs-main h2:hover .docs-anchor, .docs-main h3:hover .docs-anchor, .docs-anchor:focus { opacity: 1; }
/* THE MEASURE: prose is held to 55ch, and that number was measured rather than assumed. The column is 760px, which
   set continuous prose at a median of 97 characters a line. `ch` is the width of "0", which in Inter is wider than
   an average character, so 70ch still gave about 90. Laying out each page's own prose at each width: 55ch gives a
   median of about 70 characters per full line across /js/collect, /how-it-works and /js/hold. Prose only — tables,
   code blocks and the field reference keep the full column, which is what makes them readable. */
.docs-main p, .docs-main li, .docs-main blockquote { max-width: 55ch; }
/* THE START PAGE'S ROUTES. Colours are the appearance tokens, so both treatments follow automatically and a
   theme change cannot leave these behind. The first card is set apart because it is the only route that needs
   no code: a larger heading and the accent border, against three plain cards in a grid beneath it. */
.docs-routes { display: grid; gap: 16px; margin: 24px 0; }
/* The three code routes are ONE ROW or a stack, never two-plus-an-orphan — an empty third cell reads as a
   mistake rather than as a layout. auto-fit does exactly that at mid widths (measured: 2+1 at 900px), so the
   count is stated instead: three across only where the column can hold them, one per row everywhere else. */
.docs-routes--set { grid-template-columns: 1fr; }
@media (min-width: 1100px) { .docs-routes--set { grid-template-columns: repeat(3, 1fr); } }
.docs-route { padding: 20px; border: 1px solid var(--docs-border); border-radius: 10px; background: var(--docs-code-bg); }
.docs-route > :first-child { margin-top: 0; }
.docs-route > :last-child { margin-bottom: 0; }
.docs-route p { max-width: none; }
/* the link under each route reads as the way in, not as prose */
.docs-route a { display: inline-block; margin-top: 4px; font-weight: 600; }
.docs-route--primary { border-color: var(--docs-accent); }
.docs-route--primary h2 { font-size: 1.6rem; }
.docs-next { margin-top: 32px; padding-top: 16px; border-top: 1px solid var(--docs-border); }
/* Prose links: coloured AND underlined. They had no colour at all and rendered in the browser's default #0000ee,
   which passed on white and measured 2.11:1 on the Terminal ground. The underline is not decoration: the accent
   against body text is 1.15:1 in Terminal and 2.94:1 in Light, under the 3:1 needed to tell a link apart by
   colour alone. .docs-anchor is excluded because it has its own muted colour and hover behaviour. */
.docs-main a:not(.docs-anchor), .docs-next a { color: var(--docs-accent); text-decoration: underline; }
/* docs.js's Copy button, added above each code block when the script runs (the page never depends on it) */
.docs-copy { display: flex; justify-content: flex-end; margin: 0 0 4px; }
.docs-copy__button { font: inherit; font-size: 0.85rem; padding: 2px 10px; color: var(--docs-text); background: var(--docs-bg); border: 1px solid var(--docs-border); border-radius: 6px; cursor: pointer; }
.docs-copy__button:hover { border-color: var(--docs-muted); }
.docs-copy__button:focus-visible { outline: 3px solid var(--docs-accent); outline-offset: 2px; }
.docs-copy__status { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* build-time highlighting (highlight.js classes; no script). The same hue carries the same meaning in both
   appearances — teal keys, amber strings, purple numbers, pink keywords, green names — so switching appearance
   never changes what a colour means. Every token is AA on the code background; the test computes it. */
.hljs-keyword, .hljs-selector-tag, .hljs-doctag, .hljs-built_in, .hljs-type, .hljs-symbol { color: var(--docs-syn-keyword); }
.hljs-string, .hljs-regexp { color: var(--docs-syn-string); }
.hljs-title, .hljs-title.function_, .hljs-section { color: var(--docs-syn-title); }
.hljs-attr, .hljs-attribute, .hljs-property, .hljs-tag, .hljs-name { color: var(--docs-syn-key); }
.hljs-number, .hljs-literal { color: var(--docs-syn-number); }
.hljs-comment, .hljs-meta { color: var(--docs-syn-comment); font-style: italic; }
/* Variables ($VAR in bash and PHP, names in JavaScript) are identifiers, so they take the key colour. */
.hljs-variable { color: var(--docs-syn-key); }
/* Interpolation inside a string — `${id}`, f"{id}", $"{id}" — is CODE, so it resets to the text colour. Unstyled it
   inherited the string's colour and read as part of the string. */
.hljs-subst { color: var(--docs-text); }
/* PLAIN BY DESIGN, named so the choice is visible rather than an omission. These inherit the text colour:
   punctuation (braces, colons, commas), parameter lists, and the wrapper span around a function whose name and
   keyword are coloured inside it. tests/unit/appearance.test.ts lists exactly these as intentional. */
.hljs-function, .hljs-params, .hljs-punctuation { color: inherit; }
.docs-nav a { display: block; padding: 4px 0; color: var(--docs-muted); text-decoration: none; }
.docs-nav a[aria-current="page"] { color: var(--docs-text); font-weight: 600; }
.docs-main pre { background: var(--docs-code-bg); border: 1px solid var(--docs-border); border-radius: 6px; padding: 12px 16px; overflow-x: auto; }
/* The canon's order (RIPPER-DESIGN-SYSTEM.md, Font Stack): Geist Mono, then JetBrains Mono, then Fira Code, then the
   system monospace. Monospace all the way down, so a face swap never reflows a code block. */
.docs-main code { font-family: "Geist Mono", "Ripper Docs JetBrains Mono", "Fira Code", ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.92em; }
/* The scroll box around every table: the TABLE scrolls, the page does not. Measured at 320px, where the
   errors table overflowed the body by 183px. max-width keeps the box inside the column even when the
   table inside it is far wider. */
.docs-main .docs-table { overflow-x: auto; max-width: 100%; }
/* The accent, not a separate focus colour: --docs-focus was never defined, so this always fell back to a blue
   that belonged to neither appearance. The accent passes the 3:1 a focus indicator needs in both. */
.docs-main .docs-table:focus-visible { outline: 2px solid var(--docs-accent); outline-offset: 2px; }
.docs-main table { border-collapse: collapse; } .docs-main td, .docs-main th { border: 1px solid var(--docs-border); padding: 4px 8px; text-align: left; }
/* The Quickstart's platform picker: native radios in a fieldset, NO script. Only the checked option's panel shows,
   through :has(); a browser without :has() (or without CSS) shows every panel in sequence. Rules by position, 1-8:
   scripts/build-site.mjs refuses a picker with more options. The checked option is bolder and ringed, not only
   coloured, and the native radio dot stays visible. */
.docs-picker__choices { border: 0; margin: 0 0 12px; padding: 0; min-width: 0; }
.docs-picker__choices legend { padding: 0; }
.docs-picker__choices legend h2 { margin: 0 0 12px; }
.docs-picker__options { display: flex; flex-wrap: wrap; gap: 8px; }
.docs-picker__option { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; padding: 6px 12px; border: 1px solid var(--docs-border); border-radius: 6px; cursor: pointer; }
.docs-picker__option input { margin: 0; accent-color: var(--docs-accent); }
.docs-picker__option:has(input:checked) { border-color: var(--docs-accent); box-shadow: inset 0 0 0 1px var(--docs-accent); font-weight: 700; }
/* :focus, not :focus-visible: WebKit drops :focus-visible from a radio the arrow keys move focus to (measured 2026-09-16) */
.docs-picker__option:has(input:focus) { outline: 3px solid var(--docs-accent); outline-offset: 2px; }
.docs-picker__panel > h3:first-child { margin-top: 20px; }
.docs-picker:has(.docs-picker__option:nth-child(1) input:checked) .docs-picker__panel:not(:nth-child(1)) { display: none; }
.docs-picker:has(.docs-picker__option:nth-child(2) input:checked) .docs-picker__panel:not(:nth-child(2)) { display: none; }
.docs-picker:has(.docs-picker__option:nth-child(3) input:checked) .docs-picker__panel:not(:nth-child(3)) { display: none; }
.docs-picker:has(.docs-picker__option:nth-child(4) input:checked) .docs-picker__panel:not(:nth-child(4)) { display: none; }
.docs-picker:has(.docs-picker__option:nth-child(5) input:checked) .docs-picker__panel:not(:nth-child(5)) { display: none; }
.docs-picker:has(.docs-picker__option:nth-child(6) input:checked) .docs-picker__panel:not(:nth-child(6)) { display: none; }
.docs-picker:has(.docs-picker__option:nth-child(7) input:checked) .docs-picker__panel:not(:nth-child(7)) { display: none; }
.docs-picker:has(.docs-picker__option:nth-child(8) input:checked) .docs-picker__panel:not(:nth-child(8)) { display: none; }
/* NARROW: one column that can shrink below its widest child. A bare 1fr is minmax(auto, 1fr), so the longest code
   line set the column's minimum and the whole page, nav included, scrolled sideways at 320px (measured on every page,
   2026-09-16). minmax(0, 1fr) lets the column narrow; a <pre> scrolls inside itself, and long inline code and table
   cells wrap. */
@media (max-width: 720px) {
  .docs-shell { grid-template-columns: minmax(0, 1fr); }
  .docs-main { min-width: 0; }
  /* The nav collapses ONLY once docs.js has said it can be reopened. No data-nav attribute means no script, and
     then the nav renders as it always did rather than becoming unreachable behind a button that does nothing. */
  /* :not([hidden]) is load-bearing, exactly as it is for the Appearance control: a bare display rule beats the
     hidden attribute, and with scripting off the button would be visible and do nothing. Caught by the
     no-script test on all four projects, 2026-09-19. */
  .docs-nav-toggle:not([hidden]) { display: inline-flex; align-items: center; }
  :root[data-nav="closed"] .docs-nav { display: none; }
  :root[data-nav="open"] .docs-nav { display: block; margin-bottom: 24px; }
  /* ONLY when narrow: on the desktop column a table sizes its columns from min-content, and anywhere-wrapping let
     the errors table squeeze its code column until codes broke mid-word (the visual lane caught it, 2026-09-16) */
  .docs-main p code, .docs-main li code, .docs-main td code, .docs-main td { overflow-wrap: anywhere; }
  /* The generated reference headings are a METHOD and a ROUTE — "POST /v1/checkout/sessions/{session_id}/…"
     — one long token with no break opportunity after the method. Measured at 320px: an h3 reported
     scrollWidth 433 in a 272px column and the whole page panned by 137px, nav and all. Narrow-only, for the
     same reason as the rule above: on the desktop column a route should never be broken mid-path. */
  .docs-main h2, .docs-main h3, .docs-main h4 { overflow-wrap: anywhere; }
  /* 44px rows so a link is a target rather than a line of text */
  .docs-nav a { display: block; min-height: 44px; line-height: 44px; }
}
@media (forced-colors: active) { .docs-main pre, .docs-picker__option { border-color: CanvasText; } .docs-picker__option:has(input:checked) { border-width: 3px; } }
