/* page.css — the stylesheet for every Fabric-View-rendered public page.
 *
 * Served at /assets/page.css. That path needs no new route: `path_to_pin_segments` drops the last
 * segment's extension, so the request resolves the shell pin `…:public:shell:web:page` — inside
 * `is_public_servable`, and `content_type_for` already maps `css` → text/css.
 *
 * WHY THIS IS A FILE AND NOT AN INLINE <style>
 *
 * The Markdown renderer inlines its stylesheet, which forces `style-src 'unsafe-inline'` on the
 * page CSP — and `'unsafe-inline'` in `style-src` also permits `style-src-attr` by fallback, so an
 * injected `style="…"` attribute would take effect. CSS-only injection is not harmless: it can
 * overlay or reposition content, and exfiltrate through attribute selectors plus `url()`. An
 * external stylesheet lets the FV pages run under `style-src 'self'` (see `style_src_for` in
 * `crates/http-edge/src/main.rs`).
 *
 * That is only sound because the serializer emits CLASS TOKENS ONLY. `crates/http-edge/src/fvhtml.rs`
 * admits style values from closed allowlists and DROPS anything unrecognized rather than falling back
 * to an inline style — so every selector below is one the serializer can actually emit, and a style
 * value with no rule here renders unstyled rather than inline. Adding a token to `fvhtml.rs` without
 * adding its rule here is the failure mode to watch for; the golden-HTML tests in that file assert the
 * class names, so a rename shows up there.
 *
 * TOKENS ARE DUPLICATED HERE ON PURPOSE. The app shell's design tokens live in its bootstrap
 * `index.html` and are loaded by the shell's own JS-driven stylesheet chain, which an anonymous page
 * does not run. Rather than couple a static page's rendering to the shell's boot sequence, the seven
 * color tokens (identical values, light + dark) and the SDL §7 scales are restated below. They must be
 * kept in step with `shell/web/index.html` by review — there is no gate.
 *
 * NO INTERNAL BRAND FORMS ANYWHERE IN THIS FILE (CLAUDE.md §Naming) — not even in a comment. A
 * stylesheet is served verbatim to anyone who requests it, so its comments are as customer-facing as
 * its rules; the internal system names (and the glyph they use) belong to none of them. The `fv-`
 * class prefix is brand-free for the same reason. Enforced by
 * `the_stylesheet_carries_no_customer_facing_internal_branding` in `fvhtml.rs`, which is why this
 * paragraph names no example.
 */

/* The seven color tokens, mirrored from `shell/web/index.html` (light, then dark). `color-scheme`
   tells the browser to theme its own form controls and scrollbars to match, which is what keeps a
   dark-mode page from flashing a white scrollbar. */
:root {
  color-scheme: light dark;
  --fv-bg-base: #FDFCFC;
  --fv-bg-primary: #FFFFFF;
  --fv-bg-secondary: #FAF9F8;
  --fv-text-primary: #34312E;
  --fv-text-secondary: #726E68;
  --fv-accent: #007C59;
  --fv-border: #DCD8D3;
  /* Inter first, then the platform stack — matching the shell's --fabric-font-sans. Inter is not
     WEBFONT-LOADED here: a page CSP of `default-src 'none'` admits no font fetch, and a blocking
     font request on the acquisition surface would cost more than the typeface is worth. It applies
     for the readers who have it installed; everyone else gets their platform's UI face. */
  --fv-font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial,
    sans-serif;
  --fv-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  /* Radius scale (SDL §7). */
  --fv-radius-none: 0;
  --fv-radius-sm: 6px;
  --fv-radius-md: 10px;
  --fv-radius-lg: 14px;
  --fv-radius-xl: 20px;
  --fv-radius-2xl: 28px;
  --fv-radius-full: 9999px;
  /* The reading measure. One value, referenced by every layout, so the layouts differ in rhythm and
     emphasis rather than each inventing its own line length. */
  --fv-measure: 46rem;
}

@media (prefers-color-scheme: dark) {
  :root {
    --fv-bg-base: #121110;
    --fv-bg-primary: #1A1918;
    --fv-bg-secondary: #1A1918;
    --fv-text-primary: #EBE9E6;
    --fv-text-secondary: #B8B3AE;
    --fv-accent: #2AC592;
    --fv-border: #514E49;
  }
}

/* ---- Document frame ---------------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--fv-bg-base);
  color: var(--fv-text-primary);
  font-family: var(--fv-font-sans);
  font-size: 1rem;
  line-height: 1.625;
  /* Both properties: the standard one, and the WebKit-prefixed spelling Safari still needs. Without
     it iOS Safari inflates text in landscape, which reflows the pricing table mid-scroll. */
  text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
}

/* `<main class="fv-page">` — the serializer's document wrapper. Bottom padding is generous because
   the footer is the last thing on every page and a cramped one reads as a truncated document. */
.fv-page {
  max-width: var(--fv-measure);
  margin: 0 auto;
  padding: 32px 20px 96px;
}

/* ---- Layout primitives (§10 container props) --------------------------------------------- */

/* `min-width: 0` is load-bearing, not defensive tidying. A flex item's default `min-width: auto`
   refuses to shrink below its content's intrinsic width, so one long unbroken token (a URL in a
   legal document, a support address) widens its row past the viewport and the whole page scrolls
   sideways on a phone. */
.fv-container,
.fv-col,
.fv-row,
.fv-grid,
.fv-stack {
  min-width: 0;
}

.fv-container,
.fv-col,
.fv-row {
  display: flex;
}

.fv-col {
  flex-direction: column;
}

.fv-row {
  flex-direction: row;
  align-items: center;
}

.fv-wrap {
  flex-wrap: wrap;
}

/* `stack` overlays its children — a hero with a background layer behind its text. A single-cell
   grid rather than absolute positioning, so the stack still sizes to its tallest child instead of
   collapsing to zero height. */
.fv-stack {
  display: grid;
  grid-template-areas: "fv-stack";
}

.fv-stack > * {
  grid-area: fv-stack;
}

.fv-grid {
  display: grid;
  grid-template-columns: repeat(var(--fv-cols, 1), minmax(0, 1fr));
}

.fv-cols-1 { --fv-cols: 1 }
.fv-cols-2 { --fv-cols: 2 }
.fv-cols-3 { --fv-cols: 3 }
.fv-cols-4 { --fv-cols: 4 }
.fv-cols-5 { --fv-cols: 5 }
.fv-cols-6 { --fv-cols: 6 }

/* Below the phone breakpoint every multi-column grid collapses to one column. A 3-up plan grid at
   360px gives each card ~100px, which truncates the price. Single-column is the only honest answer
   and it is why the plans grid needs no per-page media query. */
@media (max-width: 40rem) {
  .fv-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

.fv-inline {
  display: inline-flex;
  align-items: baseline;
}

/* align (cross axis) / justify (main axis) — the closed sets ALIGN_VALUES / JUSTIFY_VALUES. */
.fv-items-start { align-items: flex-start }
.fv-items-center { align-items: center }
.fv-items-end { align-items: flex-end }
.fv-items-stretch { align-items: stretch }
.fv-items-baseline { align-items: baseline }
.fv-justify-start { justify-content: flex-start }
.fv-justify-center { justify-content: center }
.fv-justify-end { justify-content: flex-end }
.fv-justify-between { justify-content: space-between }
.fv-justify-around { justify-content: space-around }
.fv-justify-evenly { justify-content: space-evenly }

/* The flex claim (§20.7 `grow`/`fixed`). `fv-shrink` mirrors the web shell's flex-shrink parity fix:
   a truncating caption beside a growing title must be allowed to shrink, or it starves the title to
   zero width (the Android Row defect, `android-row-caption-starves-weighted-title`). */
.fv-grow { flex: 1 1 auto }
.fv-shrink { flex: 0 1 auto; min-width: 0 }
.fv-nogrow { flex: 0 0 auto }
.fv-fixed { flex: 0 0 auto }

.fv-spacer {
  flex: 0 0 auto;
}

.fv-divider {
  border: none;
  border-top: 1px solid var(--fv-border);
  margin: 32px 0;
  width: 100%;
}

/* ---- Typography (§13 text variants) ------------------------------------------------------ */

/* Every text node carries `.fv-text` plus `.fv-text-{variant}`. The margin reset is on the base
   class so a variant rule only states the spacing it wants — the browser's default heading margins
   would otherwise fight the container `gap` that actually spaces this layout. */
.fv-text {
  margin: 0;
  min-width: 0;
}

.fv-text-title {
  font-size: 1.875rem;
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.02em;
}

.fv-text-subtitle {
  font-size: 1.25rem;
  font-weight: 400;
  line-height: 1.5;
  color: var(--fv-text-secondary);
}

.fv-text-heading {
  font-size: 1.125rem;
  font-weight: 600;
  line-height: 1.25;
}

.fv-text-label {
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.25;
  text-transform: none;
}

.fv-text-body {
  font-size: 1rem;
  line-height: 1.625;
}

.fv-text-caption {
  font-size: 0.875rem;
  line-height: 1.5;
}

.fv-text-micro {
  font-size: 0.75rem;
  line-height: 1.5;
}

.fv-text-code,
.fv-mono {
  font-family: var(--fv-font-mono);
  font-size: 0.875rem;
}

.fv-weight-regular { font-weight: 400 }
.fv-weight-medium { font-weight: 500 }
.fv-weight-semibold { font-weight: 600 }
.fv-weight-bold { font-weight: 700 }

.fv-align-start { text-align: start }
.fv-align-center { text-align: center }
.fv-align-end { text-align: end }

.fv-strike { text-decoration: line-through }
.fv-italic { font-style: italic }
.fv-muted { color: var(--fv-text-secondary) }
.fv-selected { color: var(--fv-accent) }

/* Line clamp (§13 `lines`, and `style.truncate` → fv-lines-1). `-webkit-line-clamp` is the only
   cross-browser clamp; it is implemented everywhere including Firefox despite the prefix. */
.fv-lines-1,
.fv-lines-2,
.fv-lines-3,
.fv-lines-4,
.fv-lines-5,
.fv-lines-6 {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.fv-lines-1 { -webkit-line-clamp: 1 }
.fv-lines-2 { -webkit-line-clamp: 2 }
.fv-lines-3 { -webkit-line-clamp: 3 }
.fv-lines-4 { -webkit-line-clamp: 4 }
.fv-lines-5 { -webkit-line-clamp: 5 }
.fv-lines-6 { -webkit-line-clamp: 6 }

/* Whitespace keys (§20.7 `wrap`). */
.fv-pre { white-space: pre }
.fv-pre-line { white-space: pre-line }
.fv-nowrap { white-space: nowrap }

/* Screen-reader-only text — the `a11y.hint` target the serializer emits with a generated
   `fv-hint-{n}` id. Clipped rather than `display:none`, which would remove it from the a11y tree
   and defeat the `aria-describedby` that points at it. */
.fv-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---- Color / surface tokens (§20.7 background, color, radius, border, shadow) ------------- */

.fv-bg-base { background: var(--fv-bg-base) }
.fv-bg-primary { background: var(--fv-bg-primary) }
.fv-bg-secondary { background: var(--fv-bg-secondary) }
.fv-bg-accent { background: var(--fv-accent); color: #fff }
.fv-bg-none { background: transparent }

.fv-fg-primary { color: var(--fv-text-primary) }
.fv-fg-secondary { color: var(--fv-text-secondary) }
.fv-fg-text-primary { color: var(--fv-text-primary) }
.fv-fg-text-secondary { color: var(--fv-text-secondary) }
.fv-fg-accent { color: var(--fv-accent) }

.fv-radius-none { border-radius: var(--fv-radius-none) }
.fv-radius-sm { border-radius: var(--fv-radius-sm) }
.fv-radius-md { border-radius: var(--fv-radius-md) }
.fv-radius-lg { border-radius: var(--fv-radius-lg) }
.fv-radius-xl { border-radius: var(--fv-radius-xl) }
.fv-radius-2xl { border-radius: var(--fv-radius-2xl) }
.fv-radius-full { border-radius: var(--fv-radius-full) }

.fv-border-none { border: none }
.fv-border-all { border: 1px solid var(--fv-border) }
.fv-border-top { border-top: 1px solid var(--fv-border) }
.fv-border-bottom { border-bottom: 1px solid var(--fv-border) }
.fv-border-left { border-left: 3px solid var(--fv-border) }
.fv-border-right { border-right: 1px solid var(--fv-border) }

/* Shadows are kept subtle and few: a static document has no elevation hierarchy to express, only
   the occasional lifted card on the landing page. */
.fv-shadow-none { box-shadow: none }
.fv-shadow-sm { box-shadow: 0 1px 2px rgb(0 0 0 / 6%) }
.fv-shadow-md { box-shadow: 0 2px 8px rgb(0 0 0 / 8%) }
.fv-shadow-lg { box-shadow: 0 8px 24px rgb(0 0 0 / 10%) }

/* ---- Links and buttons ------------------------------------------------------------------- */

.fv-link {
  color: var(--fv-accent);
  text-decoration: none;
}

.fv-link:hover,
.fv-link:focus-visible {
  text-decoration: underline;
}

/* A visible focus ring on every focusable thing. The anonymous pages are the surface most likely to
   be read by keyboard and assistive tech, and `outline: none` with no replacement is the single most
   common accessibility regression in a hand-written stylesheet. */
.fv-link:focus-visible,
.fv-disclosure > summary:focus-visible {
  outline: 2px solid var(--fv-accent);
  outline-offset: 2px;
  border-radius: var(--fv-radius-sm);
}

/* Link `variant`/`size` (fv-btn-*) — the call-to-action forms the landing and pricing pages use. */
.fv-btn-primary,
.fv-btn-secondary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 12px 20px;
  border-radius: var(--fv-radius-full);
  font-weight: 600;
  text-decoration: none;
  /* No `transition`: the reduced-motion rule at the bottom would have to override it, and a static
     page's buttons gain nothing from an animated hover. */
}

.fv-btn-primary {
  background: var(--fv-accent);
  color: #fff;
}

.fv-btn-primary:hover,
.fv-btn-primary:focus-visible {
  /* A filled button must not underline on hover — the underline reads as a rendering fault on a
     solid pill. Darkening the surface is the affordance instead. */
  text-decoration: none;
  filter: brightness(0.93);
}

.fv-btn-secondary {
  background: transparent;
  color: var(--fv-text-primary);
  border: 1px solid var(--fv-border);
}

.fv-btn-secondary:hover,
.fv-btn-secondary:focus-visible {
  text-decoration: none;
  border-color: var(--fv-accent);
  color: var(--fv-accent);
}

.fv-btn-sm { padding: 8px 14px; font-size: 0.875rem }
.fv-btn-md { padding: 12px 20px }
.fv-btn-lg { padding: 16px 28px; font-size: 1.125rem }

/* ---- Images ------------------------------------------------------------------------------ */

.fv-image {
  max-width: 100%;
  height: auto;
  display: block;
}

.fv-fit-cover { object-fit: cover }
.fv-fit-contain { object-fit: contain }
.fv-fit-fill { object-fit: fill }

.fv-shape-circle { border-radius: var(--fv-radius-full) }
.fv-shape-rounded { border-radius: var(--fv-radius-md) }
.fv-shape-square { border-radius: 0 }

/* ---- Disclosure -------------------------------------------------------------------------- */

.fv-disclosure {
  border: 1px solid var(--fv-border);
  border-radius: var(--fv-radius-md);
  padding: 12px 16px;
}

.fv-disclosure > summary {
  cursor: pointer;
  font-weight: 600;
  /* `list-style` for standards browsers; `::-webkit-details-marker` for Safari, which ignores it. */
  list-style: none;
}

.fv-disclosure > summary::-webkit-details-marker {
  display: none;
}

.fv-disclosure > summary::after {
  content: "+";
  float: right;
  color: var(--fv-text-secondary);
}

.fv-disclosure[open] > summary::after {
  content: "\2212"; /* MINUS SIGN — visually matched to the "+" it replaces, unlike a hyphen. */
}

/* ---- Prose (the `native` markdown bridge) ------------------------------------------------ */

/* `.fv-prose` wraps the output of `legal::render_markdown_body`, so its descendants are plain
   Markdown-generated tags with no classes. These are the only element selectors in this file, and
   they are scoped under `.fv-prose` so they cannot reach the composed node tree around them. */
.fv-prose > :first-child {
  margin-top: 0;
}

.fv-prose > :last-child {
  margin-bottom: 0;
}

.fv-prose h1 {
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.25;
  margin: 40px 0 12px;
}

/* A body `##` is the document's main sectioning level — the page's own H1 is composed above the
   prose, so the rule head here is on h2 rather than h1. */
.fv-prose h2 {
  font-size: 1.25rem;
  font-weight: 600;
  line-height: 1.3;
  margin: 40px 0 12px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--fv-border);
}

.fv-prose h3 {
  font-size: 1.0625rem;
  font-weight: 600;
  margin: 28px 0 8px;
}

.fv-prose h4,
.fv-prose h5,
.fv-prose h6 {
  font-size: 1rem;
  font-weight: 600;
  margin: 24px 0 8px;
}

.fv-prose p {
  margin: 0 0 16px;
}

.fv-prose ul,
.fv-prose ol {
  margin: 0 0 16px;
  padding-left: 24px;
}

.fv-prose li {
  margin: 0 0 6px;
}

.fv-prose a {
  color: var(--fv-accent);
  text-decoration: none;
  /* Long URLs and support addresses appear as link text in the legal documents. Without this a
     single unbreakable token overflows the measure and scrolls the page sideways on a phone. */
  overflow-wrap: anywhere;
}

.fv-prose a:hover,
.fv-prose a:focus-visible {
  text-decoration: underline;
}

.fv-prose strong {
  font-weight: 600;
}

.fv-prose code {
  font-family: var(--fv-font-mono);
  font-size: 0.875em;
  background: var(--fv-bg-secondary);
  border: 1px solid var(--fv-border);
  border-radius: var(--fv-radius-sm);
  padding: 1px 5px;
}

.fv-prose pre {
  background: var(--fv-bg-secondary);
  border: 1px solid var(--fv-border);
  border-radius: var(--fv-radius-md);
  padding: 12px 16px;
  overflow-x: auto;
}

.fv-prose pre code {
  background: none;
  border: none;
  padding: 0;
}

.fv-prose blockquote {
  margin: 16px 0;
  padding: 8px 16px;
  border-left: 3px solid var(--fv-border);
  color: var(--fv-text-secondary);
}

/* The pricing and comparison tables. `display: block` + `overflow-x: auto` on the wrapper would be
   better, but the Markdown renderer emits a bare `<table>` with nothing to wrap it — so the table
   itself scrolls, which is why `table-layout` stays auto and the cells are allowed to wrap. */
.fv-prose table {
  border-collapse: collapse;
  width: 100%;
  margin: 16px 0;
  font-size: 0.9375rem;
  display: block;
  overflow-x: auto;
}

.fv-prose th,
.fv-prose td {
  border: 1px solid var(--fv-border);
  padding: 8px 10px;
  text-align: left;
  vertical-align: top;
}

.fv-prose th {
  background: var(--fv-bg-secondary);
  font-weight: 600;
}

.fv-prose hr {
  border: none;
  border-top: 1px solid var(--fv-border);
  margin: 32px 0;
}

.fv-prose img {
  max-width: 100%;
  height: auto;
}

/* ---- Presentational roles (§20.7 `style.role`) ------------------------------------------- */

/* These are the composition lambda's own vocabulary — `tlnk_root/fabric/site/page/render.star`
   sets each one. A role with no rule here renders unstyled, so this list and that lambda's roles
   must move together. */

/* The bypass link (WCAG 2.4.1, Level A) — first in the tab order, hidden until it has focus.
   Clipped exactly like `.fv-sr-only` rather than `display:none`, which would take it out of the tab
   order and so remove the very affordance it provides. On focus it becomes a real, visible target
   pinned to the top-left, because a keyboard user has to SEE where the focus went. */
.fv-role-skip-link {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.fv-role-skip-link:focus,
.fv-role-skip-link:focus-visible {
  position: fixed;
  top: 8px;
  left: 8px;
  z-index: 10;
  width: auto;
  height: auto;
  margin: 0;
  padding: 8px 16px;
  overflow: visible;
  clip-path: none;
  background: var(--fv-bg-base);
  color: var(--fv-accent);
  border: 1px solid var(--fv-accent);
  border-radius: var(--fv-radius-sm);
  font-size: 0.9375rem;
  text-decoration: none;
}

.fv-role-site-header {
  padding-bottom: 16px;
  margin-bottom: 8px;
  border-bottom: 1px solid var(--fv-border);
}

.fv-role-wordmark {
  font-size: 1.125rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--fv-text-primary);
  text-decoration: none;
}

.fv-role-wordmark:hover,
.fv-role-wordmark:focus-visible {
  color: var(--fv-accent);
  text-decoration: none;
}

.fv-role-nav-link {
  font-size: 0.9375rem;
  color: var(--fv-text-secondary);
  text-decoration: none;
}

.fv-role-nav-link:hover,
.fv-role-nav-link:focus-visible {
  color: var(--fv-accent);
  text-decoration: none;
}

.fv-role-breadcrumbs {
  font-size: 0.875rem;
  color: var(--fv-text-secondary);
}

/* The separator is generated, not authored: putting "/" in the node tree would make it selectable
   text that a screen reader announces between every crumb. */
.fv-role-breadcrumbs > * + *::before {
  content: "/";
  margin-right: 8px;
  color: var(--fv-border);
}

.fv-role-article {
  /* The article's own children are spaced by its container gap; this only guarantees the block
     never collapses to zero height on a page whose body failed to render. */
  min-height: 1px;
}

.fv-role-pager {
  font-size: 0.9375rem;
}

/* The derived index listing (`_derived_index` in the composition lambda). Its column layout and gaps
   come from tokens in the node tree; these rules state only what a token cannot — the group heading's
   typography and the link's block presentation, which is what makes a list of titles read as an index
   rather than as a paragraph of links. */
.fv-role-index {
  margin-top: 8px;
}

/* A section heading inside the listing is a <h3> (the `heading` text variant). It sits tighter to its
   own links than the default heading rhythm, so the group reads as one unit. */
.fv-role-index-group > .fv-text-heading {
  margin: 0;
  font-size: 1.125rem;
  letter-spacing: -0.01em;
}

.fv-role-index-link {
  /* Block, so the whole row is the target — an index is navigated by pointing at a title, not at the
     few characters of it that happen to be under the cursor. */
  display: block;
  font-weight: 500;
  text-decoration: none;
}

.fv-role-index-link:hover {
  text-decoration: underline;
}

/* The footer declares its own top border and padding in the node tree (`style.border: "top"`,
   `padding.top`), so this rule states only what a token cannot: the large separating margin and the
   footer's typography. */
.fv-role-site-footer {
  margin-top: 64px;
  font-size: 0.875rem;
  color: var(--fv-text-secondary);
}

/* A footer column's caption. `label` is deliberately NOT a heading variant (§13), so it carries no
   outline weight and needs its own emphasis here to read as a column head rather than a first link. */
.fv-role-footer-heading {
  font-weight: 600;
  color: var(--fv-text-primary);
  margin-bottom: 4px;
}

/* The columns' own links sit tighter than body rhythm — a stacked list of destinations, not prose. */
.fv-role-footer-group > .fv-link {
  display: block;
  line-height: 1.9;
}

/* ---- Header wordmark image --------------------------------------------------------------- */

/* The `logo.svg` wordmark. Height only: the asset carries a viewBox, so constraining one axis keeps
   its aspect ratio without this file having to know its intrinsic size. `display: block` kills the
   inline-image baseline gap that would otherwise push the header's bottom border down by ~4px.

   The asset is scheme-aware INTERNALLY (its own `prefers-color-scheme` query) because an <img> is a
   separate document that inherits no color from this stylesheet — so there is deliberately no filter
   or opacity trick here trying to recolor it from outside. */
.fv-role-wordmark-logo {
  height: 26px;
  width: auto;
  display: block;
}

@media (max-width: 40rem) {
  .fv-role-wordmark-logo {
    height: 22px;
  }
}

/* ---- Authored block roles (the `:::` directive vocabulary) -------------------------------- */

/* `:::hero` — the tinted opening panel. Its surface, radius and padding come from tokens in the node
   tree (`background: secondary`, `radius: xl`, `padding: xl` — bare token names; the serializer adds
   the `fv-bg-` family prefix), so this rule states only what a token cannot.

   THE DARK-MODE RULE IS LOAD-BEARING, not a refinement. In dark, `--fv-bg-secondary` (#1A1918) and
   `--fv-bg-primary` are the same value and both sit within a shade of `--fv-bg-base` (#121110) — so
   the tinted panel that carries the whole hero would be invisible, and the page would silently
   render as if the block were missing. A hairline border plus a lift off the base gives it an edge in
   both schemes rather than relying on fill contrast that only exists in one. */
.fv-role-hero-panel {
  border: 1px solid var(--fv-border);
}

@media (prefers-color-scheme: dark) {
  .fv-role-hero-panel {
    /* Slightly LIGHTER than bg-base, which is how elevation reads in a dark scheme (in light it
       reads as a recess). One value, not a new token: this is the only surface on the page that
       needs to separate from the base without becoming a card. */
    background: #201F1D;
  }
}

/* The hero's lead paragraph is the page's opening claim, so it is set larger than body prose. The
   `.fv-prose` descendant selector is how a prose run inside the panel is reached — the markdown
   bridge emits plain <p>, which carries no class of its own. */
.fv-role-hero-panel > .fv-prose > p:first-child {
  font-size: 1.1875rem;
  line-height: 1.6;
  color: var(--fv-text-secondary);
}

/* The button row. `margin-top` rather than a larger container gap so the note line below stays
   tight to the buttons it qualifies. */
.fv-role-hero-actions {
  margin-top: 4px;
}

/* A hero CTA past the button limit renders as an ordinary link (the composer drops its `variant`),
   so it needs the inline-link affordance back — without it, a third action would read as inert text
   sitting beside two buttons. */
.fv-role-hero-cta:not([class*="fv-btn-"]) {
  align-self: center;
  font-weight: 500;
}

/* `:::features` — the claim grid. The cards' radius, border and padding come from tokens; the grid's
   column count and collapse behavior come from `.fv-grid`, so nothing here repeats them.

   The wrapper carries the block's own vertical rhythm — the same job `.fv-role-plans` does, and the
   reason it needs a rule at all: a role the serializer emits with no rule behind it is exactly what
   `every_class_the_real_pages_emit_is_styled` (pageaudit.rs) exists to reject, because the class is
   emitted silently and the block just renders unspaced. */
.fv-role-features {
  margin: 8px 0;
}

.fv-role-feature-card {
  background: var(--fv-bg-primary);
}

/* A card's heading is a real subsection heading (it replaces a `## ` or a bold list lead-in), so it
   keeps outline weight — but it must not carry the body's heading margins inside a padded card. */
.fv-role-feature-card > .fv-text-heading {
  margin: 0;
  font-size: 1.0625rem;
  letter-spacing: -0.01em;
}

/* The card's prose is a short supporting sentence, not a document section: no top margin on its
   first paragraph and no bottom margin on its last, so the card's own padding is the only inset. */
.fv-role-feature-card .fv-prose > p:first-child {
  margin-top: 0;
}

.fv-role-feature-card .fv-prose > p:last-child {
  margin-bottom: 0;
}

/* `:::callout` — an admonition. The left border carries the tone; the tone classes below only
   recolor it, so a callout with an unrecognized tone still reads as a callout. */
.fv-role-callout {
  border: 1px solid var(--fv-border);
  border-left: 3px solid var(--fv-accent);
  border-radius: var(--fv-radius-md);
  background: var(--fv-bg-secondary);
  padding: 16px;
}

/* The three tones. Deliberately NOT signalled by color alone (WCAG 1.4.1): a `warn` is authored
   with a title, and its stronger border weight is visible in a monochrome print of a legal page. */
.fv-role-callout-note {
  border-left-color: var(--fv-text-secondary);
}

.fv-role-callout-warn {
  border-left-width: 4px;
  border-left-color: #B4541A;
}

.fv-role-callout-tip {
  border-left-color: var(--fv-accent);
}

@media (prefers-color-scheme: dark) {
  .fv-role-callout-warn {
    border-left-color: #E08A4C;
  }
}

/* A callout's title uses the `label` variant, which is NOT a heading — §13 defines `label` as a
   form-field label and the serializer emits <p> for it, so a callout inside a section cannot inject
   a phantom level into the page outline. This rule is what makes it *read* as the box's title. It
   sits tight to its own body so the box reads as one unit rather than a heading plus a paragraph. */
.fv-role-callout > .fv-text-label {
  margin: 0;
  font-size: 1rem;
}

/* `:::faq` — a stack of `<details>` disclosures. The list needs no rules of its own beyond the
   separators; `.fv-disclosure` above already styles each item and its marker. */
.fv-role-faq > .fv-disclosure + .fv-disclosure {
  border-top: 1px solid var(--fv-border);
}

/* `:::steps` — a numbered walkthrough. The row gap and the column claim come from tokens in the node
   tree; these rules state only the numeral's badge, which no token can express. */
.fv-role-steps {
  margin: 8px 0;
}

.fv-role-step-number {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  margin: 0;
  border-radius: var(--fv-radius-full);
  background: var(--fv-accent);
  color: #fff;
  font-size: 0.875rem;
  font-weight: 600;
  /* The badge is a circle, so its digit must not be pushed off-center by the inherited leading. */
  line-height: 1;
}

/* A step's own label introduces the step's body rather than a page section, which is why it uses the
   non-heading `label` variant (see the callout note above) and gets its emphasis from this rule. */
.fv-role-step > * > .fv-text-label {
  margin: 0;
  font-size: 1rem;
}

/* `:::plans` — the plan comparison. The card grid collapses to one column below the phone
   breakpoint through `.fv-grid`, which is why no per-page media query appears here. */
.fv-role-plan-card {
  border: 1px solid var(--fv-border);
  border-radius: var(--fv-radius-lg);
  background: var(--fv-bg-primary);
  padding: 24px;
}

.fv-role-plan-featured {
  border-color: var(--fv-accent);
  box-shadow: 0 2px 8px rgb(0 0 0 / 8%);
}

/* The price is the one line a reader scans for, so it is set larger than the plan name's own
   typography would suggest and tightened against the name above it. It carries the non-heading
   `body` variant — a price is a fact about the plan, not a section of the document — so every
   dimension it needs is stated HERE rather than inherited from a heading tier; `line-height`
   included, or the price would take body prose's 1.625 leading at 1.5rem. */
.fv-role-plan-price {
  margin: 0;
  color: var(--fv-text-primary);
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.02em;
}

/* A plan's call to action is a button-styled link (`fv-btn-primary` supplies the appearance); this
   only makes it a block so it fills the card's width and lines up across the cards. */
.fv-role-plan-cta {
  display: block;
  margin-top: 8px;
  text-align: center;
}

/* A plan card's list is the plan's contents, not prose: tighter than the body rhythm, and without
   the outer margin a <ul> would otherwise add inside the card's own padding. */
.fv-role-plan-card .fv-prose ul {
  margin: 0;
  padding-left: 20px;
}

.fv-role-plans {
  margin: 8px 0;
}

/* ---- Layouts (the `fv-layout-*` body class) ---------------------------------------------- */

/* Each layout adjusts the measure and the leading rhythm; the class is stamped on <body> from the
   layout the composition lambda resolved, so `<main>` is always reached as a descendant. */

/* `landing` and `index` open with a claim and want breathing room and a centered lead. */
.fv-layout-landing .fv-page,
.fv-layout-index .fv-page {
  max-width: 60rem;
}

.fv-layout-landing .fv-text-title {
  font-size: 2.5rem;
  letter-spacing: -0.03em;
}

.fv-layout-landing .fv-text-subtitle,
.fv-layout-index .fv-text-subtitle {
  font-size: 1.375rem;
}

/* `marketing` — /pricing, /delete-account. Wider than a document (the pricing table needs it),
   narrower than the landing page. */
.fv-layout-marketing .fv-page {
  max-width: 52rem;
}

/* `legal-document` and `help-article` are reading layouts: the base measure, and a slightly looser
   line height because these are the longest pages on the site. */
.fv-layout-legal-document .fv-page,
.fv-layout-help-article .fv-page {
  max-width: var(--fv-measure);
}

.fv-layout-legal-document .fv-prose,
.fv-layout-help-article .fv-prose {
  line-height: 1.7;
}

/* A legal page's effective-date stamp sits directly under the H1 and reads as metadata. */
.fv-layout-legal-document #legal-stamp {
  color: var(--fv-text-secondary);
}

@media (max-width: 40rem) {
  .fv-page {
    padding: 24px 16px 72px;
  }

  .fv-layout-landing .fv-text-title {
    font-size: 1.875rem;
  }
}

/* Print: the nav, footer, and pager are navigation, which a printed page cannot use. Legal
   documents get printed and filed, so this is a real path rather than a nicety. */
@media print {
  .fv-role-site-header,
  .fv-role-site-footer,
  .fv-role-pager {
    display: none;
  }

  .fv-page {
    max-width: none;
    padding: 0;
  }

  body {
    background: #fff;
    color: #000;
  }
}

/* Motion patterns (§20.7 `style.motion`) are entry animations. Honor the OS preference by defining
   them ONLY when motion is not reduced — the opt-out direction, so a new pattern added below cannot
   accidentally ship unguarded. */
@media (prefers-reduced-motion: no-preference) {
  .fv-motion-fade {
    animation: fv-fade 240ms ease-out both;
  }

  .fv-motion-rise {
    animation: fv-rise 280ms ease-out both;
  }

  @keyframes fv-fade {
    from { opacity: 0 }
    to { opacity: 1 }
  }

  @keyframes fv-rise {
    from { opacity: 0; transform: translateY(8px) }
    to { opacity: 1; transform: none }
  }
}

/* ---- The spacing / size scales ------------------------------------------------------------ */

/* Generated from SDL §7 (`SPACE_TOKENS` in `fvhtml.rs`). The named aliases share a rule with their
   numeric step (xs=1, sm=2, md=4, lg=6, xl=8), which is why they appear as grouped selectors. */

/* gap — `full` omitted: a percentage gap is not a meaningful spacing step */
.fv-gap-0{gap:0}
.fv-gap-1,.fv-gap-xs{gap:4px}
.fv-gap-2,.fv-gap-sm{gap:8px}
.fv-gap-3{gap:12px}
.fv-gap-4,.fv-gap-md{gap:16px}
.fv-gap-5{gap:20px}
.fv-gap-6,.fv-gap-lg{gap:24px}
.fv-gap-8,.fv-gap-xl{gap:32px}
.fv-gap-10{gap:40px}
.fv-gap-12{gap:48px}
.fv-gap-px{gap:1px}
/* padding, all sides */
.fv-p-0{padding:0}
.fv-p-1,.fv-p-xs{padding:4px}
.fv-p-2,.fv-p-sm{padding:8px}
.fv-p-3{padding:12px}
.fv-p-4,.fv-p-md{padding:16px}
.fv-p-5{padding:20px}
.fv-p-6,.fv-p-lg{padding:24px}
.fv-p-8,.fv-p-xl{padding:32px}
.fv-p-10{padding:40px}
.fv-p-12{padding:48px}
.fv-p-px{padding:1px}
/* padding-top */
.fv-pt-0{padding-top:0}
.fv-pt-1,.fv-pt-xs{padding-top:4px}
.fv-pt-2,.fv-pt-sm{padding-top:8px}
.fv-pt-3{padding-top:12px}
.fv-pt-4,.fv-pt-md{padding-top:16px}
.fv-pt-5{padding-top:20px}
.fv-pt-6,.fv-pt-lg{padding-top:24px}
.fv-pt-8,.fv-pt-xl{padding-top:32px}
.fv-pt-10{padding-top:40px}
.fv-pt-12{padding-top:48px}
.fv-pt-px{padding-top:1px}
/* padding-right */
.fv-pr-0{padding-right:0}
.fv-pr-1,.fv-pr-xs{padding-right:4px}
.fv-pr-2,.fv-pr-sm{padding-right:8px}
.fv-pr-3{padding-right:12px}
.fv-pr-4,.fv-pr-md{padding-right:16px}
.fv-pr-5{padding-right:20px}
.fv-pr-6,.fv-pr-lg{padding-right:24px}
.fv-pr-8,.fv-pr-xl{padding-right:32px}
.fv-pr-10{padding-right:40px}
.fv-pr-12{padding-right:48px}
.fv-pr-px{padding-right:1px}
/* padding-bottom */
.fv-pb-0{padding-bottom:0}
.fv-pb-1,.fv-pb-xs{padding-bottom:4px}
.fv-pb-2,.fv-pb-sm{padding-bottom:8px}
.fv-pb-3{padding-bottom:12px}
.fv-pb-4,.fv-pb-md{padding-bottom:16px}
.fv-pb-5{padding-bottom:20px}
.fv-pb-6,.fv-pb-lg{padding-bottom:24px}
.fv-pb-8,.fv-pb-xl{padding-bottom:32px}
.fv-pb-10{padding-bottom:40px}
.fv-pb-12{padding-bottom:48px}
.fv-pb-px{padding-bottom:1px}
/* padding-left */
.fv-pl-0{padding-left:0}
.fv-pl-1,.fv-pl-xs{padding-left:4px}
.fv-pl-2,.fv-pl-sm{padding-left:8px}
.fv-pl-3{padding-left:12px}
.fv-pl-4,.fv-pl-md{padding-left:16px}
.fv-pl-5{padding-left:20px}
.fv-pl-6,.fv-pl-lg{padding-left:24px}
.fv-pl-8,.fv-pl-xl{padding-left:32px}
.fv-pl-10{padding-left:40px}
.fv-pl-12{padding-left:48px}
.fv-pl-px{padding-left:1px}
/* margin, all sides */
.fv-m-0{margin:0}
.fv-m-1,.fv-m-xs{margin:4px}
.fv-m-2,.fv-m-sm{margin:8px}
.fv-m-3{margin:12px}
.fv-m-4,.fv-m-md{margin:16px}
.fv-m-5{margin:20px}
.fv-m-6,.fv-m-lg{margin:24px}
.fv-m-8,.fv-m-xl{margin:32px}
.fv-m-10{margin:40px}
.fv-m-12{margin:48px}
.fv-m-px{margin:1px}
/* margin-top */
.fv-mt-0{margin-top:0}
.fv-mt-1,.fv-mt-xs{margin-top:4px}
.fv-mt-2,.fv-mt-sm{margin-top:8px}
.fv-mt-3{margin-top:12px}
.fv-mt-4,.fv-mt-md{margin-top:16px}
.fv-mt-5{margin-top:20px}
.fv-mt-6,.fv-mt-lg{margin-top:24px}
.fv-mt-8,.fv-mt-xl{margin-top:32px}
.fv-mt-10{margin-top:40px}
.fv-mt-12{margin-top:48px}
.fv-mt-px{margin-top:1px}
/* margin-right */
.fv-mr-0{margin-right:0}
.fv-mr-1,.fv-mr-xs{margin-right:4px}
.fv-mr-2,.fv-mr-sm{margin-right:8px}
.fv-mr-3{margin-right:12px}
.fv-mr-4,.fv-mr-md{margin-right:16px}
.fv-mr-5{margin-right:20px}
.fv-mr-6,.fv-mr-lg{margin-right:24px}
.fv-mr-8,.fv-mr-xl{margin-right:32px}
.fv-mr-10{margin-right:40px}
.fv-mr-12{margin-right:48px}
.fv-mr-px{margin-right:1px}
/* margin-bottom */
.fv-mb-0{margin-bottom:0}
.fv-mb-1,.fv-mb-xs{margin-bottom:4px}
.fv-mb-2,.fv-mb-sm{margin-bottom:8px}
.fv-mb-3{margin-bottom:12px}
.fv-mb-4,.fv-mb-md{margin-bottom:16px}
.fv-mb-5{margin-bottom:20px}
.fv-mb-6,.fv-mb-lg{margin-bottom:24px}
.fv-mb-8,.fv-mb-xl{margin-bottom:32px}
.fv-mb-10{margin-bottom:40px}
.fv-mb-12{margin-bottom:48px}
.fv-mb-px{margin-bottom:1px}
/* margin-left */
.fv-ml-0{margin-left:0}
.fv-ml-1,.fv-ml-xs{margin-left:4px}
.fv-ml-2,.fv-ml-sm{margin-left:8px}
.fv-ml-3{margin-left:12px}
.fv-ml-4,.fv-ml-md{margin-left:16px}
.fv-ml-5{margin-left:20px}
.fv-ml-6,.fv-ml-lg{margin-left:24px}
.fv-ml-8,.fv-ml-xl{margin-left:32px}
.fv-ml-10{margin-left:40px}
.fv-ml-12{margin-left:48px}
.fv-ml-px{margin-left:1px}
/* size axis: width. `full` is the fill sentinel — the one token here that
   is a proportion rather than a length, which is why it is kept for the
   size axes and dropped from every spacing group above */
.fv-w-0{width:0}
.fv-w-1,.fv-w-xs{width:4px}
.fv-w-2,.fv-w-sm{width:8px}
.fv-w-3{width:12px}
.fv-w-4,.fv-w-md{width:16px}
.fv-w-5{width:20px}
.fv-w-6,.fv-w-lg{width:24px}
.fv-w-8,.fv-w-xl{width:32px}
.fv-w-10{width:40px}
.fv-w-12{width:48px}
.fv-w-px{width:1px}
.fv-w-full{width:100%}
/* size axis: height */
.fv-h-0{height:0}
.fv-h-1,.fv-h-xs{height:4px}
.fv-h-2,.fv-h-sm{height:8px}
.fv-h-3{height:12px}
.fv-h-4,.fv-h-md{height:16px}
.fv-h-5{height:20px}
.fv-h-6,.fv-h-lg{height:24px}
.fv-h-8,.fv-h-xl{height:32px}
.fv-h-10{height:40px}
.fv-h-12{height:48px}
.fv-h-px{height:1px}
.fv-h-full{height:100%}
/* size axis: minWidth */
.fv-minw-0{min-width:0}
.fv-minw-1,.fv-minw-xs{min-width:4px}
.fv-minw-2,.fv-minw-sm{min-width:8px}
.fv-minw-3{min-width:12px}
.fv-minw-4,.fv-minw-md{min-width:16px}
.fv-minw-5{min-width:20px}
.fv-minw-6,.fv-minw-lg{min-width:24px}
.fv-minw-8,.fv-minw-xl{min-width:32px}
.fv-minw-10{min-width:40px}
.fv-minw-12{min-width:48px}
.fv-minw-px{min-width:1px}
.fv-minw-full{min-width:100%}
/* size axis: maxWidth */
.fv-maxw-0{max-width:0}
.fv-maxw-1,.fv-maxw-xs{max-width:4px}
.fv-maxw-2,.fv-maxw-sm{max-width:8px}
.fv-maxw-3{max-width:12px}
.fv-maxw-4,.fv-maxw-md{max-width:16px}
.fv-maxw-5{max-width:20px}
.fv-maxw-6,.fv-maxw-lg{max-width:24px}
.fv-maxw-8,.fv-maxw-xl{max-width:32px}
.fv-maxw-10{max-width:40px}
.fv-maxw-12{max-width:48px}
.fv-maxw-px{max-width:1px}
.fv-maxw-full{max-width:100%}
/* size axis: minHeight */
.fv-minh-0{min-height:0}
.fv-minh-1,.fv-minh-xs{min-height:4px}
.fv-minh-2,.fv-minh-sm{min-height:8px}
.fv-minh-3{min-height:12px}
.fv-minh-4,.fv-minh-md{min-height:16px}
.fv-minh-5{min-height:20px}
.fv-minh-6,.fv-minh-lg{min-height:24px}
.fv-minh-8,.fv-minh-xl{min-height:32px}
.fv-minh-10{min-height:40px}
.fv-minh-12{min-height:48px}
.fv-minh-px{min-height:1px}
.fv-minh-full{min-height:100%}
/* size axis: maxHeight */
.fv-maxh-0{max-height:0}
.fv-maxh-1,.fv-maxh-xs{max-height:4px}
.fv-maxh-2,.fv-maxh-sm{max-height:8px}
.fv-maxh-3{max-height:12px}
.fv-maxh-4,.fv-maxh-md{max-height:16px}
.fv-maxh-5{max-height:20px}
.fv-maxh-6,.fv-maxh-lg{max-height:24px}
.fv-maxh-8,.fv-maxh-xl{max-height:32px}
.fv-maxh-10{max-height:40px}
.fv-maxh-12{max-height:48px}
.fv-maxh-px{max-height:1px}
.fv-maxh-full{max-height:100%}
/* spacer size — a fixed basis; `.fv-spacer` supplies flex:0 0 auto */
.fv-space-0{flex-basis:0}
.fv-space-1,.fv-space-xs{flex-basis:4px}
.fv-space-2,.fv-space-sm{flex-basis:8px}
.fv-space-3{flex-basis:12px}
.fv-space-4,.fv-space-md{flex-basis:16px}
.fv-space-5{flex-basis:20px}
.fv-space-6,.fv-space-lg{flex-basis:24px}
.fv-space-8,.fv-space-xl{flex-basis:32px}
.fv-space-10{flex-basis:40px}
.fv-space-12{flex-basis:48px}
.fv-space-px{flex-basis:1px}
.fv-space-full{flex-basis:100%}
