/* Inter Tight, self-hosted. Fetched once by tools/fetch_fonts.py from Google Fonts (SIL Open Font
   License) and served from this site; a visitor's browser never calls Google. Regenerate with
   the script; do not edit by hand. */

@font-face {
  font-family: 'Inter Tight';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/assets/fonts/inter-tight-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Inter Tight';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/assets/fonts/inter-tight-latin.woff2') format('woff2');
  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;
}

/* castomapi.com — the one stylesheet. DESIGN.md explains every decision here; this file only makes them.
   Order: tokens → themes → reader preferences → reset → type → chrome → components → page sections → print. */

/* ------------------------------------------------------------------ tokens (light) */
:root {
  --paper: #F4F5F8;
  --paper-2: #FFFFFF;
  --field: #E9EBF0;
  --ink: #0B0C10;
  --ink-2: #454A5C;
  --ink-3: #5F6478;
  --rule: #D7DAE3;
  --rule-w: 1px;
  --accent: #7C4DFF;
  --accent-ink: #4C1FD1;
  --accent-soft: rgba(124, 77, 255, .12);
  --ok: #0E7C3A;
  --warn: #8A5200;
  --bad: #C2261A;
  --brand-1: #3B18C9;
  --brand-2: #7C4DFF;
  --brand-3: #B98CFF;
  --shadow: 0 1px 2px rgba(11, 12, 16, .06), 0 12px 32px -16px rgba(11, 12, 16, .25);
  --btn-bg: var(--ink);
  --btn-fg: var(--paper);
  /* A violet that carries words (the header CTA, the menu hover, the current sub menu pill, the
     footer's call to action) and the word colour on it. Light: the ramp's accent-ink stop under
     white, 8.7:1; --accent itself stays for focus and decoration. Dark: the light stop under ink,
     7.7:1. Contrast: the one ≥ 7:1 violet (DESIGN.md). */
  --fill: #4C1FD1;
  --fill-hover: #3B18C9;
  --on-fill: #FFFFFF;
  --pill-bg: color-mix(in srgb, var(--ink) 4.5%, var(--paper));
  --pill-bg-hover: color-mix(in srgb, var(--ink) 9%, var(--paper));
  --tile-bg: #FFFFFF;
  --u-thick: 2px;
  --m-get: #0A6A31;
  --m-post: #4C1FD1;
  --m-put: #0B5CAD;
  --m-patch: #7A4900;
  --m-delete: #B8231A;
  --radius: 14px;
  --radius-sm: 8px;
  --radius-pill: 999px;
  --t-1: .875rem;
  --t0: 1.0625rem;
  --t1: 1.25rem;
  --t2: 1.5625rem;
  --t3: 1.95rem;
  --t4: 2.44rem;
  --t5: 3.05rem;
  --t6: 3.8rem;
  --t7: 4.75rem;
  --wrap: 1180px;
  --bar-h: 64px;
  --ease: cubic-bezier(.2, .7, .2, 1);
  --dur: .18s;
  /* The §0.11 link line (the short length and its spring) and the floating controls' size (§0.24). */
  --u-rest: max(33%, 1.5em);
  --u-spring: cubic-bezier(.34, 1.56, .64, 1);
  --castomo-size: 56px;
  color-scheme: light;
}

/* ------------------------------------------------------------------ dark */
[data-theme="dark"] {
  --paper: #0B0C10;
  --paper-2: #14161D;
  --field: #1B1E27;
  --ink: #E8EAEE;
  --ink-2: #B4B8C6;
  --ink-3: #9296A6;
  --rule: #262A36;
  --rule-w: 1px;
  --accent: #8B5CFF;
  --accent-ink: #B98CFF;
  --accent-soft: rgba(139, 92, 255, .18);
  --ok: #5FD898;
  --warn: #FFC46B;
  --bad: #FF8D81;
  --brand-1: #3B18C9;
  --brand-2: #7C4DFF;
  --brand-3: #B98CFF;
  --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 12px 32px -16px rgba(0, 0, 0, .8);
  --btn-bg: var(--ink);
  --btn-fg: var(--paper);
  --fill: #B98CFF;
  --fill-hover: #E8EAEE;
  --on-fill: #0B0C10;
  --pill-bg: color-mix(in srgb, var(--ink) 4.5%, var(--paper));
  --pill-bg-hover: color-mix(in srgb, var(--ink) 9%, var(--paper));
  --tile-bg: #14161D;
  --u-thick: 2px;
  --m-get: #5FD898;
  --m-post: #B98CFF;
  --m-put: #7CC4FF;
  --m-patch: #FFC46B;
  --m-delete: #FF8D81;
  color-scheme: dark;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --paper: #0B0C10;
    --paper-2: #14161D;
    --field: #1B1E27;
    --ink: #E8EAEE;
    --ink-2: #B4B8C6;
    --ink-3: #9296A6;
    --rule: #262A36;
    --rule-w: 1px;
    --accent: #8B5CFF;
    --accent-ink: #B98CFF;
    --accent-soft: rgba(139, 92, 255, .18);
    --ok: #5FD898;
    --warn: #FFC46B;
    --bad: #FF8D81;
    --brand-1: #3B18C9;
    --brand-2: #7C4DFF;
    --brand-3: #B98CFF;
    --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 12px 32px -16px rgba(0, 0, 0, .8);
    --btn-bg: var(--ink);
    --btn-fg: var(--paper);
    --fill: #B98CFF;
    --fill-hover: #E8EAEE;
    --on-fill: #0B0C10;
    --pill-bg: color-mix(in srgb, var(--ink) 4.5%, var(--paper));
    --pill-bg-hover: color-mix(in srgb, var(--ink) 9%, var(--paper));
    --tile-bg: #14161D;
    --u-thick: 2px;
    --m-get: #5FD898;
    --m-post: #B98CFF;
    --m-put: #7CC4FF;
    --m-patch: #FFC46B;
    --m-delete: #FF8D81;
    color-scheme: dark;
  }
}

/* ------------------------------------------------------------------ high contrast (light) */
[data-theme="contrast"] {
  --paper: #FFFFFF;
  --paper-2: #FFFFFF;
  --field: #F0F0F0;
  --ink: #000000;
  --ink-2: #000000;
  --ink-3: #000000;
  --rule: #000000;
  --rule-w: 2px;
  --accent: #3B18C9;
  --accent-ink: #3B18C9;
  --accent-soft: rgba(0, 0, 0, 0);
  --ok: #005A24;
  --warn: #6B3F00;
  --bad: #A81208;
  --brand-1: #3B18C9;
  --brand-2: #3B18C9;
  --brand-3: #3B18C9;
  --shadow: none;
  --btn-bg: #000000;
  --btn-fg: #FFFFFF;
  --fill: #3B18C9;
  --fill-hover: #000000;
  --on-fill: #FFFFFF;
  --pill-bg: color-mix(in srgb, var(--ink) 4.5%, var(--paper));
  --pill-bg-hover: color-mix(in srgb, var(--ink) 9%, var(--paper));
  --tile-bg: #FFFFFF;
  --u-thick: 3px;
  --m-get: #005A24;
  --m-post: #3B18C9;
  --m-put: #003E8A;
  --m-patch: #6B3F00;
  --m-delete: #A81208;
  color-scheme: light;
}
@media (prefers-contrast: more) {
  :root:not([data-theme]) {
    --paper: #FFFFFF;
    --paper-2: #FFFFFF;
    --field: #F0F0F0;
    --ink: #000000;
    --ink-2: #000000;
    --ink-3: #000000;
    --rule: #000000;
    --rule-w: 2px;
    --accent: #3B18C9;
    --accent-ink: #3B18C9;
    --accent-soft: rgba(0, 0, 0, 0);
    --ok: #005A24;
    --warn: #6B3F00;
    --bad: #A81208;
    --brand-1: #3B18C9;
    --brand-2: #3B18C9;
    --brand-3: #3B18C9;
    --shadow: none;
    --btn-bg: #000000;
    --btn-fg: #FFFFFF;
    --fill: #3B18C9;
    --fill-hover: #000000;
    --on-fill: #FFFFFF;
    --pill-bg: color-mix(in srgb, var(--ink) 4.5%, var(--paper));
    --pill-bg-hover: color-mix(in srgb, var(--ink) 9%, var(--paper));
    --tile-bg: #FFFFFF;
    --u-thick: 3px;
    --m-get: #005A24;
    --m-post: #3B18C9;
    --m-put: #003E8A;
    --m-patch: #6B3F00;
    --m-delete: #A81208;
    color-scheme: light;
  }
}

/* ------------------------------------------------------------------ high contrast (dark) */
[data-theme="contrast-dark"] {
  --paper: #000000;
  --paper-2: #000000;
  --field: #111111;
  --ink: #FFFFFF;
  --ink-2: #FFFFFF;
  --ink-3: #FFFFFF;
  --rule: #FFFFFF;
  --rule-w: 2px;
  --accent: #B98CFF;
  --accent-ink: #B98CFF;
  --accent-soft: rgba(0, 0, 0, 0);
  --ok: #7FFF9F;
  --warn: #FFE066;
  --bad: #FF8D81;
  --brand-1: #B98CFF;
  --brand-2: #B98CFF;
  --brand-3: #B98CFF;
  --shadow: none;
  --btn-bg: #FFFFFF;
  --btn-fg: #000000;
  --fill: #B98CFF;
  --fill-hover: #FFFFFF;
  --on-fill: #000000;
  --pill-bg: color-mix(in srgb, var(--ink) 4.5%, var(--paper));
  --pill-bg-hover: color-mix(in srgb, var(--ink) 9%, var(--paper));
  --tile-bg: #000000;
  --u-thick: 3px;
  --m-get: #7FFF9F;
  --m-post: #B98CFF;
  --m-put: #7CC4FF;
  --m-patch: #FFE066;
  --m-delete: #FF8D81;
  color-scheme: dark;
}

/* ------------------------------------------------------------------ reader preferences */
[data-size="large"] { font-size: 112.5%; }
[data-size="xlarge"] { font-size: 125%; }
[data-spacing="wide"] body { letter-spacing: .12em; word-spacing: .16em; line-height: 1.8; }
[data-spacing="wide"] p, [data-spacing="wide"] li { margin-block-end: 2em; }
[data-spacing="wide"] h1, [data-spacing="wide"] h2, [data-spacing="wide"] h3 { letter-spacing: .04em; }
[data-motion="reduce"] *, [data-motion="reduce"] *::before, [data-motion="reduce"] *::after {
  transition: none !important; animation: none !important; scroll-behavior: auto !important;
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; scroll-behavior: auto !important; }
}

/* ------------------------------------------------------------------ hero request stream
   Real operations from the catalogue, sliding past: the platform doing its one job. It is the
   hero's only object — beside the text on wide screens, under it once the grid collapses.
   Decoration only (aria-hidden in the markup) — the hero facts state the same numbers in text.
   The track holds the list twice and travels exactly half its height, so the loop has no seam.
   It pauses on hover and on keyboard focus anywhere in the hero, and under either reduced-motion
   hook above the animation stops with the first frame in place, which is a readable still list. */
.req-stream {
  position: relative; margin-top: 20px; height: 168px; overflow: hidden;
  -webkit-mask-image: linear-gradient(180deg, transparent, #000 18%, #000 82%, transparent);
  mask-image: linear-gradient(180deg, transparent, #000 18%, #000 82%, transparent);
}
/* minmax(0,1fr) + the min-width:0 on the tile and its path: without them a tile cannot shrink
   below its content's min-content width, so on a narrow column it overruns the stream and the
   mask silently crops the cost off the right edge instead of the path ellipsising. */
.req-track { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 8px; animation: req-slide 26s linear infinite; }
.hero:hover .req-track, .hero:focus-within .req-track { animation-play-state: paused; }
.req {
  display: flex; align-items: center; gap: 10px; padding: 10px 14px; min-width: 0;
  border: var(--rule-w) solid var(--rule); border-radius: var(--radius-sm);
  background: var(--paper-2); box-shadow: var(--shadow); font-size: var(--t-1);
}
.req code { font-family: ui-monospace, Menlo, Consolas, monospace; color: var(--ink-2); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.req-ok { margin-inline-start: auto; color: var(--ok); font-weight: 600; }
.req-cost { color: var(--ink-3); white-space: nowrap; }
@keyframes req-slide { from { transform: translateY(0); } to { transform: translateY(-50%); } }
/* Below 960px the hero grid is one column, so the stream sits under the facts as a full-width
   band. It shows two tiles instead of three and sheds the tile padding: the mobile hero is
   already long, and this is the only object in it. */
@media (max-width: 960px) {
  .req-stream { height: 108px; margin-top: 24px; }
  .req { padding: 8px 12px; gap: 8px; }
}

/* ------------------------------------------------------------------ the home page's explorers
   Two readers of the same baked catalogue: "what is there for X" and "what would a month cost".
   Both answer from #page-data, so neither can disagree with the endpoint pages or with pricing. */
.cov-label, .calc-label { display: block; font-size: var(--t-1); color: var(--ink-3); margin-bottom: 6px; }
.cov-input, .calc-pick {
  width: 100%; max-width: 420px; padding: 10px 14px; font: inherit; color: var(--ink);
  background: var(--paper-2); border: var(--rule-w) solid var(--rule); border-radius: var(--radius-sm);
}
.cov-summary { font-size: var(--t-1); color: var(--ink-3); margin: 12px 0 16px; }
.cov-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px;
            grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); }
.cov {
  min-width: 0; padding: 12px 14px; background: var(--paper-2);
  border: var(--rule-w) solid var(--rule); border-radius: var(--radius-sm);
}
.cov-head { display: flex; align-items: baseline; gap: 8px; text-decoration: none; color: inherit; min-width: 0; }
.cov-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cov-kind { font-size: var(--t-1); color: var(--ink-3); }
.cov-kind[data-kind="integration"] { color: var(--accent-ink); }
.cov-meta { display: flex; align-items: baseline; gap: 8px; margin: 6px 0 0; font-size: var(--t-1); color: var(--ink-3); }
.cov-cost { margin-inline-start: auto; white-space: nowrap; }

.calc-rows { list-style: none; margin: 16px 0 0; padding: 0; display: grid; gap: 8px; }
.calc-row {
  display: flex; align-items: center; gap: 10px; min-width: 0; padding: 10px 14px;
  background: var(--paper-2); border: var(--rule-w) solid var(--rule); border-radius: var(--radius-sm);
}
.calc-row > b { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.calc-rate { font-size: var(--t-1); color: var(--ink-3); white-space: nowrap; }
.calc-calls { margin-inline-start: auto; width: 7.5em; padding: 6px 8px; font: inherit; color: var(--ink);
              background: transparent; border: var(--rule-w) solid var(--rule); border-radius: var(--radius-sm); }
.calc-drop { padding: 4px 10px; font: inherit; line-height: 1; color: var(--ink-3); cursor: pointer;
             background: transparent; border: var(--rule-w) solid var(--rule); border-radius: var(--radius-sm); }
.calc-total { margin: 16px 0 4px; font-size: var(--t2); }
.calc-total b { font-size: 1.6em; }
.calc-note { font-size: var(--t-1); color: var(--ink-3); margin: 0; }
.calc-note[data-tone="ok"] { color: var(--ok); }

/* ------------------------------------------------------------------ reset */
*, *::before, *::after { box-sizing: border-box; }
html { font-size: 106.25%; scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
body {
  margin: 0; background: var(--paper); color: var(--ink);
  font-family: 'Inter Tight', system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: var(--t0); line-height: 1.6; font-weight: 400;
  -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
}
img, svg { max-width: 100%; display: block; }
svg { fill: none; }
button, input, textarea, select { font: inherit; color: inherit; }
button { cursor: pointer; }
/* The bar is sticky, so an anchor jump would otherwise land underneath it (WCAG 2.4.11). The gutter
   is reserved for good: the phone popover's scroll lock (html.pop-lock) would otherwise shift the page. */
html { scroll-padding-block-start: calc(var(--bar-h) + 8px); scrollbar-gutter: stable; }
/* Text links (ECOSYSTEM-GUIDELINES §0.11, as castomui.com): no full underline at rest, only a short
   line under the first third, which stretches to full width with a spring on hover and focus. The
   line is a background, not text-decoration, so it follows a link that wraps onto two lines. Only
   class-less links get it (and `.more`, the arrow links that close a reason): a link with a class is
   a component (a card, a chip, a tile, a menu row, a button) that draws its own affordance, and the
   list below switches it off for the class-less links inside those. */
@supports (transition-timing-function: linear(0, 1)) {
  :root { --u-spring: linear(0, .21 7%, .6 15%, .92 23%, 1.08 30%, 1.13 35%, 1.1 41%, 1.02 50%, .98 57%, .985 66%, 1.003 80%, 1); }
}
a { color: var(--accent-ink); text-decoration: none; }
/* A bare URL as link text has no space to break at; on a phone it is the one thing that can widen the page. */
p a, li a, td a { overflow-wrap: anywhere; }
:is(a:not([class]), a.more) {
  background-image: linear-gradient(currentColor, currentColor);
  background-position: 0 100%;
  background-repeat: no-repeat;
  background-size: var(--u-rest) var(--u-thick);
  padding-block-end: 2px;
  transition: background-size .6s var(--u-spring), color var(--dur, .2s);
}
:is(a:not([class]), a.more):is(:hover, :focus-visible, :active) { background-size: 100% var(--u-thick); }
:is(.nav-menu, .lang-menu, .subnav, .tile-list, .toc, .logo-row) a:not([class]) { background-image: none; padding-block-end: 0; }
/* The line is for links in the content only. Header and footer links are navigation, marked by their
   place and their colour change, so they never get it. */
:is(body > header, body > footer, .bar, .foot) a:not([class]) { background-image: none; padding-block-end: 0; }
.foot-family a:not([class]) { background-image: none; }
@media (prefers-reduced-motion: reduce) { :is(a:not([class]), a.more) { transition-duration: .01s; } }
[data-motion="reduce"] :is(a:not([class]), a.more) { transition-duration: .01s; }
@media (forced-colors: active) { :is(a:not([class]), a.more) { background-image: none; text-decoration: underline; } }
:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; border-radius: 4px; }
:focus:not(:focus-visible) { outline: none; }
::selection { background: var(--accent); color: #fff; }
hr { border: 0; border-top: var(--rule-w) solid var(--rule); margin: 32px 0; }
table { border-collapse: collapse; width: 100%; }
code, pre, kbd, samp { font-family: ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; font-size: .92em; }
[hidden] { display: none !important; }
.sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* ------------------------------------------------------------------ type */
h1, h2, h3, h4 { font-weight: 600; letter-spacing: -.025em; line-height: 1.12; margin: 0 0 .5em; text-wrap: balance; color: var(--ink); }
h1 { font-size: clamp(var(--t4), 5.5vw, var(--t6)); }
h2 { font-size: clamp(var(--t2), 3.4vw, var(--t4)); }
h3 { font-size: var(--t2); }
h4 { font-size: var(--t1); }
p { margin: 0 0 1.1em; }
.lead { font-size: var(--t1); color: var(--ink-2); line-height: 1.5; max-width: 60ch; text-wrap: pretty; }
.eyebrow { font-size: var(--t-1); font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--accent-ink); margin: 0 0 12px; }
/* On a phone the hero has to get to the headline fast, and this line is scene-setting the rest
   of the page repeats. Only the hero's own eyebrow goes: the ones that label sections further
   down are headings people navigate by, and they stay. */
@media (max-width: 960px) { .hero .eyebrow { display: none; } }
.num { font-variant-numeric: tabular-nums; }
.muted { color: var(--ink-3); }
.prose { max-width: 68ch; }
.prose > * + h2 { margin-top: 2em; }
.prose > * + h3 { margin-top: 1.6em; }
/* Those two only fire where a heading is a direct child of .prose. On a page built from <section>s
   — every endpoint page — the heading is the section's first child instead, so the space before it
   came from whatever the previous section happened to END with: 20px after a paragraph, nothing at
   all after a code block, a table or a bare div. Spacing a section by its last child's type is the
   bug; the section itself carries it now. Adjacent margins collapse, so a section already followed
   by a paragraph's 20px lands on the same gap rather than adding to it. */
.prose > * + section { margin-top: 2em; }
.prose ul, .prose ol { padding-inline-start: 1.3em; margin: 0 0 1.2em; }
.prose li + li { margin-top: .35em; }
.prose blockquote { margin: 1.5em 0; padding: 4px 0 4px 20px; border-left: 3px solid var(--accent); color: var(--ink-2); }
.prose figure { margin: 1.5em 0; }
.prose table { font-size: var(--t-1); margin: 1.2em 0; }
.prose th, .prose td { text-align: left; padding: 10px 12px; border-bottom: var(--rule-w) solid var(--rule); vertical-align: top; }
.prose th { font-weight: 600; color: var(--ink-2); }
.prose dl { margin: 0 0 1.2em; }
.prose dt { font-weight: 600; }
.prose dd { margin: 0 0 .8em; }

/* ------------------------------------------------------------------ layout */
.wrap { width: min(var(--wrap), 100% - clamp(32px, 8vw, 64px)); margin-inline: auto; }
.section { padding-block: clamp(56px, 8vw, 96px); }
.section + .section { padding-top: 0; }
/* A background change (band / band-ink) needs its own top padding even against another .section —
   the zero-gap rule above assumes a seamless visual continuation, which is wrong the moment the
   ground colour changes on either side of the seam. */
.section.band + .section, .section.band-ink + .section,
.section + .section.band, .section + .section.band-ink { padding-top: clamp(56px, 8vw, 96px); }
.section-head { margin-bottom: clamp(24px, 4vw, 40px); max-width: 68ch; }
.band { background: var(--paper-2); border-block: var(--rule-w) solid var(--rule); }
/* The inverted band: ink ground, paper text — so in the dark theme it is the light band. Its links
   and buttons are written in the same two tokens, never in a literal, or one theme fails contrast. */
.band-ink { background: var(--ink); color: var(--paper); }
.band-ink h2, .band-ink h3 { color: var(--paper); }
.band-ink a:not(.btn) { color: var(--paper); }
.band-ink .lead, .band-ink .muted { color: var(--paper); opacity: .85; }
.band-ink .btn-primary { background: var(--paper); color: var(--ink); }
.band-ink .btn-ghost { color: var(--paper); border-color: var(--paper); }
.band-ink .btn-ghost:hover { background: var(--paper); color: var(--ink); }
.grid { display: grid; gap: 20px; }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 960px) { .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 600px) { .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } }
.split { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(24px, 5vw, 64px); align-items: center; }
@media (max-width: 860px) { .split { grid-template-columns: 1fr; } }
.stack > * + * { margin-top: 12px; }
/* A grid or flex item defaults to min-width:auto, which is its content's min-content — and a code
   block's min-content is its longest line. That is how a 390px phone got a 788px prose column.
   Every layout child here may shrink below its content; the scroll containers inside then scroll. */
.grid > *, .split > *, .with-toc > *, .hero-grid > *, .bento > *, .reason-body > *, .plans > * { min-width: 0; }

/* ------------------------------------------------------------------ skip + bar */
.skip { display: inline-flex; align-items: center; gap: 8px; position: absolute; inset-inline-start: 16px; inset-block-start: 8px; transform: translateY(-160%); background: var(--ink); color: var(--paper); padding: 10px 16px; border-radius: var(--radius-pill); z-index: 100; text-decoration: none; font-weight: 600; }
.skip:focus { transform: none; outline-color: var(--accent); }

.bar { position: sticky; top: 0; z-index: 50; background-color: color-mix(in srgb, var(--paper-2) 0%, transparent); border-bottom: var(--rule-w) solid transparent; }
/* The header rests see-through, with no rule under it, and turns 99% --paper-2 once the page scrolls
   (guidelines §0.26). Phones: .is-scrolled from header.js; desktop: the pill layer does it with --p.
   The border stays, transparent, so the bar keeps its height. */
@media (max-width: 1200px) { .bar.is-scrolled { background-color: color-mix(in srgb, var(--paper-2) 99%, transparent); } }
.bar-in { display: flex; align-items: center; gap: 10px; min-height: var(--bar-h); }
.brand { display: inline-flex; align-items: center; gap: 10px; color: var(--ink); text-decoration: none; font-weight: 700; font-size: var(--t1); letter-spacing: -.02em; flex: none; }
.brand svg { width: 32px; height: 32px; }
.brand b { color: var(--accent-ink); font-weight: 700; }
/* The product marks play the brand kit's "Draw — Pop" cut on hover (kupisiewicz/brand-kit,
   tools/build_animations.py draw_pop): each tile of the mark pops in from 45% with a small
   overshoot, one after another, and settles. These are the kit's own keyframes and easing, cut down
   to the draw-in so they play once per hover instead of looping — the kit's 3.2 s loop spends its
   first 0.64 s drawing in (0-14% to 108%, 14-20% settling), then holds and fades out; here that
   0.64 s is the whole animation, the 108% peak landing at 70%. At rest nothing animates and the mark
   is whole. The animation exists only while the link is hovered or focused, so every new hover
   replays it from the start, and `both` holds each tile at its first frame until its stagger has
   passed. The hover is keyed on the LINK holding the mark, so the whole logo or the whole family
   tile is the target. The tiles are the mark's own shapes, the direct children of its <svg>, so the
   markup carries nothing for the animation at all. Under reduced motion the global rule removes the
   animation and the mark stays whole.

   The hover rule sets the animation longhands, not the `animation` shorthand, because the shorthand
   would reset the per-tile delays below back to zero. */
/* `:not(.shelf-bg)`: the faint background copy of a family mark (site.js) stays out of the pop.
   Only marks inside a tile LINK: the shelf's heading icon (.foot-fam-ico) has an ellipse with a
   `rotate(-20 12 12)` attribute, and a CSS transform-origin moves the pivot of that attribute too,
   which knocked the planet's ring off its sphere. */
:is(.brand svg, .foot-family a > svg:not(.shelf-bg)) > * { transform-box: fill-box; transform-origin: center; }
.brand:hover svg > *, .brand:focus-visible svg > *, .foot-family a:hover svg:not(.shelf-bg) > *, .foot-family a:focus-visible svg:not(.shelf-bg) > * {
  animation-name: mk-pop; animation-duration: .64s; animation-timing-function: cubic-bezier(.2, .7, .3, 1); animation-fill-mode: both;
}
/* The kit's stagger, by position: tile k of n starts (k-1) × 0.42 s / (n-1) after the hover begins
   (brand-kit draw_pop). `:nth-child(k):nth-last-child(n-k+1)` picks tile k of a mark with exactly n
   tiles, so no delay has to be written into the markup — fourteen marks sit on every page, and
   inline delays cost ~1.7 KB a page, enough to push the changelog pages over check_html's budget.
   tools/sync_marks.py refuses a mark with more tiles than these rules cover, or whose kit delays no
   longer follow this rule. The first tile of every mark starts at once. */
:is(.brand, .foot-family) svg > :nth-child(2):nth-last-child(1) { animation-delay: .42s; }
:is(.brand, .foot-family) svg > :nth-child(2):nth-last-child(2) { animation-delay: .21s; }
:is(.brand, .foot-family) svg > :nth-child(3):nth-last-child(1) { animation-delay: .42s; }
:is(.brand, .foot-family) svg > :nth-child(2):nth-last-child(3) { animation-delay: .14s; }
:is(.brand, .foot-family) svg > :nth-child(3):nth-last-child(2) { animation-delay: .28s; }
:is(.brand, .foot-family) svg > :nth-child(4):nth-last-child(1) { animation-delay: .42s; }
:is(.brand, .foot-family) svg > :nth-child(2):nth-last-child(4) { animation-delay: .105s; }
:is(.brand, .foot-family) svg > :nth-child(3):nth-last-child(3) { animation-delay: .21s; }
:is(.brand, .foot-family) svg > :nth-child(4):nth-last-child(2) { animation-delay: .315s; }
:is(.brand, .foot-family) svg > :nth-child(5):nth-last-child(1) { animation-delay: .42s; }
@keyframes mk-pop {
  0% { opacity: 0; transform: scale(.45); }
  70% { opacity: 1; transform: scale(1.08); }
  100% { opacity: 1; transform: scale(1); }
}
.nav-links { display: flex; align-items: center; gap: 2px; margin-inline-start: 8px; margin-inline-end: auto; }
.nav-top, .nav-group > summary { font-size: var(--t-1); font-weight: 500; color: var(--ink-2); padding: 8px 12px; border-radius: var(--radius-sm); text-decoration: none; white-space: nowrap; line-height: 1.2; }
.nav-top:hover, .nav-group > summary:hover, .nav-group[open] > summary { color: var(--ink); background: var(--field); }
.nav-top[aria-current="page"], .nav-group[data-current] > summary { color: var(--ink); box-shadow: inset 0 -2px 0 var(--accent); border-radius: var(--radius-sm) var(--radius-sm) 0 0; }
.nav-group { position: relative; }
.nav-group > summary { list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; }
.nav-group > summary::-webkit-details-marker { display: none; }
.nav-group > summary::after { content: ""; width: 7px; height: 7px; border-right: 1.8px solid currentColor; border-bottom: 1.8px solid currentColor; transform: translateY(-2px) rotate(45deg); transition: transform var(--dur) var(--ease); }
.nav-group[open] > summary::after { transform: translateY(1px) rotate(-135deg); }
.nav-menu { position: absolute; top: calc(100% + 10px); inset-inline-start: 0; background: var(--paper-2); border: var(--rule-w) solid var(--rule); border-radius: var(--radius); box-shadow: var(--shadow); padding: 12px; display: grid; gap: 4px; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); width: min(var(--menu-w, 40rem), calc(100vw - 2.5rem)); z-index: 60; animation: menu-in var(--dur) var(--ease); }
.nav-menu::before { content: ""; position: absolute; inset-inline: 0; top: -12px; height: 12px; }
.nav-menu[data-cols="3"] { --menu-w: 56rem; }
.nav-menu[data-cols="4"] { --menu-w: 68rem; }
.nav-menu[data-cols="1"] { --menu-w: 22rem; }
@keyframes menu-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.nav-menu a { display: grid; grid-template-columns: 2.4rem 1fr; grid-template-rows: auto auto; column-gap: 12px; align-items: center; padding: 8px 10px; border-radius: var(--radius-sm); text-decoration: none; color: var(--ink); }
.nav-menu a:hover, .nav-menu a:focus-visible { background: var(--field); }
.nav-menu a b { font-weight: 600; font-size: var(--t-1); line-height: 1.3; }
.nav-menu a span:not(.nav-ico) { grid-column: 2; font-size: var(--t-1); color: var(--ink-3); line-height: 1.35; }
.nav-ico { grid-row: 1 / span 2; width: 2.4rem; height: 2.4rem; border-radius: 10px; background: var(--accent-soft); color: var(--accent-ink); display: grid; place-items: center; border: var(--rule-w) solid transparent; }
[data-theme="contrast"] .nav-ico, [data-theme="contrast-dark"] .nav-ico { border-color: var(--rule); }
.nav-ico svg { width: 1.3rem; height: 1.3rem; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.nav-menu a:hover .nav-ico, .nav-menu a[aria-current="page"] .nav-ico { background: var(--fill); color: var(--on-fill); }
.nav-menu a.compact { grid-template-rows: auto; padding: 5px 8px; }
.nav-menu a.compact .nav-ico { grid-row: 1; width: 1.9rem; height: 1.9rem; border-radius: 8px; }
.nav-menu a.compact .nav-ico svg { width: 1.05rem; height: 1.05rem; }
.nav-col { display: grid; gap: 2px; align-content: start; }
.nav-col h3 { font-size: var(--t-1); font-weight: 600; color: var(--ink-3); letter-spacing: .06em; text-transform: uppercase; margin: 8px 10px 6px; }
.nav-col + .nav-col { border-inline-start: var(--rule-w) solid var(--rule); padding-inline-start: 12px; }
/* "Browse by area" in the Endpoints menu: one heading over two columns of links. The column takes two
   of the menu's three tracks; its links fill them row by row. */
.nav-col-split { grid-column: span 2; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 12px; }
.nav-col-split h3 { grid-column: 1 / -1; }
.nav-menu .nav-more { grid-column: 1 / -1; margin-top: 6px; padding: 10px; border-top: var(--rule-w) solid var(--rule); font-size: var(--t-1); font-weight: 600; text-align: center; display: block; }

.bar-end { display: flex; align-items: center; gap: 6px; flex: none; }
.icon-btn { display: inline-grid; place-items: center; width: 40px; height: 40px; border-radius: var(--radius-pill); border: var(--rule-w) solid transparent; background: transparent; color: var(--ink-2); }
.icon-btn:hover, .icon-btn[aria-expanded="true"], .icon-btn[aria-pressed="true"] { background: var(--field); color: var(--ink); }
.icon-btn svg { width: 20px; height: 20px; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; padding: 11px 20px; border-radius: var(--radius-pill); font-weight: 600; font-size: var(--t-1); text-decoration: none; border: var(--rule-w) solid transparent; line-height: 1.2; min-height: 44px; transition: transform var(--dur) var(--ease), background var(--dur), color var(--dur); }
.btn:hover { transform: translateY(-1px); }
.btn-primary { background: var(--btn-bg); color: var(--btn-fg); }
/* The hover ground is the ramp's dark stop, literal: white on it measures 9.5:1 in every theme,
   where white on --accent would be 4.0:1 in the light one. */
.btn-primary:hover { background: #3B18C9; color: #fff; }
[data-theme="contrast"] .btn-primary:hover, [data-theme="contrast-dark"] .btn-primary:hover { background: var(--btn-bg); color: var(--btn-fg); outline: 3px solid var(--accent); outline-offset: 2px; }
.btn-ghost { background: transparent; color: var(--ink); border-color: var(--rule); }
.btn-ghost:hover { background: var(--field); }
.btn-lg { font-size: var(--t0); padding: 14px 26px; }
.btn svg { width: 18px; height: 18px; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* The one icon every button carries (§0.2), after its words; `overflow: visible` lets a motion leave
   the icon's own box. */
.btn .btn-ico { overflow: visible; margin-inline-end: 1px; }

/* the language picker and the accessibility panel */
.lang, .a11y { position: relative; }
.lang-btn { display: inline-flex; align-items: center; gap: 4px; height: 40px; padding: 0 10px; border-radius: var(--radius-pill); border: var(--rule-w) solid transparent; background: transparent; color: var(--ink-2); font-weight: 600; font-size: var(--t-1); text-transform: uppercase; letter-spacing: .04em; }
.lang-btn:hover, .lang-btn[aria-expanded="true"] { background: var(--field); color: var(--ink); }
.lang-btn svg { width: 10px; height: 6px; stroke: currentColor; }
.pop { position: absolute; inset-inline-end: 0; top: calc(100% + 8px); background: var(--paper-2); border: var(--rule-w) solid var(--rule); border-radius: var(--radius); box-shadow: var(--shadow); padding: 8px; min-width: 12rem; z-index: 70; animation: menu-in var(--dur) var(--ease); }
/* Each popover names itself and carries a close button (guidelines §0.25): outside-click is not a
   reliable gesture once the phone sheet below covers everything. */
.pop-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 0 12px; }
.pop-close { width: 32px; height: 32px; flex: none; }
.pop-close svg { width: 16px; height: 16px; }
.lang-pop { min-width: 15rem; padding: 12px; }
.lang-menu { list-style: none; margin: 0; padding: 0; }
.lang-menu a { display: flex; align-items: center; gap: 10px; padding: 8px 12px; border-radius: var(--radius-sm); text-decoration: none; color: var(--ink); font-size: var(--t-1); }
.lang-menu a:hover { background: var(--field); }
.lang-menu a[aria-current="true"] { font-weight: 600; box-shadow: inset 3px 0 0 var(--accent); }
.a11y-panel { width: min(22rem, calc(100vw - 2rem)); padding: 16px; }
.a11y-h { font-size: var(--t0); margin: 0; }
.a11y-set { border: 0; padding: 0; margin: 0 0 14px; }
.a11y-set legend, .a11y-set > .a11y-lbl { font-size: var(--t-1); font-weight: 600; color: var(--ink-2); padding: 0; margin-bottom: 6px; display: block; }
.a11y-opts { display: flex; flex-wrap: wrap; gap: 6px; }
.a11y-opts button { padding: 7px 12px; border-radius: var(--radius-pill); border: var(--rule-w) solid var(--rule); background: transparent; font-size: var(--t-1); font-weight: 500; min-height: 36px; }
.a11y-opts button[aria-checked="true"] { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.a11y-switch { display: flex; align-items: center; justify-content: space-between; width: 100%; padding: 8px 0; background: none; border: 0; font-weight: 600; font-size: var(--t-1); text-align: left; min-height: 40px; }
.a11y-dot { width: 40px; height: 22px; border-radius: 11px; background: var(--field); border: var(--rule-w) solid var(--rule); position: relative; flex: none; }
.a11y-dot::after { content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: var(--ink-3); transition: transform var(--dur) var(--ease), background var(--dur); }
.a11y-switch[aria-checked="true"] .a11y-dot { background: var(--accent); border-color: var(--accent); }
.a11y-switch[aria-checked="true"] .a11y-dot::after { transform: translateX(18px); background: #fff; }
.a11y-hint { font-size: var(--t-1); color: var(--ink-3); margin: -2px 0 6px; }
.a11y-foot { margin: 8px 0 0; font-size: var(--t-1); }
/* Below 640px a popover anchored to the bar's edge would run off the viewport, so both become a
   full-screen sheet: no anchor to overflow, a close button, the page locked under it (site.js adds
   html.pop-lock only while one is open, and only this width locks, guidelines §0.25). */
.pop-scrim { display: none; }
@media (max-width: 640px) {
  .pop { position: fixed; inset: 0; width: auto; max-width: none; min-width: 0; max-height: none; height: 100dvh; border-radius: 0; border: 0; box-shadow: none; padding: max(16px, env(safe-area-inset-top)) 16px max(16px, env(safe-area-inset-bottom)); z-index: 80; animation: pop-sheet-in var(--dur) var(--ease); }
  .a11y-panel, .lang-pop { width: auto; max-height: none; }
  /* A panel taller than a short phone (or grown by its own "Larger" setting) scrolls itself. */
  .a11y-panel { overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; padding-inline: max(16px, env(safe-area-inset-left)) max(16px, env(safe-area-inset-right)); }
  /* Below .bar's own z-index 50: the popover is a descendant of .bar and cannot paint above a sibling
     stacking context that sits higher than .bar. */
  .pop-scrim:not([hidden]) { display: block; position: fixed; inset: 0; background: rgba(10, 12, 16, .45); z-index: 45; }
  html.pop-lock, html.pop-lock body { overflow: hidden; }
}
@keyframes pop-sheet-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }

/* read-aloud: one button on the bar, a stop button beside it while reading */
.player { display: inline-flex; align-items: center; gap: 4px; }
.player[data-state="unsupported"] { display: none; }
.player-btn { display: inline-flex; align-items: center; gap: 8px; height: 40px; padding: 0 14px 0 10px; border-radius: var(--radius-pill); border: var(--rule-w) solid var(--rule); background: transparent; color: var(--ink-2); font-weight: 600; font-size: var(--t-1); }
.player-btn:hover { background: var(--field); color: var(--ink); }
.player-btn svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.player[data-state="idle"] .i-pause, .player[data-state="playing"] .i-play, .player[data-state="paused"] .i-pause { display: none; }
.player.is-active .player-btn { background: var(--fill); color: var(--on-fill); border-color: var(--fill); }
.is-reading { outline: 3px solid var(--accent); outline-offset: 6px; border-radius: 4px; }
@media (max-width: 720px) { .player-label { display: none; } .player-btn { width: 40px; padding: 0; justify-content: center; } }

/* the drawer — it takes over at 1200, the shared header's breakpoint (guidelines §0.26): the pill
   and the bubble need the centred bar, which four menus, Pricing and the tools fill at 1201. */
.menu-toggle { position: absolute; opacity: 0; width: 0; height: 0; margin: 0; pointer-events: none; }
.burger { display: none; }
.menu-body { display: contents; }
@media (max-width: 1200px) {
  .bar-in { gap: 6px; }
  .burger { display: grid; place-items: center; width: 40px; height: 40px; border-radius: var(--radius-pill); color: var(--ink-2); order: 10; cursor: pointer; }
  .burger:hover { background: var(--field); color: var(--ink); }
  .burger svg { width: 22px; height: 22px; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
  .burger .burger-x { display: none; }
  .menu-toggle:checked ~ .burger .burger-x { display: block; }
  .menu-toggle:checked ~ .burger svg:not(.burger-x) { display: none; }
  .menu-toggle:focus-visible ~ .burger { outline: 3px solid var(--accent); outline-offset: 2px; }
  .bar-end { margin-inline-start: auto; }
  .bar-end .bar-cta { display: none; }
  .menu-body { display: none; position: absolute; inset-inline: 0; top: 100%; background: var(--paper-2); border-bottom: var(--rule-w) solid var(--rule); box-shadow: var(--shadow); padding: 12px clamp(16px, 4vw, 32px) 20px; max-height: calc(100vh - var(--bar-h)); overflow: auto; }
  .menu-toggle:checked ~ .menu-body { display: block; }
  .nav-links { display: grid; gap: 4px; margin: 0; }
  .nav-top, .nav-group > summary { display: flex; align-items: center; justify-content: space-between; font-size: var(--t0); padding: 12px 10px; }
  .nav-group[data-current] > summary, .nav-top[aria-current="page"] { box-shadow: inset 3px 0 0 var(--accent); border-radius: var(--radius-sm); }
  .nav-group { position: static; }
  .nav-menu, .nav-menu[data-cols="3"], .nav-menu[data-cols="4"] { position: static; width: auto; box-shadow: none; border: 0; padding: 4px 0 12px 8px; grid-template-columns: 1fr; animation: none; }
  .nav-menu::before { display: none; }
  .nav-col + .nav-col { border: 0; padding: 0; }
  .nav-col-split { grid-column: auto; grid-template-columns: 1fr; }
  .nav-menu a b { font-size: var(--t0); }
  .drawer-cta { display: flex; margin-top: 12px; }
}
/* On a wide screen the drawer checkbox has no visible label (the burger is hidden), so it must not
   exist as a form control either — an off-screen checkbox with a hidden label is an unlabelled field
   to an accessibility checker, and it has nothing to do at that width. */
@media (min-width: 1201px) { .drawer-cta { display: none; } .menu-toggle { display: none; } }

/* ------------------------------------------------------------------ footer + back to top */
/* `overflow-x: clip` because the family shelf below runs out to the edges of the window, and 100vw
   counts the scrollbar — without it the page would gain a few pixels of sideways scroll. */
.foot { margin-top: clamp(64px, 10vw, 120px); border-top: var(--rule-w) solid var(--rule); background: var(--paper-2); padding: clamp(40px, 6vw, 64px) 0 32px; font-size: var(--t-1); color: var(--ink-2); overflow-x: clip; }
.foot-grid { display: grid; grid-template-columns: 1.4fr repeat(5, 1fr); gap: 24px; }
@media (max-width: 1080px) { .foot-grid { grid-template-columns: repeat(3, 1fr); } .foot-brand { grid-column: 1 / -1; } }
@media (max-width: 600px) { .foot-grid { grid-template-columns: repeat(2, 1fr); } }
.foot h2 { font-size: var(--t-1); font-weight: 600; color: var(--ink); letter-spacing: .06em; text-transform: uppercase; margin: 0 0 12px; }
.foot ul { list-style: none; margin: 0; padding: 0; }
.foot li + li { margin-top: 8px; }
.foot a { color: var(--ink-2); text-decoration: none; }
.foot a:hover { color: var(--ink); }
.foot-brand .brand { margin-bottom: 12px; }
.foot-brand p { max-width: 30ch; }
/* The section tab row under the header (dash-b's landing shape): the current page's siblings, one
   line, scrolling sideways when there are more than fit. Drawn as the shared sub menu's pills at the
   end of this file (guidelines §0.21, §0.26); assets/header.js adds the scroll arrows, brings the
   current pill into view and gathers the row into its own pill on scroll (desktop). */
.subnav { border-bottom: var(--rule-w) solid var(--rule); background: var(--paper-2); position: sticky; top: var(--bar-h); z-index: 30; }
.subnav ul { display: flex; gap: 4px; list-style: none; margin: 0; padding: 0; overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.subnav ul::-webkit-scrollbar { display: none; }
.subnav li { flex: none; }
.subnav a { display: block; padding: 14px 10px; font-size: var(--t-1); font-weight: 500; color: var(--ink-2); text-decoration: none; white-space: nowrap; border-bottom: 2px solid transparent; }
.subnav a:hover { color: var(--ink); }
.subnav a[aria-current="page"] { color: var(--ink); font-weight: 600; border-bottom-color: var(--accent); }
.subnav a:focus-visible { outline: 3px solid var(--accent); outline-offset: -3px; border-radius: var(--radius-sm); }
@media print { .subnav { display: none; } }

/* "Report a bug", "Get your key", "Contact us" (guidelines §0.12): one centred row right under the
   link columns, above the shelves, with the call to action in the middle in the violet fill. `.foot a`
   would repaint the buttons' text in the footer's link grey, so their own colours are restated. */
.foot .btn-accent { background: var(--fill); border-color: var(--fill); color: var(--on-fill); }
.foot .btn-accent:hover { background: var(--fill-hover); border-color: var(--fill-hover); color: var(--on-fill); }
[data-theme^="contrast"] .foot .btn-accent:hover { outline: 3px solid var(--accent); outline-offset: 2px; }
.foot-help { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin-block-start: clamp(32px, 5vw, 48px); }
.foot-help .btn { min-height: 44px; }
/* A phone cannot fit the three in one row, and wrapping would push the call to action off-centre, so
   they stack in the same order at one width. */
@media (max-width: 520px) { .foot-help { flex-direction: column; align-items: center; } .foot-help .btn { width: min(100%, 280px); justify-content: center; } }
.foot .foot-help .btn-primary { color: var(--btn-fg); }
.foot .foot-help .btn-primary:hover { color: #fff; }
.foot .foot-help .btn-ghost { color: var(--ink); }
[data-theme^="contrast"] .foot .foot-help .btn-primary:hover { color: var(--btn-fg); }

/* The two shelves (guidelines §0.27, as castomui.com draws them): the ecosystem, then the tools.
   Large rounded tiles in ONE row that scrolls sideways and snaps each tile to the centre, the row
   running out to both window edges. Every tile links to that product's own website (castomAPI's own
   tile to this site's home). Without site.js it is a native scrolling row; site.js makes it circular,
   opens it on the first tile centred, adds the arrow buttons (`.shelf-nav`) and the faint background
   mark (`.shelf-bg`). */
.foot-family { position: relative; margin-top: clamp(48px, 7vw, 80px); padding-top: clamp(32px, 5vw, 48px); border-top: var(--rule-w) solid var(--rule); }
/* The second shelf is a continuation, not a new chapter of the footer: no rule, most of the gap gone. */
.foot-family + .foot-family { margin-top: clamp(8px, 2vw, 24px); padding-top: 0; border-top: 0; }
.foot-family h2 { font-size: var(--t3); font-weight: 600; line-height: 1.15; letter-spacing: -.025em; text-transform: none; color: var(--ink); margin: 0 0 clamp(16px, 2.5vw, 28px); text-wrap: balance; }
.foot-family h2 span { color: var(--ink-3); }
/* The icon sits above the heading as its own line, centred, with a tight gap under it. A stroke that
   reads as fine at 24px reads as heavy blown up to 40px, so it is thinned to the same optical weight. */
.foot-family-centered { text-align: center; }
.foot-family-centered .foot-fam-ico { display: block; margin: 0 auto 6px; --ci-size: 40px; --ci-stroke: 0.8; color: var(--accent-ink); }
[data-theme^="contrast"] .foot-fam-ico { color: var(--ink); }
.foot-family-centered h2 { text-align: center; }
.foot-family-centered h2 span { display: block; margin-top: 6px; }
/* The row bleeds to both edges of the footer: the negative margin pulls it out of the content column
   and the same amount as padding puts the first tile back. Measured in the footer's own width (cqw,
   .foot is the container), not 100vw: with the scrollbar gutter reserved (html, above) 100vw is wider
   than the page, and the row poked past the footer's clip. The vertical padding leaves room for the
   hover lift and shadow, which the scroller would otherwise clip. */
.foot { container-type: inline-size; }
.foot-family ul { display: flex; gap: 20px; list-style: none; margin: 0 calc(50% - 50cqw); padding: 12px calc(50cqw - 50%) 28px; overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x mandatory; scroll-padding-inline: 0; scrollbar-width: none; }
.foot-family ul::-webkit-scrollbar { display: none; }
.foot-family li { flex: 0 0 min(298px, 65.6vw); margin: 0; scroll-snap-align: center; }
.foot-family li + li { margin-top: 0; }
/* Each tile is one link. Grid rows place the markup (mark, name, line, domain) the way an Apple tile
   reads: the name as a small uppercase eyebrow, the line as the headline, the mark large below, the
   domain as a pill at the foot. The mark is aria-hidden, so the reading order is untouched. */
.foot-family a { position: relative; display: grid; grid-template-rows: auto auto 1fr auto; justify-items: start; height: 100%; min-height: clamp(304px, 40vh, 368px); padding: clamp(24px, 3vw, 34px); overflow: hidden; color: var(--ink-2); text-decoration: none; background: rgba(1, 1, 1, .001); border: var(--rule-w) solid rgba(1, 1, 1, .001); box-shadow: var(--shadow); border-radius: calc(var(--radius) * 1.5); transition: transform var(--dur) var(--ease), background-color var(--dur) var(--ease), border-color var(--dur) var(--ease); }
/* The class-less-link reset (§0.11) zeroes padding-block-end on footer links; the tile keeps its own. */
.foot-family li > a:not([class]) { padding-block-end: clamp(24px, 3vw, 34px); }
/* The product's own hue names it, but a raw brand hue (livee's lime, iKonekt's green) drops to 2:1 on
   the pale footer, so it is deepened toward black in light and lifted toward white in dark; the
   contrast palettes use plain ink. */
.foot-family b { grid-row: 1; justify-self: center; font-size: var(--t-1); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: color-mix(in srgb, var(--hue, var(--accent-ink)) 55%, #000); }
[data-theme="dark"] .foot-family b { color: color-mix(in srgb, var(--hue, var(--accent-ink)) 50%, #fff); }
@media (prefers-color-scheme: dark) { :root:not([data-theme]) .foot-family b { color: color-mix(in srgb, var(--hue, var(--accent-ink)) 50%, #fff); } }
[data-theme^="contrast"] .foot-family b { color: var(--ink); }
.foot-family span { grid-row: 2; margin-top: 10px; font-size: var(--t2); font-weight: 600; line-height: 1.2; letter-spacing: -.02em; color: var(--ink); text-wrap: balance; }
.foot-family svg { grid-row: 3; align-self: center; justify-self: center; width: clamp(96px, 11vw, 100px); height: auto; aspect-ratio: 1; margin: 24px 0; }
.foot-family small { grid-row: 4; justify-self: center; display: inline-flex; align-items: center; gap: 8px; padding: 8px 16px; border: var(--rule-w) solid var(--rule); border-radius: var(--radius-pill); font-size: var(--t-1); font-weight: 500; line-height: 1.2; color: var(--ink-2); transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease); }
/* Hover and keyboard focus, never an underline: the tile lifts and its near-transparent wash deepens,
   a line in the brand's own colour (--hue, inline, data/shelves.py) draws across its top edge, and the
   domain pill fills while its arrow nudges forward. The focus ring is an outline, never clipped. */
.foot-family a:hover, .foot-family a:focus-visible { color: var(--ink-2); text-decoration: none; transform: translateY(-4px); background: rgba(1, 1, 1, .031); border-color: rgba(1, 1, 1, .031); }
.foot-family a::before { content: ""; position: absolute; inset-block-start: 0; inset-inline: 0; height: 3px; background: var(--hue, var(--accent)); transform: scaleX(0); transform-origin: 0 50%; transition: transform var(--dur) var(--ease); pointer-events: none; }
.foot-family small::after { content: ""; display: inline-block; flex: none; width: .9em; height: .9em; background: currentColor; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14M13 6l6 6-6 6'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14M13 6l6 6-6 6'/%3E%3C/svg%3E") center / contain no-repeat; transition: transform var(--dur) var(--ease); }
.foot-family a:hover::before, .foot-family a:focus-visible::before { transform: scaleX(1); }
/* The pill fills with the brand colour and its text turns white and bold on every tile — one
   consistent hover across the ecosystem (castomui.com, decided 2026-09-23). */
.foot-family a:hover small, .foot-family a:focus-visible small { background: var(--hue, var(--accent-ink)); border-color: var(--hue, var(--accent-ink)); color: #fff; font-weight: 700; }
.foot-family a:hover small::after, .foot-family a:focus-visible small::after { transform: translateX(3px); }
/* The contrast palettes keep one violet that measures ≥ 7:1 (DESIGN.md), so a brand colour gives way
   to it there and the background mark (a tint) is dropped. `!important` because --hue is inline. */
[data-theme="contrast"] .foot-family a, [data-theme="contrast-dark"] .foot-family a { --hue: var(--accent-ink) !important; }
[data-theme="contrast"] .foot-family .shelf-bg, [data-theme="contrast-dark"] .foot-family .shelf-bg { display: none; }
[data-theme="contrast-dark"] .foot-family a:is(:hover, :focus-visible) small { color: #000; }
@media (prefers-contrast: more) {
  :root:not([data-theme]) .foot-family a { --hue: var(--accent-ink) !important; }
  :root:not([data-theme]) .foot-family .shelf-bg { display: none; }
}
/* The tile's own mark again, large and faint, as its background, turned and pushed off the bottom
   corner. site.js adds it; every other child of the link is positioned, so they paint above it. */
.foot-family .shelf-bg { position: absolute; inset-block-end: -18%; inset-inline-end: -18%; width: 74%; height: auto; aspect-ratio: 1; margin: 0; opacity: .08; transform: rotate(-14deg); transition: transform .6s var(--ease), opacity var(--dur) var(--ease); pointer-events: none; }
.foot-family a > :not(.shelf-bg) { position: relative; }
.foot-family a:hover .shelf-bg, .foot-family a:focus-visible .shelf-bg { opacity: .14; transform: rotate(-6deg) scale(1.06); }
/* The two arrow buttons site.js adds, at the end of the shelf. The row is circular, so neither is
   ever disabled. The chevron is a mask, flipped for "previous". */
.shelf-nav { display: flex; justify-content: flex-end; gap: 12px; }
.shelf-nav button { display: grid; place-items: center; width: 44px; height: 44px; padding: 0; border: var(--rule-w) solid var(--rule); border-radius: var(--radius-pill); background: var(--field); color: var(--ink); cursor: pointer; transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease); }
.shelf-nav button::before { content: ""; width: 18px; height: 18px; background: currentColor; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 5l7 7-7 7'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 5l7 7-7 7'/%3E%3C/svg%3E") center / contain no-repeat; }
.shelf-nav .shelf-prev::before { transform: scaleX(-1); }
.shelf-nav button:hover { background: var(--ink); color: var(--paper); }
@media (max-width: 600px) {
  .foot-family ul { gap: 12px; }
  .foot-family a { min-height: 272px; }
}
.foot-legal { margin: 32px 0 0; padding-top: 20px; border-top: var(--rule-w) solid var(--rule); color: var(--ink-3); line-height: 1.7; }
.foot-line { display: flex; flex-wrap: wrap; gap: 8px 24px; justify-content: space-between; align-items: center; margin-top: 12px; color: var(--ink-3); }
.foot-line a { display: inline-block; padding: 8px 0; }
.totop { position: fixed; inset-inline-end: 20px; inset-block-end: 20px; width: 48px; height: 48px; border-radius: var(--radius-pill); background: var(--ink); color: var(--paper); display: grid; place-items: center; box-shadow: var(--shadow); opacity: 0; transform: translateY(12px); pointer-events: none; transition: opacity var(--dur), transform var(--dur) var(--ease); z-index: 40; }
.totop[data-show] { opacity: 1; transform: none; pointer-events: auto; }
.totop svg { width: 20px; height: 20px; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.totop:hover { background: var(--fill-hover); color: var(--on-fill); }
/* Back to top sits bottom-left and matches the chat button bottom-right (guidelines §0.24): both are
   --castomo-size, both 20px off the bottom. A ring draws around it with the scroll and is closed at
   the bottom of the page (site.js sets --totop-p). */
.totop { inset-inline-end: auto; inset-inline-start: 20px; width: var(--castomo-size); height: var(--castomo-size); }
.totop svg:not(.totop-ring) { width: 22px; height: 22px; }
.totop-ring { position: absolute; inset: 0; width: 100% !important; height: 100% !important; transform: rotate(-90deg); pointer-events: none; }
.totop-ring circle { fill: none; stroke: var(--accent); stroke-width: 3; stroke-linecap: round;
  stroke-dasharray: 1; stroke-dashoffset: calc(1 - var(--totop-p, 0)); transition: stroke-dashoffset .12s linear; }
[data-theme^="contrast"] .totop-ring circle { stroke: var(--paper); }
.toast { position: fixed; inset-inline: 0; inset-block-end: 20px; margin-inline: auto; width: max-content; max-width: calc(100vw - 40px); background: var(--ink); color: var(--paper); padding: 12px 16px; border-radius: var(--radius); box-shadow: var(--shadow); display: flex; gap: 12px; align-items: center; z-index: 80; font-size: var(--t-1); }
.toast button { background: var(--accent); color: #fff; border: 0; padding: 8px 14px; border-radius: var(--radius-pill); font-weight: 600; }

/* ------------------------------------------------------------------ components */
.chip { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: var(--radius-pill); font-size: var(--t-1); font-weight: 600; background: var(--accent-soft); color: var(--accent-ink); border: var(--rule-w) solid transparent; line-height: 1.3; white-space: nowrap; }
[data-theme="contrast"] .chip, [data-theme="contrast-dark"] .chip { border-color: var(--rule); }
.chip-plain { background: var(--field); color: var(--ink-2); }
.chip-pro { background: var(--ink); color: var(--paper); letter-spacing: .04em; }
.chips { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.method { font-family: ui-monospace, Menlo, Consolas, monospace; font-weight: 700; font-size: var(--t-1); letter-spacing: .04em; padding: 3px 8px; border-radius: 6px; border: var(--rule-w) solid currentColor; line-height: 1.3; }
.method[data-m="GET"] { color: var(--m-get); }
.method[data-m="POST"] { color: var(--m-post); }
.method[data-m="PUT"] { color: var(--m-put); }
.method[data-m="PATCH"] { color: var(--m-patch); }
.method[data-m="DELETE"] { color: var(--m-delete); }
.path { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: .95em; word-break: break-all; }

.card { background: var(--paper-2); border: var(--rule-w) solid var(--rule); border-radius: var(--radius); padding: clamp(18px, 2.5vw, 28px); position: relative; }
a.card { color: inherit; text-decoration: none; display: block; transition: transform var(--dur) var(--ease), border-color var(--dur); }
a.card:hover { transform: translateY(-2px); border-color: var(--accent); }
.card h3 { font-size: var(--t1); margin: 0 0 6px; }
.card p { color: var(--ink-2); margin: 0; font-size: var(--t-1); line-height: 1.5; }
.card .nav-ico { margin-bottom: 14px; }
.card-arrow::after { content: "→"; position: absolute; inset-inline-end: 20px; inset-block-start: 18px; color: var(--ink-3); font-size: var(--t1); transition: transform var(--dur) var(--ease); }
a.card-arrow:hover::after { transform: translateX(4px); color: var(--accent-ink); }
.tile-list { display: grid; gap: 8px; }
.tile-list a { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 12px; align-items: center; padding: 12px 14px; border: var(--rule-w) solid var(--rule); border-radius: var(--radius-sm); background: var(--paper-2); text-decoration: none; color: var(--ink); min-width: 0; }
.tile-list a:hover { border-color: var(--accent); }
.tile-list a > span { min-width: 0; }
/* A tile without a trailing cost spans the last column too, or the grid shows a hole. */
.tile-list a > span:nth-child(2):last-child { grid-column: 2 / -1; }
.tile-list b { font-weight: 600; overflow-wrap: anywhere; }
/* A path has no spaces; without this it is the one thing on a phone that can push the tile wide. */
.tile-list small { display: block; color: var(--ink-3); font-size: var(--t-1); overflow-wrap: anywhere; }
.tile-list .cost { font-size: var(--t-1); color: var(--ink-3); white-space: nowrap; }

.note { padding: 14px 18px; border: var(--rule-w) solid var(--rule); border-inline-start: 4px solid var(--accent); border-radius: var(--radius-sm); background: var(--paper-2); font-size: var(--t-1); color: var(--ink-2); margin: 20px 0; }
.note b { color: var(--ink); }
.note-warn { border-inline-start-color: var(--warn); }
.status-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-inline-end: 8px; vertical-align: middle; background: var(--ink-3); }
.status-dot[data-s="up"] { background: var(--ok); }
.status-dot[data-s="down"] { background: var(--bad); }

pre.code { background: var(--field); border: var(--rule-w) solid var(--rule); border-radius: var(--radius-sm); padding: 14px 16px; overflow: auto; font-size: .9rem; line-height: 1.55; margin: 0; tab-size: 2; color: var(--ink); }
[data-theme="dark"] pre.code, [data-theme="contrast-dark"] pre.code { background: #000; }
@media (prefers-color-scheme: dark) { :root:not([data-theme]) pre.code { background: #000; } }
.codebox { position: relative; }
.codebox .copy { position: absolute; inset-inline-end: 8px; inset-block-start: 8px; padding: 4px 10px; font-size: var(--t-1); border-radius: var(--radius-pill); border: var(--rule-w) solid var(--rule); background: var(--paper-2); color: var(--ink-2); font-weight: 600; min-height: 28px; }
.codebox .copy:hover { color: var(--ink); border-color: var(--ink); }
.tabs { display: flex; flex-wrap: wrap; gap: 4px; border-bottom: var(--rule-w) solid var(--rule); margin-bottom: 12px; }
.tabs button { padding: 8px 14px; border: 0; background: transparent; font-weight: 600; font-size: var(--t-1); color: var(--ink-3); border-bottom: 2px solid transparent; margin-bottom: -1px; min-height: 40px; }
.tabs button[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--accent); }
.tabs button:hover { color: var(--ink); }
.tabpanel[hidden] { display: none; }
[data-tabs="static"] .tabpanel + .tabpanel { margin-top: 12px; }

.table-wrap { overflow-x: auto; border: var(--rule-w) solid var(--rule); border-radius: var(--radius-sm); background: var(--paper-2); }
.table { font-size: var(--t-1); }
.table th, .table td { text-align: left; padding: 10px 14px; border-bottom: var(--rule-w) solid var(--rule); vertical-align: top; }
.table thead th { font-weight: 600; color: var(--ink-2); background: var(--field); white-space: nowrap; }
.table tbody tr:last-child td { border-bottom: 0; }
.table code { background: var(--field); padding: 1px 6px; border-radius: 5px; font-size: .88em; }
.table .req { color: var(--accent-ink); font-weight: 600; }

.faq details { border-top: var(--rule-w) solid var(--rule); }
.faq details:last-child { border-bottom: var(--rule-w) solid var(--rule); }
.faq summary { list-style: none; cursor: pointer; padding: 16px 40px 16px 0; font-weight: 600; position: relative; font-size: var(--t0); }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; position: absolute; inset-inline-end: 4px; top: 12px; font-size: var(--t2); font-weight: 400; color: var(--ink-3); line-height: 1; }
.faq details[open] summary::after { content: "−"; }
.faq details > div, .faq details > p { padding: 0 0 18px; color: var(--ink-2); max-width: 68ch; }

/* The one breadcrumbs set (guidelines §0.17, §0.21, §0.26): lifted by tools/build.py to the top of
   <main>, centred, led by a faint home icon, with as much air below as above. No line at rest; the
   §0.11 line grows only on hover and focus. 14px, not the guideline's 11: nothing on this site is
   set under 14px (DESIGN.md, check_css.py). */
.crumbs { font-size: var(--t-1); color: var(--ink-3); margin: 0; }
.crumbs ol { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 4px 8px; }
.crumbs li + li::before { content: "/"; content: "/" / ""; margin-inline-end: 8px; color: var(--rule); }
.crumbs a { color: var(--ink-3); text-decoration: none; }
.crumbs a:hover { color: var(--ink); }
.crumbs a:not([class]) { background-size: 0% var(--u-thick); }
.crumbs a:not([class]):is(:hover, :focus-visible, :active) { background-size: 100% var(--u-thick); }
.crumbs [aria-current] { color: var(--ink-2); }
/* margin-inline again: `.crumbs { margin: 0 }` above comes after `.wrap` and cancelled its centring,
   so the trail sat left of the sub menu pills on wide screens. */
.crumbs-bar { padding-block: 18px 22px; margin-inline: auto; }
.crumbs-bar ol { justify-content: center; }
.crumbs-bar li:first-child a { display: inline-flex; align-items: center; gap: 6px; }
.crumb-home { width: 13px; height: 13px; flex: none; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; opacity: .3; }

.toc { font-size: var(--t-1); position: sticky; top: calc(var(--bar-h) + 24px); align-self: start; }
.toc h2 { font-size: var(--t-1); text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3); margin: 0 0 10px; }
.toc ol { list-style: none; margin: 0; padding: 0; border-inline-start: var(--rule-w) solid var(--rule); }
.toc a { display: block; padding: 5px 14px; color: var(--ink-2); text-decoration: none; border-inline-start: 2px solid transparent; margin-inline-start: -1px; }
.toc a:hover, .toc a[aria-current] { color: var(--ink); border-inline-start-color: var(--accent); }
.with-toc { display: grid; grid-template-columns: 1fr 15rem; gap: clamp(24px, 5vw, 64px); align-items: start; }
@media (max-width: 960px) { .with-toc { grid-template-columns: 1fr; } .toc { position: static; } }

.search { position: relative; margin: 0 0 20px; }
.search input { width: 100%; padding: 14px 16px 14px 46px; border: var(--rule-w) solid var(--rule); border-radius: var(--radius-pill); background: var(--paper-2); font-size: var(--t0); min-height: 52px; }
.search input:focus-visible { outline-offset: 0; }
.search svg { position: absolute; inset-inline-start: 16px; top: 50%; transform: translateY(-50%); width: 20px; height: 20px; stroke: var(--ink-3); stroke-width: 2; stroke-linecap: round; pointer-events: none; }
.search-hint { font-size: var(--t-1); color: var(--ink-3); margin: 6px 0 0 16px; }
.search-empty { padding: 24px; text-align: center; color: var(--ink-3); border: var(--rule-w) dashed var(--rule); border-radius: var(--radius); }

.stat { padding: 20px 0; border-top: var(--rule-w) solid var(--rule); }
.stat b { display: block; font-size: clamp(var(--t4), 5vw, var(--t6)); font-weight: 700; letter-spacing: -.04em; line-height: 1; color: var(--ink); font-variant-numeric: tabular-nums; }
.stat span { display: block; color: var(--ink-2); margin-top: 8px; font-size: var(--t-1); }
.stat i { font-style: normal; color: var(--accent-ink); }

/* ------------------------------------------------------------------ the tester */
.tester { border: var(--rule-w) solid var(--rule); border-radius: var(--radius); background: var(--paper-2); overflow: hidden; }
.tester-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding: 12px 14px; border-bottom: var(--rule-w) solid var(--rule); background: var(--field); }
.tester-url { flex: 1 1 18rem; font-family: ui-monospace, Menlo, Consolas, monospace; font-size: .9rem; min-width: 0; word-break: break-all; }
.tester-url input { width: 100%; padding: 8px 10px; border: var(--rule-w) solid var(--rule); border-radius: var(--radius-sm); background: var(--paper-2); font-family: inherit; font-size: inherit; }
.tester-grid { display: grid; grid-template-columns: 1fr 1fr; }
@media (max-width: 860px) { .tester-grid { grid-template-columns: 1fr; } }
.tester-pane { padding: 14px; min-width: 0; }
.tester-pane + .tester-pane { border-inline-start: var(--rule-w) solid var(--rule); }
@media (max-width: 860px) { .tester-pane + .tester-pane { border-inline-start: 0; border-top: var(--rule-w) solid var(--rule); } }
.tester-pane h2 { font-size: var(--t-1); text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3); margin: 0 0 10px; display: flex; align-items: center; gap: 10px; }
.tester-pane h2 .chip { text-transform: none; letter-spacing: 0; }
.tester textarea { width: 100%; min-height: 12rem; resize: vertical; padding: 12px; border: var(--rule-w) solid var(--rule); border-radius: var(--radius-sm); background: var(--field); font-family: ui-monospace, Menlo, Consolas, monospace; font-size: .9rem; line-height: 1.55; tab-size: 2; }
.tester .headers { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: .85rem; color: var(--ink-2); margin: 0 0 10px; padding: 0; list-style: none; }
.tester .headers li { padding: 3px 0; }
.tester .headers b { color: var(--ink); font-weight: 600; }
.tester-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 10px; }
.tester-progress { height: 3px; background: var(--rule); position: relative; overflow: hidden; }
.tester-progress::after { content: ""; position: absolute; inset: 0; background: var(--accent); transform: translateX(-100%); }
.tester[data-state="sending"] .tester-progress::after { animation: prog .5s var(--ease) forwards; }
@keyframes prog { to { transform: none; } }
.tester-out { min-height: 12rem; }
.tester-out pre.code { max-height: 28rem; }
.tester-status { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; font-size: var(--t-1); color: var(--ink-2); margin-bottom: 10px; }
.tester-status .ok { color: var(--ok); font-weight: 700; }
.tester-msg { color: var(--bad); font-size: var(--t-1); margin-top: 8px; font-weight: 600; }
.tester-foot { padding: 10px 14px; border-top: var(--rule-w) solid var(--rule); font-size: var(--t-1); color: var(--ink-3); display: flex; flex-wrap: wrap; gap: 8px 16px; justify-content: space-between; align-items: center; }
.tester-foot a { display: inline-block; padding: 6px 0; font-weight: 600; }

/* ------------------------------------------------------------------ page sections */
.hero { padding: clamp(48px, 9vw, 120px) 0 clamp(40px, 6vw, 80px); position: relative; overflow: hidden; }
/* Two short clauses, so the headline is set smaller than a full-bleed display line would be and
   wraps after the first: 12ch holds "One account." on its own row at every width. */
.hero h1 { font-size: clamp(2.1rem, 5vw, 3.6rem); font-weight: 700; letter-spacing: -.03em; line-height: 1.02; margin: 0 0 20px; max-width: 12ch; }
.hero h1 em { font-style: normal; color: var(--accent-ink); }
/* One sentence now, so it is set at body scale and measured to stop well short of the column. */
.hero .lead { font-size: clamp(var(--t-1), 1.5vw, var(--t1)); margin-bottom: 28px; max-width: 46ch; }
.hero-ctas { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-bottom: 20px; }
.hero-facts { display: flex; flex-wrap: wrap; gap: 8px 20px; font-size: var(--t-1); color: var(--ink-3); }
.hero-facts b { color: var(--ink); font-weight: 600; }
.hero-grid { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: clamp(24px, 5vw, 64px); align-items: center; }
@media (max-width: 960px) { .hero-grid { grid-template-columns: 1fr; } .hero h1 { max-width: none; } }

.hero-tester { max-width: 560px; margin-inline: auto; width: 100%; }

.reasons { counter-reset: reason; }
.reason { display: grid; grid-template-columns: 5rem 1fr; gap: 16px clamp(16px, 3vw, 40px); padding: clamp(32px, 5vw, 56px) 0; border-top: var(--rule-w) solid var(--rule); }
.reason:last-child { border-bottom: var(--rule-w) solid var(--rule); }
.reason-n { font-size: var(--t1); font-weight: 700; color: var(--accent-ink); font-variant-numeric: tabular-nums; letter-spacing: -.02em; line-height: 1.2; padding-top: .35em; }
.reason-n::before { counter-increment: reason; content: counter(reason, decimal-leading-zero); }
.reason-body { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(20px, 4vw, 48px); align-items: start; }
.reason h2 { font-size: clamp(var(--t2), 3vw, var(--t4)); margin-bottom: 12px; }
.reason p { color: var(--ink-2); max-width: 52ch; }
.reason .more { font-weight: 600; }
@media (max-width: 960px) { .reason-body { grid-template-columns: 1fr; } }
@media (max-width: 600px) { .reason { grid-template-columns: 1fr; gap: 6px; } }
.payload { border: var(--rule-w) solid var(--rule); border-radius: var(--radius); background: var(--paper-2); overflow: hidden; font-size: .88rem; min-width: 0; }
.payload-h { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding: 10px 14px; background: var(--field); border-bottom: var(--rule-w) solid var(--rule); font-family: ui-monospace, Menlo, Consolas, monospace; min-width: 0; }
.payload-h code { overflow-wrap: anywhere; min-width: 0; }
.payload-h .cost { margin-inline-start: auto; color: var(--ink-3); font-size: var(--t-1); }
.payload pre { margin: 0; padding: 12px 14px; overflow: auto; line-height: 1.5; font-size: .85rem; color: var(--ink); }
.payload pre + pre { border-top: var(--rule-w) dashed var(--rule); }
.payload .k { color: var(--accent-ink); }
.payload .s { color: var(--ok); }
.payload .n { color: var(--m-put); }
.payload .c { color: var(--ink-3); }

.bento { display: grid; grid-template-columns: repeat(12, 1fr); gap: 16px; }
.bento > * { grid-column: span 4; }
.bento .w6 { grid-column: span 6; }
.bento .w8 { grid-column: span 8; }
.bento .w12 { grid-column: span 12; }
@media (max-width: 960px) { .bento > *, .bento .w6, .bento .w8 { grid-column: span 6; } }
@media (max-width: 600px) { .bento > *, .bento .w6, .bento .w8, .bento .w12 { grid-column: span 12; } }

.logo-row { display: flex; flex-wrap: wrap; gap: 10px; }
.logo-row a { display: inline-flex; align-items: center; gap: 8px; padding: 8px 14px 8px 8px; border: var(--rule-w) solid var(--rule); border-radius: var(--radius-pill); text-decoration: none; color: var(--ink); font-weight: 600; font-size: var(--t-1); background: var(--paper-2); }
.logo-row a:hover { border-color: var(--accent); }
.logo-row i { width: 22px; height: 22px; border-radius: 50%; background: var(--hue, var(--accent)); display: inline-block; }
[data-theme="contrast"] .logo-row i, [data-theme="contrast-dark"] .logo-row i { background: var(--ink); }

.plans { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; align-items: stretch; }
@media (max-width: 860px) { .plans { grid-template-columns: 1fr; } }
.plan { display: flex; flex-direction: column; }
.plan .price { font-size: var(--t4); font-weight: 700; letter-spacing: -.03em; line-height: 1; margin: 10px 0 4px; }
.plan .price small { font-size: var(--t-1); font-weight: 500; color: var(--ink-3); letter-spacing: 0; }
.plan ul { list-style: none; margin: 16px 0 20px; padding: 0; font-size: var(--t-1); color: var(--ink-2); flex: 1; }
.plan li { padding: 6px 0 6px 26px; position: relative; }
.plan li::before { content: ""; position: absolute; left: 4px; top: 12px; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
.plan-featured { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }

.cta-band { text-align: center; padding: clamp(48px, 8vw, 96px) 0; }
.cta-band h2 { font-size: clamp(var(--t3), 4.5vw, var(--t5)); max-width: 20ch; margin-inline: auto; }
.cta-band .lead { margin-inline: auto; }
.cta-band .hero-ctas { justify-content: center; }

.steps { counter-reset: step; display: grid; gap: 16px; }
/* A row of steps laid out with .grid instead of .steps still numbers 1, 2, 3 — the counter is reset on
   the grid, not on each step, or every step would read "1". */
.grid { counter-reset: step; }
.step { display: grid; grid-template-columns: 2.6rem 1fr; gap: 14px; align-items: start; }
.step::before { counter-increment: step; content: counter(step); width: 2.6rem; height: 2.6rem; border-radius: 50%; background: var(--ink); color: var(--paper); display: grid; place-items: center; font-weight: 700; }
.step h3 { font-size: var(--t1); margin: 4px 0 4px; }
.step p { margin: 0; color: var(--ink-2); }

.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li { display: grid; grid-template-columns: 9rem 1fr; gap: 16px; padding: 14px 0; border-top: var(--rule-w) solid var(--rule); }
.timeline time { color: var(--ink-3); font-size: var(--t-1); font-variant-numeric: tabular-nums; }
@media (max-width: 600px) { .timeline li { grid-template-columns: 1fr; gap: 4px; } }

.article { max-width: 68ch; }
.article-head { margin-bottom: 32px; }
.article-meta { color: var(--ink-3); font-size: var(--t-1); }

/* ------------------------------------------------------------------ castomo-icons placements
   Small doodles (24px, the .ci grammar) sit inline beside an eyebrow or a heading, coloured with
   the accent so they read as a mark rather than a second icon set. Big doodles are decorative only:
   position:absolute inside a position:relative host, never over the text column (section-head and
   the CTA band cap their own text at 68ch / a centred measure, so the doodle lives in the gutter),
   z-index -1 so a heading that happens to run long always wins, and hidden under 960px — the same
   breakpoint the rest of the chrome collapses at — rather than crowd a narrow column. */
.ci-sm { --ci-size: 18px; }
/* display:inline-block is not cosmetic here: the reset above sets `img, svg { display: block }`, and
   the build expands every <i data-ci> placeholder into a real <svg>, so icons.css's own
   `i[data-ci] { display: inline-block }` never matches anything on a built page. Without this an
   icon meant to sit beside its text takes a line of its own above it. */
.eyebrow-ico, .h-ico { color: var(--accent-ink); display: inline-block; }
.eyebrow-ico { margin-inline-end: 6px; vertical-align: -3px; }
/* The icon trails its heading at every level now, so the spacing sits on its leading edge. */
.h-ico { margin-inline-start: 10px; vertical-align: -6px; }
/* A page opens on its icon. Inside the h1 it takes the headline's own size — 1em, so it tracks the
   h1's clamp at every width instead of sitting beside it as an 18px mark — and the spacing goes to
   em units with it. The small .h-ico above still applies inside section h2s, where a mark beside
   the heading is the point rather than the opening. */
h1 .h-ico { --ci-size: 1em; margin-inline-start: .28em; vertical-align: -0.14em; }

.section-head, .cta-band { position: relative; }
.deco { position: absolute; pointer-events: none; color: var(--accent); opacity: .55; z-index: -1; }
[data-theme="contrast"] .deco, [data-theme="contrast-dark"] .deco { opacity: .4; }
.deco-corner { inset-block-start: 0; inset-inline-end: clamp(-160px, -14vw, -32px); --ci-big: clamp(72px, 9vw, 140px); }
.deco-cta { inset-block-start: 50%; inset-inline-end: clamp(8px, 5vw, 56px); transform: translateY(-50%); --ci-big: clamp(64px, 8vw, 110px); }
.hero .deco-hero { inset-block-start: clamp(-8px, -1vw, 12px); inset-inline-end: clamp(-24px, -2vw, 4px); --ci-big: clamp(90px, 11vw, 170px); }
@media (max-width: 960px) { .deco { display: none; } }

/* ------------------------------------------------------------------ print */
@media print {
  .bar, .foot, .totop, .player, .tester-actions, .skip { display: none !important; }
  body { background: #fff; color: #000; }
  a { color: #000; }
  .tester, .card, .payload { break-inside: avoid; }
}

/* ==== The shared ecosystem chrome (castomui-web-2026 assets/site.css, its chrome tail) =============
   One header, sub menu, trail and footer on every castomo site (ECOSYSTEM-GUIDELINES §0.21–§0.27).
   Copied from castomui.com with castomAPI's own adaptations, each noted where it is made:
   the violet that carries words is --fill / --on-fill (every theme block above), never white on
   --accent; nothing is set under 14px (DESIGN.md), so the guideline's 12px pills and 11px trail are
   14px here; the brand mark has no plate and its tiles are the direct children of its <svg>. */

/* Static sub menu = the castomo.com/demos filter pills (§0.21): soft grey at rest, the current page
   flat violet. The grey is mixed from the page's own ink and paper, so it follows every theme. */
.subnav ul { gap: 8px; padding-block: 10px; }
.subnav li > a:not([class]) {
  padding: 5px 15px; border: 1px solid var(--paper); border-radius: 22px;
  background: var(--pill-bg); color: var(--ink);
  font-size: var(--t-1); font-weight: 700; line-height: 1.3; letter-spacing: .3px;
  transition: background-color .25s ease-in-out, color .25s ease-in-out;
}
.subnav li > a:not([class]):hover { background-color: var(--pill-bg-hover); color: var(--ink); }
.subnav li > a:not([class])[aria-current="page"] { background: var(--fill); border-color: transparent; color: var(--on-fill); font-weight: 700; transition-duration: .7s; }
.subnav li > a:not([class]):focus-visible { border-radius: 22px; outline-offset: 2px; }
@media (forced-colors: active) { .subnav li > a:not([class])[aria-current="page"] { forced-color-adjust: none; background: Highlight; color: HighlightText; } }

/* Idle logo (§0.21): site.js sets data-idle on the header mark every 5–7 s, one cut at random, never
   the same twice running; each plays once. The mark's tiles are its <svg>'s own children (castomUI's
   sit in a <g> under a plate). The global reduced-motion rules remove them. */
.bar .brand svg[data-idle] > * { animation-duration: .9s; animation-timing-function: cubic-bezier(.2, .7, .3, 1); animation-fill-mode: both; }
.bar .brand svg[data-idle="pop"] > * { animation-name: mk-pop; }
.bar .brand svg[data-idle="spin"] > * { animation-name: mk-spin; }
.bar .brand svg[data-idle="bounce"] > * { animation-name: mk-bounce; }
.bar .brand svg[data-idle="wiggle"] > * { animation-name: mk-wiggle; }
.bar .brand svg[data-idle="flip"] > * { animation-name: mk-flip; }
@keyframes mk-spin { 0% { transform: rotate(0); } 80% { transform: rotate(372deg); } 100% { transform: rotate(360deg); } }
@keyframes mk-bounce { 0%, 100% { transform: translateY(0); } 30% { transform: translateY(-18%); } 55% { transform: translateY(0) scale(1.06, .94); } 75% { transform: translateY(-6%); } }
@keyframes mk-wiggle { 0%, 100% { transform: rotate(0); } 20% { transform: rotate(-12deg); } 40% { transform: rotate(10deg); } 60% { transform: rotate(-6deg); } 80% { transform: rotate(3deg); } }
@keyframes mk-flip { 0% { transform: perspective(80px) rotateY(0); } 100% { transform: perspective(80px) rotateY(360deg); } }

/* Main menu (§0.21): the hovered or open item is the violet fill with its word colour, never grey.
   The section-you-are-in mark keeps its own look until hovered. */
.nav-top:hover, .nav-group > summary:hover, .nav-group[open] > summary,
.nav-group[data-current] > summary:hover, .nav-group[data-current][open] > summary { color: var(--on-fill); background: var(--fill); }
.nav-group > summary:hover svg, .nav-group[open] > summary svg { color: inherit; }

/* Where a menu opens from (castomui.com). A three-column menu hangs from the bar, centred under it
   (tools/build.py data-anchor), because from a centred menu a 70rem panel runs off the edge. Only
   above the drawer breakpoint: in the drawer every menu is static already. */
@media (min-width: 1201px) {
  .nav-group[data-anchor="bar"] { position: static; }
  .nav-group[data-anchor="bar"] > .nav-menu { inset-inline: 0; margin-inline: auto; top: calc(100% + 6px); }
}

/* ALTERNATIVE shelf style, opt-in (§0.27, §13): add "shelf-alt" to a .foot-family section. "The mark
   is the hero": a card holding a tinted window, the mark large in it with its faint twin behind, the
   name, line and domain pill below; on hover the window takes the brand colour and the mark pops out
   of the tile. Visuals only: the row, snap, arrows and markup are the default shelf's. */
.foot-family.shelf-alt a {
  isolation: isolate; grid-template-rows: 188px auto auto 1fr; justify-items: center; text-align: center;
  padding: 12px 12px 20px; background: var(--tile-bg); border-color: var(--rule); overflow: visible;
  transition: transform .35s cubic-bezier(.3, 1.45, .5, 1), box-shadow .35s var(--ease);
}
.foot-family.shelf-alt a:hover, .foot-family.shelf-alt a:focus-visible { background: var(--tile-bg); border-color: var(--rule); box-shadow: 0 16px 28px -18px rgba(11, 12, 16, .4); }
.foot-family.shelf-alt li > a:not([class]) { padding-block-end: 28px; }
.foot-family.shelf-alt a::after { content: ""; position: absolute; inset: 12px 12px auto; height: 188px; border-radius: calc(var(--radius) * 1.1); background: color-mix(in srgb, var(--ink) 5%, var(--tile-bg)); z-index: -1; pointer-events: none; transition: background-color .35s var(--ease); }
.foot-family.shelf-alt a:hover::after, .foot-family.shelf-alt a:focus-visible::after { background: color-mix(in srgb, var(--hue, var(--accent)) 12%, var(--tile-bg)); }
.foot-family.shelf-alt a::before { z-index: 2; left: 10px; right: 10px; border-radius: calc(var(--radius) * 1.5) calc(var(--radius) * 1.5) 0 0; }
.foot-family.shelf-alt a > svg:not(.shelf-bg) { grid-row: 1; align-self: center; justify-self: center; width: 96px; margin: 0; z-index: 1; transition: transform .5s cubic-bezier(.3, 1.7, .5, 1); }
.foot-family.shelf-alt a:hover > svg:not(.shelf-bg), .foot-family.shelf-alt a:focus-visible > svg:not(.shelf-bg) {
  z-index: 3; transform: translateY(-44px) scale(3) rotate(-6deg); filter: drop-shadow(0 6px 6px rgba(11, 12, 16, .18));
}
.foot-family.shelf-alt .shelf-bg {
  grid-area: auto; inset: 12px auto auto 12px; width: calc(100% - 24px); height: 188px; aspect-ratio: auto; overflow: hidden;
  border-radius: calc(var(--radius) * 1.1); opacity: .1; transform: none; z-index: 0; filter: grayscale(1); transition: filter .35s var(--ease), opacity var(--dur) var(--ease);
}
.foot-family.shelf-alt .shelf-bg > * { transform-box: view-box; transform-origin: 50% 50%; transform: translate(26%, 20%) scale(1.9) rotate(-14deg); transition: transform .6s var(--ease); }
.foot-family.shelf-alt a:hover .shelf-bg, .foot-family.shelf-alt a:focus-visible .shelf-bg { opacity: .16; transform: none; filter: none; }
.foot-family.shelf-alt a:hover .shelf-bg > *, .foot-family.shelf-alt a:focus-visible .shelf-bg > * { transform: translate(22%, 16%) scale(2) rotate(-6deg); }
.foot-family.shelf-alt b { grid-row: 2; justify-self: center; margin: 16px 6px 0; font-size: var(--t1); font-weight: 700; letter-spacing: -.02em; text-transform: none; }
.foot-family.shelf-alt span { grid-row: 3; justify-self: center; margin: 4px 6px 0; font-size: var(--t0); font-weight: 400; line-height: 1.5; letter-spacing: 0; color: var(--ink-2); text-wrap: balance; }
.foot-family.shelf-alt small { grid-row: 4; align-self: end; justify-self: center; margin: 16px 6px 0; }
/* The popped mark (3 × 96px) needs headroom the scroller would clip, handed back by negative margins. */
.foot-family.shelf-alt ul { padding-top: 124px; margin-top: -112px; padding-bottom: 56px; margin-bottom: -28px; }
@media (max-width: 600px) { .foot-family.shelf-alt a { min-height: 0; grid-template-rows: 160px auto auto 1fr; } .foot-family.shelf-alt a::after, .foot-family.shelf-alt .shelf-bg { height: 160px !important; } }
@media (prefers-reduced-motion: reduce) { .foot-family.shelf-alt a > svg:not(.shelf-bg), .foot-family.shelf-alt .shelf-bg > * { transition: none; } }

/* ==== Site header: pill + bubble (header lab #13; ECOSYSTEM-GUIDELINES §0.26) ======================
   Resting: a full-width bar, logo left, bold menu on the true centre, tools and the CTA right; the sub
   menu and the trail centred, with no strip of their own. Scrolled (desktop): the bar gathers into a
   floating pill, the sub menu into a second pill. Submenus open in one shared speech bubble that
   glides between sections, an OutlineTone scene leaping in. Behaviour: assets/header.js; the no-JS
   page keeps the plain bar and the <details> menus. */

/* §0.22 CTA: the one violet-filled pill in the header, one moving icon (the rocket, §0.23), an
   attention pulse on load and every 8–12 s (header.js). */
.bar-cta.btn { background: var(--fill); color: var(--on-fill); border-color: transparent; box-shadow: 0 6px 18px -6px color-mix(in srgb, var(--fill) 70%, transparent); transition: transform .25s cubic-bezier(.34, 1.56, .64, 1), box-shadow .25s; }
.bar-cta.btn:hover, .bar-cta.btn:focus-visible { background: var(--fill); color: var(--on-fill); transform: translateY(-2px); box-shadow: 0 10px 24px -8px color-mix(in srgb, var(--fill) 80%, transparent); }
[data-theme^="contrast"] .bar-cta.btn { box-shadow: none; }
.bar-cta.btn.cta-ping { animation: cta-ping 1.1s cubic-bezier(.2, .7, .3, 1); }
@keyframes cta-ping { 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--fill) 55%, transparent); } 70% { box-shadow: 0 0 0 12px transparent; } 100% { box-shadow: 0 6px 18px -6px color-mix(in srgb, var(--fill) 70%, transparent); } }
.bar-cta .rkt { width: 18px; height: 18px; margin-inline-start: 8px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; transition: transform .5s cubic-bezier(.34, 1.56, .64, 1); }
.bar-cta:hover .rkt, .bar-cta:focus-visible .rkt, .bar-cta.cta-ping .rkt { animation: rkt-fly 1.1s cubic-bezier(.3, .7, .3, 1); }
@keyframes rkt-fly { 0% { transform: none; opacity: 1; } 40% { transform: translate(14px, -14px) rotate(8deg); opacity: 0; } 41% { transform: translate(-12px, 12px); opacity: 0; } 100% { transform: none; opacity: 1; } }

/* Flags (§0.23), written in by the build as empty spans (tools/build.py FLAGS); the SVGs are the ones
   castomui.com inlines, as data URIs so the markup stays one span. */
.flag { display: inline-block; width: 20px; height: 14px; border-radius: 2px; flex: none; box-shadow: 0 0 0 1px var(--rule); background: center / cover no-repeat; }
.flag-en { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 30'%3E%3Cpath d='M0 0v30h60V0z' fill='%23012169'/%3E%3Cpath d='M0 0l60 30m0-30L0 30' stroke='%23fff' stroke-width='6'/%3E%3Cpath d='M0 0l60 30m0-30L0 30' stroke='%23C8102E' stroke-width='4'/%3E%3Cpath d='M30 0v30M0 15h60' stroke='%23fff' stroke-width='10'/%3E%3Cpath d='M30 0v30M0 15h60' stroke='%23C8102E' stroke-width='6'/%3E%3C/svg%3E"); }
.flag-pl { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 10'%3E%3Cpath fill='%23fff' d='M0 0h16v5H0z'/%3E%3Cpath fill='%23DC143C' d='M0 5h16v5H0z'/%3E%3C/svg%3E"); }
.lang-btn { gap: 6px; }
.bar-end { gap: 8px; }
.bar-end .lang { order: 9; }
.bar-end .bar-cta { order: 10; }
.bar-tools { display: inline-flex; align-items: center; gap: 2px; border: 1px solid var(--rule); border-radius: 99px; padding: 2px; }
.bar-tools > * { border: 0 !important; }
/* The name stays beside the mark on a phone: the pair just tightens, one step smaller under 350px. */
@media (max-width: 420px) { .bar .brand { gap: 7px; font-size: 18px; } .bar .brand svg { width: 28px; height: 28px; } }
@media (max-width: 350px) { .bar .brand { gap: 5px; font-size: 16px; } .bar .brand svg { width: 24px; height: 24px; } }

/* ── the morphing pill: the bar keeps its size forever; a background layer is clipped to insets that
   grow with scroll, so it gathers from a full-width bar into a floating pill hugging logo, menu and
   tools. The logo slides right and the tools left by the same amount (transform only), so nothing
   below ever re-lays out. See-through at rest, 99% --paper-2 as it gathers; no rule at any point. */
@media (min-width: 1201px) {
  .bar { background: transparent; border-bottom-color: transparent; }
  .bar-in { position: relative; z-index: 2; }
  .pill-glow { position: absolute; inset: 0; z-index: 0; pointer-events: none;
    filter: drop-shadow(0 12px 24px rgb(11 12 16 / calc(var(--p, 0) * .18))); }
  .pill { position: absolute; inset: 0; background: color-mix(in srgb, var(--paper-2) calc(var(--p, 0) * 99%), transparent);
    clip-path: inset(var(--iy, 0px) var(--ixr, 0px) var(--iy, 0px) var(--ixl, 0px) round calc(var(--p, 0) * 999px)); }
  .bar-in > .brand, .bar-in .nav-links { transform: translateX(var(--shift-l, 0px)); }
  .bar-in .bar-end { transform: translateX(calc(-1 * var(--shift-r, 0px))); }
  .bar[data-layout="centre"] .bar-in .nav-links { transform: none; }

  /* ── the bubble: each section's own <details> panel stays where it is (links, focus order, no-JS
     intact) but loses its box; one shared bubble behind them glides and resizes to whichever is open,
     its tail pointing at the open item. Switching sections morphs instead of blinking. */
  .nav-menu { background: transparent; border-color: transparent; box-shadow: none; animation: none;
    top: calc(100% + 40px); --menu-w: 50rem; padding: 16px 16px 16px 196px; min-height: 236px; }
  .nav-menu[data-cols="3"] { --menu-w: 70rem; }
  .nav-menu::before { top: -42px; height: 42px; }
  .bubble { position: absolute; left: 0; top: 0; z-index: 1; width: var(--w, 0px); height: var(--h, 0px);
    transform: translate(var(--x, 0px), var(--y, 0px)) scale(.94); transform-origin: var(--tail, 50%) 0;
    background: var(--paper-2); border: var(--rule-w) solid var(--rule); border-radius: 26px;
    box-shadow: 0 30px 60px -30px rgb(11 12 16 / .45), 0 2px 6px rgb(11 12 16 / .06);
    opacity: 0; pointer-events: none;
    transition: transform .42s cubic-bezier(.3, 1.3, .5, 1), width .42s cubic-bezier(.3, 1.3, .5, 1),
                height .42s cubic-bezier(.3, 1.3, .5, 1), opacity .18s ease; }
  .bubble.on { opacity: 1; transform: translate(var(--x, 0px), var(--y, 0px)); }
  .bubble.instant, .bubble.instant::before { transition: none; }
  /* The tail: a rotated square sharing the bubble's fill, so the pair reads as one speech bubble. */
  .bubble::before { content: ""; position: absolute; top: -12px; left: calc(var(--tail, 50%) - 12px); width: 24px; height: 24px;
    background: inherit; border-left: var(--rule-w) solid var(--rule); border-top: var(--rule-w) solid var(--rule);
    border-top-left-radius: 5px; transform: rotate(45deg); transition: left .42s cubic-bezier(.3, 1.3, .5, 1); }
  /* The scene's stage: a tinted well on the bubble's left, the OutlineTone piece leaping in (it
     draws itself in the first time it is seen, as every <outlinetone-art> does). */
  .bubble::after { content: ""; position: absolute; left: 14px; top: 14px; bottom: 14px; width: 184px; border-radius: 18px;
    background: color-mix(in srgb, var(--accent) 9%, var(--paper-2)); }
  .nav-art { position: absolute; left: 22px; bottom: 22px; width: 168px; pointer-events: none; }
  /* Selectors carry .bar .nav-links on purpose: they must outrank the §0.11 link reset and the
     bar-anchored menus' own top. */
  .bar .nav-links .nav-group > .nav-menu { top: calc(100% + 30px); padding: 22px 22px 22px 216px; gap: 6px 14px; }
  /* The hover bridge covers only the gap between the bar row and the bubble, never the other menu
     items, so moving sideways to the next section always reaches it at once. */
  .bar .nav-links .nav-group > .nav-menu::before { top: -30px; height: 30px; }
  .bar .nav-links :is(.nav-menu > a, .nav-col > a) { padding: 12px 14px; column-gap: 14px; row-gap: 3px; border-radius: 14px; }
  .bar .nav-links .nav-menu .nav-col > a.compact { padding: 6px 10px; }
  .bar .nav-links .nav-menu > .nav-more { margin-top: 10px; padding: 14px; border-radius: 14px; border-top: 0; position: relative; }
  .bar .nav-links .nav-menu > .nav-more::after { content: ""; position: absolute; left: 14px; right: 14px; top: -6px; border-top: var(--rule-w) solid var(--rule); }
  /* Every submenu icon moves on hover/focus (§0.2): the tile pops and wiggles, the glyph redraws
     itself (pathLength=1 is set by header.js, so one dash length fits every shape). castomAPI's
     Endpoints menu holds its links in columns, so the column links move too. */
  .bar .nav-links :is(.nav-menu > a, .nav-col > a):is(:hover, :focus-visible) .nav-ico { animation: hdr-ico .6s cubic-bezier(.3, 1.6, .5, 1); }
  .bar .nav-links :is(.nav-menu > a, .nav-col > a):is(:hover, :focus-visible) .nav-ico svg :is(path, circle, rect, line, polyline) { stroke-dasharray: 1; animation: hdr-draw .7s cubic-bezier(.4, 0, .2, 1); }
  .nav-group[open] .nav-art { animation: hdr-leap .7s cubic-bezier(.3, 1.2, .5, 1) .05s both; }
  .nav-group[open] :is(.nav-menu > a, .nav-col > a, .nav-col > h3) { animation: hdr-in .34s cubic-bezier(.2, .7, .3, 1) both; }
  .nav-menu[data-from="left"] { --dx: -26px; }
  .nav-menu[data-from="right"] { --dx: 26px; }
  .nav-group[open] .nav-menu > :nth-child(2) { animation-delay: .03s; }
  .nav-group[open] .nav-menu > :nth-child(3) { animation-delay: .06s; }
  .nav-group[open] .nav-menu > :nth-child(4) { animation-delay: .09s; }
  .nav-group[open] .nav-menu > :nth-child(n+5) { animation-delay: .12s; }
}
@keyframes hdr-in { from { opacity: 0; transform: translate(var(--dx, 0), 8px); } }
@keyframes hdr-leap {
  0% { opacity: 0; transform: translate(18%, 70%) rotate(14deg) scale(.7); }
  55% { opacity: 1; transform: translate(0, -10%) rotate(-5deg) scale(1.04); }
  78% { transform: translate(0, 2%) scale(1.03, .96); }
  100% { transform: none; }
}
@keyframes hdr-ico { 30% { transform: scale(1.18) rotate(-10deg); } 60% { transform: scale(.96) rotate(6deg); } }
@keyframes hdr-draw { from { stroke-dashoffset: 1; } }
@media (min-width: 1201px) and (prefers-reduced-motion: reduce) {
  .bubble, .bubble::before { transition: opacity .18s ease; }
  .nav-group[open] .nav-art, .nav-group[open] .nav-menu * { animation: none; }
}
[data-motion="reduce"] .bubble, [data-motion="reduce"] .bubble::before { transition: opacity .18s ease; }
[data-motion="reduce"] .nav-group[open] :is(.nav-art, .nav-menu *) { animation: none !important; }
@media (max-width: 1200px) { .nav-art, .bubble, .pill-glow { display: none; } }

@media (min-width: 1201px) {
  /* Three columns: logo left, menu centred on the page, tools + CTA right. Equal 1fr sides keep the
     menu on the true centre whatever the two sides weigh; minmax(max-content, …) means the tightest
     width sets it a little off centre rather than under the tools. The 40px gap (GAP_X in header.js)
     leaves room for a tool's hover lift and focus ring beside the menu. */
  .bar .bar-in.wrap { width: calc(100% - clamp(32px, 4vw, 72px)); max-width: none;
    display: grid; grid-template-columns: minmax(max-content, 1fr) auto minmax(max-content, 1fr); column-gap: 40px; }
  .bar .bar-in > .brand { grid-column: 1; justify-self: start; }
  .bar .bar-in .nav-links { grid-column: 2; grid-row: 1; margin: 0; }
  .bar .bar-in > .bar-end { grid-column: 3; grid-row: 1; justify-self: end; }
  .bar .nav-links :is(.nav-top, .nav-group > summary) { font-weight: 700; }
  .bar .bar-tools > * { display: inline-flex; align-items: center; }

  /* One family for play, accessibility and language: the same 37px ghost shape, the same ink, the
     same wash and lift on hover, circle-based 22px icons at one stroke weight, the flag a circle too. */
  .bar .bar-tools { border: 0; padding: 0; gap: 2px; background: none; }
  .bar .bar-tools .player-label { display: none; }
  .bar :is(.bar-tools .player-btn, .bar-tools .icon-btn, .lang-btn) {
    height: 37px; border: 0 !important; border-radius: 999px; background: transparent; color: var(--ink);
    transition: background-color .2s, transform .35s cubic-bezier(.34, 1.56, .64, 1); }
  .bar .bar-tools :is(.player-btn, .icon-btn) { width: 37px; padding: 0; justify-content: center; }
  .bar :is(.bar-tools .player-btn, .bar-tools .icon-btn, .lang-btn):is(:hover, :focus-visible, [aria-expanded="true"]) { background: var(--field); transform: scale(1.08); }
  .bar .bar-tools :is(.player-btn, .icon-btn) svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
  .bar .bar-tools .icon-btn svg circle[fill] { fill: currentColor; }
  .bar .bar-tools .player.is-active .player-btn { background: var(--fill); color: var(--on-fill); }
  .bar .lang-btn { padding: 0 10px 0 8px; gap: 6px; font-size: 14px; font-weight: 700; text-transform: uppercase; letter-spacing: .02em; }
  .bar .lang-btn .flag { width: 17px; height: 17px; margin: 0 2px; border-radius: 50%; box-shadow: 0 0 0 1.9px currentColor; }
  .bar .lang-btn > svg:last-child { width: 10px; height: 6px; }
  .bar .bar-end { gap: 4px; }

  /* Sub menu pill: the same technique as the header pill. The strip keeps its size; a background
     layer clipped to insets gathers around the pills (fading in as it does), and the row slides to
     the centre (transform). At rest the sub menu has no strip of its own. */
  .subnav { background: transparent; border-bottom-color: transparent; }
  .subnav > .wrap { position: relative; z-index: 1; }
  .subnav ul { transform: translateX(var(--sshift, 0px)); }
  .sub-glow { position: absolute; inset: 0; pointer-events: none; filter: drop-shadow(0 10px 20px rgb(11 12 16 / calc(var(--p, 0) * .16))); }
  .sub-pill { position: absolute; inset: 0; background: var(--paper-2); opacity: var(--p, 0); border-bottom: 0;
    clip-path: inset(var(--siy, 0px) var(--sixr, 0px) var(--siy, 0px) var(--sixl, 0px) round calc(var(--p, 0) * 999px)); }
}
/* Centred sub menu at every width. Auto margins on the end pills, not justify-content: centre, so an
   overflowing row still scrolls to its first pill instead of cutting it off on the left. */
.subnav ul > li:first-child { margin-inline-start: auto; }
.subnav ul > li:last-child { margin-inline-end: auto; }
@media (max-width: 1200px) { .sub-glow { display: none; } }

/* Sub menu arrows (header.js): shown only while the row overflows and only on the side that has more
   to show. The row fades out under each visible arrow so a cut-off pill reads as "more this way". */
.subnav > .wrap { position: relative; }
.sub-arrow { position: absolute; top: 50%; z-index: 2; display: inline-grid; place-items: center; width: 32px; height: 32px;
  margin-top: -16px; padding: 0; border: 0; border-radius: 50%; background: var(--paper); color: var(--ink);
  box-shadow: 0 4px 12px -4px rgb(11 12 16 / .35), 0 0 0 1px var(--rule); cursor: pointer; transition: transform .3s cubic-bezier(.34, 1.56, .64, 1); }
.sub-arrow[hidden] { display: none; }
.sub-arrow.prev { left: calc(var(--ul-l, 0px) + 4px); }
.sub-arrow.next { right: calc(var(--ul-r, 0px) + 4px); }
.sub-arrow svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; transition: transform .3s cubic-bezier(.34, 1.56, .64, 1); }
.sub-arrow:hover, .sub-arrow:focus-visible { transform: scale(1.1); }
.sub-arrow.prev:hover svg, .sub-arrow.prev:focus-visible svg { transform: translateX(-2px); }
.sub-arrow.next:hover svg, .sub-arrow.next:focus-visible svg { transform: translateX(2px); }
.subnav[data-more~="prev"] ul { --fade-l: 48px; }
.subnav[data-more~="next"] ul { --fade-r: 48px; }
.subnav[data-more] ul { scroll-behavior: smooth;
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-l, 0px), #000 calc(100% - var(--fade-r, 0px)), transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-l, 0px), #000 calc(100% - var(--fade-r, 0px)), transparent 100%); }
@media (prefers-reduced-motion: reduce) { .subnav[data-more] ul { scroll-behavior: auto; } }
[data-motion="reduce"] .subnav[data-more] ul { scroll-behavior: auto; }

/* Phones and tablets (the drawer, ≤ 1200px): no CTA in the bar (the drawer carries its own), and the
   bar slides away while the reader scrolls down, leaving only the sub menu pinned at the top;
   scrolling up brings it back (header.js). The tools stay in the bar as raw round icons, one family
   with the language button (§0.24, §0.26). */
@media (max-width: 1200px) {
  .bar-end .bar-cta { display: none; }
  .bar { transition: transform .32s cubic-bezier(.2, .7, .2, 1), background-color .25s ease; }
  .subnav { transition: top .32s cubic-bezier(.2, .7, .2, 1); }
  html.bar-away .bar { transform: translateY(-100%); }
  html.bar-away .subnav { top: 0; }
  .bar .bar-tools { border: 0; padding: 0; gap: 0; background: none; }
  .bar .bar-tools .player-label { display: none; }
  .bar .bar-tools :is(.player-btn, .icon-btn), .bar .lang-btn {
    height: 40px; border: 0 !important; border-radius: 999px; background: transparent; color: var(--ink); }
  .bar .bar-tools :is(.player-btn, .icon-btn) { width: 40px; padding: 0; justify-content: center; }
  .bar .bar-tools :is(.player-btn, .icon-btn):is(:hover, :focus-visible, [aria-expanded="true"]), .bar .lang-btn:is(:hover, :focus-visible, [aria-expanded="true"]) { background: var(--field); }
  .bar .bar-tools :is(.player-btn, .icon-btn) svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.9; }
  .bar .bar-tools .icon-btn svg circle[fill] { fill: currentColor; }
  .bar .bar-tools .player.is-active .player-btn { background: var(--fill); color: var(--on-fill); }
  .bar .lang-btn { padding: 0 8px; gap: 6px; font-size: 14px; font-weight: 700; text-transform: uppercase; }
  .bar .lang-btn .flag { width: 18px; height: 18px; border-radius: 50%; box-shadow: 0 0 0 1.9px currentColor; }
  .bar .bar-end { gap: 0; }
}
@media (max-width: 1200px) and (prefers-reduced-motion: reduce) { .bar, .subnav { transition: none; } }
[data-motion="reduce"] :is(.bar, .subnav) { transition: none; }

/* The narrowest phones: the footer's link lists stack in one column (§0.27). */
@media (max-width: 370px) { .foot-grid { grid-template-columns: 1fr; } }

/* The showcase layer: the 2026 redesign on the OutlineTone kit (09B), on top of site.css.
   Ported from castomui.com (Ghost) and fabls.io; the page anatomy is guidelines §4, the heroes §15.1.
   Bundled into dist/assets/site.css after site.css and before assets/showcase.d/*.css (tools/build.py
   copy_assets), so a page still makes one stylesheet request. No colour is typed here: every paint is
   a token site.css already redefines per theme, so light, dark and both contrast modes follow. */

/* ── art tokens: declared where --ink changes (§15.12), read by the inline drawings ─────────── */
:root, .sc, .sc-hero {
  --art-ink: var(--ink);
  --art-tone: var(--accent);
  --outlinetone-ink: var(--ink);
}
.kit-ink { display: block; width: 100%; height: auto; overflow: visible; }
.kit-ink .sk { stroke: var(--art-ink); }
.kit-ink .fk { fill: var(--art-ink); }
.kit-ink .fa { fill: var(--art-tone); }
outlinetone-art { --outlinetone-ink: var(--art-ink); }

/* ── the static hero (hx-intro): words left, drawing right; first paint is final layout ─────── */
.sc-hero { padding-block: clamp(20px, 4vw, 48px) clamp(24px, 4vw, 56px); }
.hx-intro {
  display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr);
  align-items: center; gap: clamp(20px, 4vw, 64px);
}
.hx-copy { min-width: 0; container-type: inline-size; }
.hx-eyebrow {
  font-size: var(--t-1); font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--accent-ink); margin: 0 0 14px;
}
.hx-eyebrow a { color: inherit; }
/* A display headline breaks only at its authored <br> (the headline rule). The build wraps each line
   in .hx-line and writes the longest line's length as .hx-fit-N (tools/build.py hero_lines); the size
   is the column width over that length, capped at the display size, so no line ever wraps by itself
   or leaves its column. Phones take the extra <br class="hx-m"> breaks and .hx-fitm-N. */
.hx-headline {
  --fit: 12; --fit-m: var(--fit);
  font-size: min(var(--t6), calc(100cqi / (var(--fit) * .53)));
  line-height: 1.04; letter-spacing: -.025em; font-weight: 800; margin: 0 0 20px;
  hyphens: none; overflow-wrap: normal; word-break: keep-all;
}
.hx-line { display: block; white-space: nowrap; }
.hx-headline em { font-style: normal; color: var(--accent-ink); }
.hx-headline br.hx-m { display: none; }
@media (max-width: 640px) {
  .hx-headline { font-size: min(var(--t5), calc(100cqi / (var(--fit-m) * .53))); }
  .hx-headline br.hx-m { display: initial; }
}
/* The accessibility panel's wide spacing (WCAG 1.4.12) adds letter- and word-spacing to every line,
   which the .53 em-per-character fit above does not know about: at the larger text sizes a hero line
   ran past a phone's edge. The fit takes the extra spacing into account instead. */
[data-spacing="wide"] .hx-headline { font-size: min(var(--t6), calc(100cqi / (var(--fit) * .64))); }
@media (max-width: 640px) {
  [data-spacing="wide"] .hx-headline { font-size: min(var(--t5), calc(100cqi / (var(--fit-m) * .64))); }
}
.hx-lede { font-size: var(--t1); line-height: 1.5; color: var(--ink-2); max-width: 60ch; margin: 0 0 20px; }
.hx-lede + .hx-lede, .hx-copy .lead { font-size: var(--t0); color: var(--ink-2); }
.hx-meta { color: var(--ink-3); font-size: var(--t-1); }
.hx-copy .hero-ctas { margin-top: 8px; }
.hx-scene { position: relative; min-width: 0; display: grid; place-items: center; }
.hx-blob {
  position: absolute; inset: 8% 6%; border-radius: 46% 54% 52% 48% / 55% 45% 55% 45%;
  background: var(--accent-soft);
}
.hx-art { position: relative; width: min(100%, 440px); }
/* The drawing draws itself in once, like the kit's own <outlinetone-art> (proposal 09): outlines,
   then the halftone. Decided before first paint, so nothing moves the layout (§15.1). */
@media (prefers-reduced-motion: no-preference) {
  :root:not([data-motion="reduce"]) .hx-art .sk { stroke-dasharray: 1400; stroke-dashoffset: 1400; animation: hx-draw 1.6s cubic-bezier(.55, 0, .3, 1) .15s forwards; }
  :root:not([data-motion="reduce"]) .hx-art .fk { opacity: 0; animation: hx-fade .5s ease .9s forwards; }
  :root:not([data-motion="reduce"]) .hx-art .fa { opacity: 0; animation: hx-fade .6s ease 1.2s forwards; }
  :root:not([data-motion="reduce"]) .hx-blob { animation: hx-breathe 9s ease-in-out infinite alternate; }
}
@keyframes hx-draw { to { stroke-dashoffset: 0; } }
@keyframes hx-fade { to { opacity: 1; } }
@keyframes hx-breathe { to { border-radius: 54% 46% 45% 55% / 45% 55% 45% 55%; scale: 1.04; } }
@media (max-width: 860px) {
  .hx-intro { grid-template-columns: 1fr; }
  .hx-scene { order: -1; }
  .hx-art { width: min(64%, 300px); }
}
[data-theme="contrast"] .hx-blob, [data-theme="contrast-dark"] .hx-blob { background: none; border: 2px solid var(--rule); }

/* ── the page after the hero ───────────────────────────────────────────────────────────────── */
.sc-body:empty { display: none; }
.sc-body > .section-head:first-child { margin-top: 0; }

/* ── chapters the redesigned pages compose (home, pricing, the top-nav pages) ─────────────── */
.sc-chapter { padding-block: clamp(40px, 6vw, 88px); }
.sc-chapter + .sc-chapter { padding-top: 0; }
.sc-head { max-width: 62ch; margin: 0 0 clamp(20px, 3vw, 36px); }
.sc-head h2 { font-size: var(--t4); line-height: 1.08; letter-spacing: -.02em; margin: 0 0 12px; }
.sc-head .line { display: block; }
.sc-head p { color: var(--ink-2); font-size: var(--t1); margin: 0; }
.sc-close { padding-block: clamp(32px, 5vw, 72px) clamp(56px, 8vw, 112px); }
outlinetone-cta-burst h2 { margin: 0 0 14px; }
outlinetone-cta-burst > a { font-weight: 700; }
outlinetone-bento, outlinetone-stat-tile { display: block; }

/* ── credits: one block at the very end of the page, in the footer (rule, §0.6) ────────────── */
.foot-credits { color: var(--ink-3); font-size: var(--t-1); margin: 18px 0 0; }
.foot-credits a { color: inherit; }

/* The credit line is the page's last line, so on a phone it would end under back-to-top and the chat
   button; room below it lets it scroll clear of them (guidelines §15.7). The footer's action row and
   the header are the shared chrome now, in assets/site.css (§0.12, §0.26). */
@media (max-width: 640px) { .foot-credits { margin-bottom: 96px; } }

/* The page bodies' former style="" attributes, moved here by the 2026 redesign because the CSP
   and the house rules forbid inline style (guidelines §15.12). Each class is named after a hash of
   its declaration, so one declaration is one rule however many pages used it. */
.il-08215bd { width:28px;height:28px;border-radius:50%;background:#DC4425;display:block;margin-bottom:12px; }
.il-31a2977 { width:28px;height:28px;border-radius:50%;background:#E0327F;display:block;margin-bottom:12px; }
.il-3a6c97e { font-size:var(--t1); }
.il-3d3eeb0 { width:28px;height:28px;border-radius:50%;background:#1863AE;display:block;margin-bottom:12px; }
.il-49fcd85 { margin-top:24px; }
.il-56f4356 { margin-top:12px; }
.il-5cde3a2 { list-style:none; }
.il-6476f32 { font-size:var(--t-1);margin-bottom:8px; }
.il-7b235bd { width:28px;height:28px;border-radius:50%;background:#F59E0B;display:block;margin-bottom:12px; }
.il-7e3a944 { font-weight:400; }
.il-7ed833a { text-decoration:none;color:inherit; }
.il-8015b53 { width:28px;height:28px;border-radius:50%;background:#16A34A;display:block;margin-bottom:12px; }
.il-8a77e5a { margin-top:8px; }
.il-8c92175 { margin-bottom:40px; }
.il-9162a24 { align-self:center; }
.il-9d9d1bb { padding-block:24px; }
.il-aad83ab { font-size:var(--t2); }
.il-b334919 { width:16px;height:16px;stroke:currentColor;stroke-width:1.8;fill:none; }
.il-b77ab9d { margin:18px 0 8px;display:flex;align-items:center;gap:12px;flex-wrap:wrap; }
.il-bb2789d { width:28px;height:28px;border-radius:50%;background:#E84520;display:block;margin-bottom:12px; }
.il-bded67f { width:28px;height:28px;border-radius:50%;background:#C80A00;display:block;margin-bottom:12px; }
.il-c071b4a { margin-bottom:28px; }
.il-c535ba3 { width:28px;height:28px;border-radius:50%;background:#D61F6C;display:block;margin-bottom:12px; }
.il-c98cb8e { font-size:var(--t-1); }
.il-d878359 { width:28px;height:28px;border-radius:50%;background:#2F4FC4;display:block;margin-bottom:12px; }
.il-d9f1497 { margin-top:40px; }
.il-da12f28 { margin-bottom:12px; }
.il-df65e67 { width:28px;height:28px;border-radius:50%;background:#0052B6;display:block;margin-bottom:12px; }
.il-f3ee0d3 { width:28px;height:28px;border-radius:50%;background:#84CC16;display:block;margin-bottom:12px; }

/* The generated pages' (templates/endpoint|group|service.html) former style="" attributes, moved here
   by the 2026 redesign: the CSP and the house rules forbid inline style (guidelines §15.12). */
.t-mb14 { margin-bottom: 14px; }
.t-mb32 { margin-bottom: 32px; }
.t-mt12 { margin-top: 12px; }
.t-mt14 { margin-top: 14px; }
.t-mtwide { margin-top: clamp(32px, 5vw, 56px); }
.t-small { font-size: var(--t-1); }

.hx-fit-4 { --fit: 4; } .hx-fitm-4 { --fit-m: 4; }
.hx-fit-5 { --fit: 5; } .hx-fitm-5 { --fit-m: 5; }
.hx-fit-6 { --fit: 6; } .hx-fitm-6 { --fit-m: 6; }
.hx-fit-7 { --fit: 7; } .hx-fitm-7 { --fit-m: 7; }
.hx-fit-8 { --fit: 8; } .hx-fitm-8 { --fit-m: 8; }
.hx-fit-9 { --fit: 9; } .hx-fitm-9 { --fit-m: 9; }
.hx-fit-10 { --fit: 10; } .hx-fitm-10 { --fit-m: 10; }
.hx-fit-11 { --fit: 11; } .hx-fitm-11 { --fit-m: 11; }
.hx-fit-12 { --fit: 12; } .hx-fitm-12 { --fit-m: 12; }
.hx-fit-13 { --fit: 13; } .hx-fitm-13 { --fit-m: 13; }
.hx-fit-14 { --fit: 14; } .hx-fitm-14 { --fit-m: 14; }
.hx-fit-15 { --fit: 15; } .hx-fitm-15 { --fit-m: 15; }
.hx-fit-16 { --fit: 16; } .hx-fitm-16 { --fit-m: 16; }
.hx-fit-17 { --fit: 17; } .hx-fitm-17 { --fit-m: 17; }
.hx-fit-18 { --fit: 18; } .hx-fitm-18 { --fit-m: 18; }
.hx-fit-19 { --fit: 19; } .hx-fitm-19 { --fit-m: 19; }
.hx-fit-20 { --fit: 20; } .hx-fitm-20 { --fit-m: 20; }
.hx-fit-21 { --fit: 21; } .hx-fitm-21 { --fit-m: 21; }
.hx-fit-22 { --fit: 22; } .hx-fitm-22 { --fit-m: 22; }
.hx-fit-23 { --fit: 23; } .hx-fitm-23 { --fit-m: 23; }
.hx-fit-24 { --fit: 24; } .hx-fitm-24 { --fit-m: 24; }
.hx-fit-25 { --fit: 25; } .hx-fitm-25 { --fit-m: 25; }
.hx-fit-26 { --fit: 26; } .hx-fitm-26 { --fit-m: 26; }
.hx-fit-27 { --fit: 27; } .hx-fitm-27 { --fit-m: 27; }
.hx-fit-28 { --fit: 28; } .hx-fitm-28 { --fit-m: 28; }
.hx-fit-29 { --fit: 29; } .hx-fitm-29 { --fit-m: 29; }
.hx-fit-30 { --fit: 30; } .hx-fitm-30 { --fit-m: 30; }
.hx-fit-31 { --fit: 31; } .hx-fitm-31 { --fit-m: 31; }
.hx-fit-32 { --fit: 32; } .hx-fitm-32 { --fit-m: 32; }
.hx-fit-33 { --fit: 33; } .hx-fitm-33 { --fit-m: 33; }
.hx-fit-34 { --fit: 34; } .hx-fitm-34 { --fit-m: 34; }
.hx-fit-35 { --fit: 35; } .hx-fitm-35 { --fit-m: 35; }
.hx-fit-36 { --fit: 36; } .hx-fitm-36 { --fit-m: 36; }
.hx-fit-37 { --fit: 37; } .hx-fitm-37 { --fit-m: 37; }
.hx-fit-38 { --fit: 38; } .hx-fitm-38 { --fit-m: 38; }
.hx-fit-39 { --fit: 39; } .hx-fitm-39 { --fit-m: 39; }
.hx-fit-40 { --fit: 40; } .hx-fitm-40 { --fit-m: 40; }
.hx-fit-41 { --fit: 41; } .hx-fitm-41 { --fit-m: 41; }
.hx-fit-42 { --fit: 42; } .hx-fitm-42 { --fit-m: 42; }
.hx-fit-43 { --fit: 43; } .hx-fitm-43 { --fit-m: 43; }
.hx-fit-44 { --fit: 44; } .hx-fitm-44 { --fit-m: 44; }
.hx-fit-45 { --fit: 45; } .hx-fitm-45 { --fit-m: 45; }
.hx-fit-46 { --fit: 46; } .hx-fitm-46 { --fit-m: 46; }
.hx-fit-47 { --fit: 47; } .hx-fitm-47 { --fit-m: 47; }
.hx-fit-48 { --fit: 48; } .hx-fitm-48 { --fit-m: 48; }
.hx-fit-49 { --fit: 49; } .hx-fitm-49 { --fit-m: 49; }
.hx-fit-50 { --fit: 50; } .hx-fitm-50 { --fit-m: 50; }
.hx-fit-51 { --fit: 51; } .hx-fitm-51 { --fit-m: 51; }
.hx-fit-52 { --fit: 52; } .hx-fitm-52 { --fit-m: 52; }
.hx-fit-53 { --fit: 53; } .hx-fitm-53 { --fit-m: 53; }
.hx-fit-54 { --fit: 54; } .hx-fitm-54 { --fit-m: 54; }
.hx-fit-55 { --fit: 55; } .hx-fitm-55 { --fit-m: 55; }
.hx-fit-56 { --fit: 56; } .hx-fitm-56 { --fit-m: 56; }
.hx-fit-57 { --fit: 57; } .hx-fitm-57 { --fit-m: 57; }
.hx-fit-58 { --fit: 58; } .hx-fitm-58 { --fit-m: 58; }
.hx-fit-59 { --fit: 59; } .hx-fitm-59 { --fit-m: 59; }
.hx-fit-60 { --fit: 60; } .hx-fitm-60 { --fit-m: 60; }
.hx-fit-61 { --fit: 61; } .hx-fitm-61 { --fit-m: 61; }
.hx-fit-62 { --fit: 62; } .hx-fitm-62 { --fit-m: 62; }
.hx-fit-63 { --fit: 63; } .hx-fitm-63 { --fit-m: 63; }
.hx-fit-64 { --fit: 64; } .hx-fitm-64 { --fit-m: 64; }
.hx-fit-65 { --fit: 65; } .hx-fitm-65 { --fit-m: 65; }
.hx-fit-66 { --fit: 66; } .hx-fitm-66 { --fit-m: 66; }
.hx-fit-67 { --fit: 67; } .hx-fitm-67 { --fit-m: 67; }
.hx-fit-68 { --fit: 68; } .hx-fitm-68 { --fit-m: 68; }
.hx-fit-69 { --fit: 69; } .hx-fitm-69 { --fit-m: 69; }
.hx-fit-70 { --fit: 70; } .hx-fitm-70 { --fit-m: 70; }
.hx-fit-71 { --fit: 71; } .hx-fitm-71 { --fit-m: 71; }
.hx-fit-72 { --fit: 72; } .hx-fitm-72 { --fit-m: 72; }
.hx-fit-73 { --fit: 73; } .hx-fitm-73 { --fit-m: 73; }
.hx-fit-74 { --fit: 74; } .hx-fitm-74 { --fit-m: 74; }
.hx-fit-75 { --fit: 75; } .hx-fitm-75 { --fit-m: 75; }
.hx-fit-76 { --fit: 76; } .hx-fitm-76 { --fit-m: 76; }
.hx-fit-77 { --fit: 77; } .hx-fitm-77 { --fit-m: 77; }
.hx-fit-78 { --fit: 78; } .hx-fitm-78 { --fit-m: 78; }
.hx-fit-79 { --fit: 79; } .hx-fitm-79 { --fit-m: 79; }
.hx-fit-80 { --fit: 80; } .hx-fitm-80 { --fit-m: 80; }
.hx-fit-81 { --fit: 81; } .hx-fitm-81 { --fit-m: 81; }
.hx-fit-82 { --fit: 82; } .hx-fitm-82 { --fit-m: 82; }
.hx-fit-83 { --fit: 83; } .hx-fitm-83 { --fit-m: 83; }
.hx-fit-84 { --fit: 84; } .hx-fitm-84 { --fit-m: 84; }
.hx-fit-85 { --fit: 85; } .hx-fitm-85 { --fit-m: 85; }
.hx-fit-86 { --fit: 86; } .hx-fitm-86 { --fit-m: 86; }
.hx-fit-87 { --fit: 87; } .hx-fitm-87 { --fit-m: 87; }
.hx-fit-88 { --fit: 88; } .hx-fitm-88 { --fit-m: 88; }
.hx-fit-89 { --fit: 89; } .hx-fitm-89 { --fit-m: 89; }
.hx-fit-90 { --fit: 90; } .hx-fitm-90 { --fit-m: 90; }
