/* deckle theme stylesheet — Docs.
 *
 * The documentation look of the deckle editor itself: an ink header bar
 * like the editor's chrome, a white page, a section sidebar beside a
 * readable content column, blue links, green for the primary action, amber
 * only for warnings. Calm and flat: hairlines instead of fills, one soft
 * shadow for framed screenshots and hovered cards.
 *
 * Fork guide
 *
 * - Palette, type and rhythm live in the custom properties on :root below.
 *   Rebrand by editing that one block; every colour and size in the rules
 *   comes from a token. Each colour token states its role and its WCAG
 *   contrast — re-check the ratios if you change a value.
 * - Breakpoints (mobile first, nothing scrolls sideways from 320px up):
 *     < 40em   one column everywhere; docs sidebar is a disclosure
 *     >= 40em  image and card grids go to two columns; Previous/Next side
 *              by side
 *     >= 48em  three- and four-column grids; text-media and the hero with
 *              an image side by side; form fields side by side; footer in
 *              two columns; two-col backend layout 3:1
 *     >= 64em  the docs shell: the header's tab row replaces the Menu
 *              button, the sidebar becomes a sticky column beside the
 *              content
 *   They are plain @media queries; search for "40em" / "48em" / "64em".
 * - Dark surfaces (header, cta panel, video box, lightbox) redefine the
 *   hooks the base layer reads — --color-link, --color-link-strong,
 *   --color-focus, --color-marker, --color-icon (and --color-rule /
 *   --color-panel for tables) — so rich text placed on them recolours
 *   itself. Blue never carries text on ink (3.19:1): on ink, links and
 *   focus rings are white; the blue tab marker is a 3:1 UI indicator only.
 * - Green (#16a34a) is too light for white text (3.30:1): the primary
 *   button uses --color-primary-strong (5.02:1) and darkens on hover.
 * - Fonts are self-hosted (assets/fonts, published at /fonts/ by
 *   baseof.html): Inter (variable, upright + italic) for everything, a
 *   system monospace stack for code. No third-party request, ever.
 * - The base layer (element defaults: headings, links, lists, images, code)
 *   is written with :where() at zero specificity, so any class rule wins
 *   over it — no link rule can ever override a button's text colour.
 *   Buttons always declare background AND colour, in every state.
 * - Element markup (the ce- classes) is documented in the theme README.
 *   Style hooks only: templates carry no inline styles (the GFM table
 *   alignment hook is the one documented exception).
 * - Spacing between elements is never a margin. Every block element sits
 *   in a div.ce-frame that pads --frame above and below (see "frames").
 */

/* ----------------------------------------------------------------- fonts */

/* Inter 4.1, variable: wght 400-700 and opsz 14-32 (the optical size
   follows the font size automatically, so headings get the display cut),
   subset to Latin, Latin Extended and common punctuation. The CSS is served
   at /css/theme.css, so ../fonts/ is the published /fonts/. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/inter-variable.woff2") format("woff2");
}

@font-face {
  font-family: "Inter";
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/inter-italic-variable.woff2") format("woff2");
}

/* ---------------------------------------------------------------- tokens */

:root {
  /* Palette: the deckle editor's own — ink, the zinc grays, accent blue,
     primary green, amber for warnings. Ratios are WCAG 2.2 contrast (AA:
     4.5:1 text, 3:1 large text and UI). One light palette, no dark mode. */
  --color-bg: #ffffff;             /* page and content background */
  --color-surface: #fafafa;        /* zinc-50: hero band, footer, table head, sidebar disclosure, form panel */
  --color-surface-2: #f4f4f5;      /* zinc-100: hover fills, inline code, the screenshot window bar */
  --color-border: #e4e4e7;         /* zinc-200: hairlines, card and table borders (decorative) */
  --color-border-strong: #d4d4d8;  /* zinc-300: hovered hairlines, the secondary button ring, window dots (decorative) */
  --color-control: #71717a;        /* zinc-500: form input borders, 4.83:1 on white (UI needs 3:1) */
  --color-text: #1c1f26;           /* ink: body copy, headings. 16.49:1 on white, 15.80:1 on surface, 15.00:1 on surface-2, >= 15:1 on every callout tint */
  --color-text-soft: #3f3f46;      /* zinc-700: leads, sidebar links, teaser and footer text. 10.44:1 on white, 10.01:1 on surface, 9.50:1 on surface-2 */
  --color-muted: #71717a;          /* zinc-500: captions, meta, labels. 4.83:1 on white, 4.63:1 on surface; never on surface-2 (4.40:1) */
  --color-accent: #2563eb;         /* blue: links, focus ring, eyebrow, icons. 5.17:1 on white, 4.95:1 on surface, 4.75:1 on accent tint; 3.19:1 on ink, so never text there */
  --color-accent-strong: #1d4ed8;  /* blue hover; current sidebar link, step numbers, note title. 6.70:1 on white, 6.16:1 on accent tint */
  --color-accent-contrast: #ffffff;/* text on blue (skip link): 5.17:1 */
  --color-accent-tint: #eff6ff;    /* note callout, current sidebar link, step number discs */
  --color-accent-border: #bfdbfe;  /* note callout border, step discs, hovered cards (decorative) */
  --color-primary: #16a34a;        /* green: the tip callout's bar only (decorative); white on it is 3.30:1, so never a text background */
  --color-primary-strong: #15803d; /* primary button (white text 5.02:1), tip title and icon (4.79:1 on wash, 4.57:1 on tint) */
  --color-primary-deep: #166534;   /* primary button hover: white text 7.13:1 */
  --color-primary-tint: #dcfce7;   /* the tip icon's disc */
  --color-primary-wash: #f0fdf4;   /* tip callout background */
  --color-primary-border: #bbf7d0; /* tip callout border (decorative) */
  --color-warn: #b45309;           /* amber: warning bar and icon, 4.84:1 on warn bg */
  --color-warn-strong: #92400e;    /* warning title: 6.84:1 on warn bg */
  --color-warn-bg: #fffbeb;        /* warning callout background */
  --color-warn-border: #fde68a;    /* warning callout border (decorative) */
  --color-dark: #1c1f26;           /* ink: header bar, cta panel, video box */
  --color-dark-text: #d4d4d8;      /* copy and nav links on ink: 11.16:1 */
  --color-dark-muted: #a1a1aa;     /* secondary copy on ink (brand suffix, video note): 6.43:1 */
  --color-dark-strong: #ffffff;    /* headings, current tab, hover, links and focus ring on ink: 16.49:1 */
  --on-dark: 255 255 255;          /* white as an RGB triplet, for translucent hairlines and fills on ink: rgb(var(--on-dark) / .12) */
  --overlay: 28 31 38;             /* ink as an RGB triplet: the lightbox scrim and soft shadows, rgb(var(--overlay) / .9) */

  /* Elevation: hairline first, one soft shadow for framed screenshots and
     hovered cards. */
  --shadow-sm: 0 1px 2px rgb(var(--overlay) / .06);
  --shadow: 0 1px 2px rgb(var(--overlay) / .05), 0 12px 32px -14px rgb(var(--overlay) / .22);

  /* Spacing scale (rem). Also drives the spacer sizes s/m/l/xl = 3/5/6/7. */
  --space-1: .25rem;
  --space-2: .5rem;
  --space-3: 1rem;
  --space-4: 1.5rem;
  --space-5: 2.5rem;
  --space-6: 4rem;
  --space-7: 6rem;
  --section-gap: var(--space-5);   /* vertical rhythm between the elements of a slot */
  --frame: calc(var(--section-gap) / 2); /* each element's share of it: the padding above and below its frame (see "frames") */

  /* Type. Inter for everything; headings differ by size and weight only. */
  --font-body: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-display: var(--font-body);
  --font-mono: ui-monospace, "SFMono-Regular", "Cascadia Code", "Roboto Mono", Menlo, Consolas, "Liberation Mono", monospace;
  --display-weight: 650;           /* headings h2-h4 (the page title and hero are 700) */
  --display-tracking: -0.02em;     /* headings letter-spacing */
  --leading: 1.7;                  /* running text */
  --text-base: clamp(1rem, .975rem + .1vw, 1.0625rem); /* body copy: 16-17px */
  --text-sm: .875rem;              /* captions, meta, footer, table head */
  --text-h1: clamp(1.875rem, 1.55rem + 1.3vw, 2.5rem);  /* page title */
  --text-h2: clamp(1.375rem, 1.25rem + .5vw, 1.625rem); /* section headings in rich text */
  --text-h3: clamp(1.125rem, 1.08rem + .2vw, 1.25rem);  /* sub-sections */
  --text-h4: 1.0625rem;                                  /* teaser and card titles, minor headings */
  --text-display: clamp(2.25rem, 1.6rem + 2.6vw, 3.5rem); /* the hero headline */

  /* Layout */
  --radius-sm: 4px;                /* inline code, small chips */
  --radius: 6px;                   /* buttons, inputs, nav links */
  --radius-lg: 8px;                /* cards, panels, callouts, screenshots */
  --measure: 70ch;                 /* the readable content column: docs pages, rich text, page header */
  --sidebar: 15rem;                /* the docs sidebar column, >= 64em */
  --container: 80rem;              /* max width of the shell (.container): header, body, footer */
  --gutter: clamp(1rem, 4vw, 2rem);/* side padding of the shell; >= 16px at every width */
  --bleed: calc((100vw - min(var(--container), 100vw)) / 2 + var(--gutter)); /* content edge to viewport edge: a full-bleed band uses margin-inline: calc(-1 * var(--bleed)) and the same as padding */
}

/* ------------------------------------------------------------------ base */

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

html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

/* A column at least the viewport tall, so the footer sits at the bottom
   of a short page (404) instead of leaving white below it. */
body {
  margin: 0;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* classic scrollbars make 100vw (and so --bleed) overshoot by the bar
     width; clip those few px so full-bleed bands never scroll sideways */
  overflow-x: clip;
}

/* Zero-specificity defaults: every class rule below wins over these. */
:where(h1, h2, h3, h4, h5, h6) {
  font-family: var(--font-display);
  font-weight: var(--display-weight);
  line-height: 1.3;
  letter-spacing: var(--display-tracking);
  margin: 0 0 var(--space-3);
  overflow-wrap: break-word;
  text-wrap: balance;
}

:where(h1) { font-size: var(--text-h1); font-weight: 700; line-height: 1.15; }
:where(h2) { font-size: var(--text-h2); }
:where(h3) { font-size: var(--text-h3); }
:where(h4) { font-size: var(--text-h4); letter-spacing: -0.01em; }

/* Goldmark gives every heading an id: an anchor lands with air above. */
:where(h2, h3, h4)[id] { scroll-margin-top: var(--space-4); }

:where(p, ul, ol, dl, address, pre) { margin: 0 0 var(--space-3); }

:where(p, li, figcaption) { text-wrap: pretty; }

:where(address) { font-style: normal; }

/* Bold carries the editor's UI labels ("click **Publish**"): a firm 600
   in the surrounding colour. */
:where(strong, b) { font-weight: 600; }

:where(blockquote) {
  margin: 0 0 var(--space-3);
  padding-inline-start: var(--space-3);
  border-inline-start: 3px solid var(--color-rule, var(--color-border));
}

:where(hr) {
  border: 0;
  border-top: 1px solid var(--color-rule, var(--color-border));
  margin: var(--space-5) 0;
}

/* List markers, links and icons read their colour from hooks that dark
   surfaces redefine (see "dark surfaces" below). */
:where(li)::marker { color: var(--color-marker, var(--color-muted)); }

:where(ol > li)::marker { font-weight: 600; }

:where(a) {
  color: var(--color-link, var(--color-accent));
  text-underline-offset: .2em;
  text-decoration-thickness: 1px;
}

:where(a:hover) {
  color: var(--color-link-strong, var(--color-accent-strong));
  text-decoration-thickness: 2px;
}

:where(img, svg, video, iframe) { max-width: 100%; height: auto; }

:where(img) { display: block; }

/* Code: a system monospace stack, nothing to download. Inline code is a
   small zinc chip; a block scrolls sideways instead of overflowing. */
:where(code, kbd, samp, pre) {
  font-family: var(--font-mono);
  font-size: .875em;
}

:where(code) {
  background: var(--color-panel, var(--color-surface-2));
  border: 1px solid var(--color-rule, var(--color-border));
  border-radius: var(--radius-sm);
  padding: .1em .35em;
  overflow-wrap: break-word;
}

:where(pre) {
  overflow-x: auto;
  background: var(--color-panel, var(--color-surface));
  border: 1px solid var(--color-rule, var(--color-border));
  border-radius: var(--radius-lg);
  padding: var(--space-3);
  line-height: 1.55;
}

:where(pre code) { background: none; border: 0; padding: 0; font-size: inherit; }

:where(kbd) {
  border: 1px solid var(--color-border-strong);
  border-bottom-width: 2px;
  border-radius: var(--radius-sm);
  padding: .05em .4em;
  background: var(--color-bg);
}

/* Focus: 2px blue ring, 2px offset on light surfaces; dark surfaces set
   --color-focus to white. Never removed. */
:focus-visible {
  outline: 2px solid var(--color-focus, var(--color-accent));
  outline-offset: 2px;
}

.container {
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* White on blue (5.17:1); when focused it sits over the ink header, so
   its ring is white. */
.skip-link {
  position: absolute;
  left: -999px;
  top: 0;
  z-index: 100;
  padding: var(--space-2) var(--space-3);
  background: var(--color-accent);
  color: var(--color-accent-contrast);
  border-radius: var(--radius);
  font-weight: 600;
  text-decoration: none;
  --color-focus: var(--color-dark-strong);
}

.skip-link:focus {
  left: var(--space-2);
  top: var(--space-2);
  color: var(--color-accent-contrast);
}

/* Visually hidden, still in the accessibility tree. */
.sr-only,
.page--home .page__header {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------------------------------------------------------- dark surfaces */

/* Every ink surface recolours the hooks the base layer reads, so links,
   list markers, icons, tables, code and focus rings inside rich text placed
   on it stay light (blue is never text on ink). */
.site-header,
.ce-cta,
.ce-video,
.ce-lightbox__dialog {
  --color-link: var(--color-dark-strong);
  --color-link-strong: var(--color-dark-strong);
  --color-focus: var(--color-dark-strong);
  --color-marker: var(--color-dark-muted);
  --color-icon: currentColor;
  --color-rule: rgb(var(--on-dark) / .2);
  --color-panel: rgb(var(--on-dark) / .08);
}

/* ----------------------------------------------------------------- brand */

/* partials/brand.html: the inline wordmark SVG (currentColor for the word
   and the back sheet, blue for the front page), a muted suffix after a
   hairline, and the site title for assistive technology. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: .625rem;
  line-height: 1;
}

.brand__mark svg {
  display: block;
  width: auto;
  height: 1.625rem;
}

/* nudged 2px down so its baseline meets the word's */
.brand__suffix {
  position: relative;
  top: .125rem;
  padding-left: .625rem;
  border-left: 1px solid currentColor;
  font-size: 1.0625rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.25rem;
}

/* ---------------------------------------------------------------- header */

/* The ink bar, like the editor's chrome. Row 1: brand, then the language
   switch and the phone Menu button. Row 2: the section tabs (from 64em
   always shown; below, opened by the Menu button). Markup:
   partials/header.html. */
.site-header {
  background: var(--color-dark);
  color: var(--color-dark-text);
}

.site-header__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2) var(--space-3);
  min-height: 4rem;
  padding-block: var(--space-2);
}

.site-header__brand {
  display: block;
  flex: none;
  color: var(--color-dark-strong);
  text-decoration: none;
  border-radius: var(--radius);
}

.site-header__brand:hover { color: var(--color-dark-strong); }

.site-header .brand__suffix {
  color: var(--color-dark-muted);
  border-color: rgb(var(--on-dark) / .22);
}

.site-header__tools {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}

/* 22% white outline; 44px tall. */
.site-nav__toggle {
  display: inline-flex;
  align-items: center;
  gap: .5em;
  min-height: 2.75rem;
  flex: none;
  font: inherit;
  font-size: .9375rem;
  font-weight: 500;
  line-height: 1;
  color: var(--color-dark-strong);
  background: transparent;
  border: 1px solid rgb(var(--on-dark) / .22);
  border-radius: var(--radius);
  padding: 0 .85em;
  cursor: pointer;
}

.site-nav__toggle:hover {
  color: var(--color-dark-strong);
  background: rgb(var(--on-dark) / .08);
}

.site-nav__toggle-icon svg {
  display: block;
  width: 1.125rem;
  height: 1.125rem;
}

.site-nav {
  display: none;
  border-top: 1px solid rgb(var(--on-dark) / .1);
}

.site-nav__menu {
  list-style: none;
  margin: 0;
  padding: var(--space-2) 0 var(--space-3);
}

.site-nav__link {
  display: block;
  color: var(--color-dark-text);
  font-size: 1rem;
  font-weight: 500;
  line-height: 1.4;
  text-decoration: none;
  padding: .7rem .875rem;
  border-radius: var(--radius);
}

.site-nav__link:hover {
  color: var(--color-dark-strong);
  background: rgb(var(--on-dark) / .06);
}

.site-nav__link[aria-current] {
  color: var(--color-dark-strong);
  background: rgb(var(--on-dark) / .08);
  box-shadow: inset 3px 0 0 var(--color-accent);
}

/* Phone: the tab list opens under the bar when the toggle says so
   (assets/js/nav.js sets aria-expanded; Escape closes it). */
@media (max-width: 63.99em) {
  .site-header:has(.site-nav__toggle[aria-expanded="true"]) .site-nav { display: block; }
}

/* Desktop: the toggle goes, the tabs show as a row; the current section's
   tab is white with a blue underline (a 3:1 indicator against ink). */
@media (min-width: 64em) {
  .site-nav__toggle { display: none; }
  .site-nav { display: block; }
  .site-nav__menu {
    display: flex;
    flex-wrap: wrap;
    padding: 0;
    margin-inline: -.75rem;
  }
  .site-nav__link {
    font-size: .9375rem;
    padding: .8rem .75rem;
    border-radius: 0;
    box-shadow: inset 0 -2px 0 transparent;
  }
  .site-nav__link:hover {
    background: transparent;
    box-shadow: inset 0 -2px 0 rgb(var(--on-dark) / .35);
  }
  .site-nav__link[aria-current] {
    background: transparent;
    box-shadow: inset 0 -3px 0 var(--color-accent);
  }
  .site-nav__link:focus-visible { outline-offset: -4px; }
}

/* Language switch (multilingual sites only; markup: partials/header.html):
   the language labels, the current one on a faint white fill. Links are
   44px tall. On a narrow phone only the other languages show; the current
   one stays in the accessibility tree. */
.site-lang__list {
  display: flex;
  align-items: center;
  gap: .125rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.site-lang__link {
  display: flex;
  align-items: center;
  min-height: 2.75rem;
  color: var(--color-dark-text);
  font-size: .875rem;
  font-weight: 500;
  line-height: 1;
  text-decoration: none;
  border-radius: var(--radius);
  padding: 0 .7em;
}

.site-lang__link:hover {
  color: var(--color-dark-strong);
  background: rgb(var(--on-dark) / .06);
}

.site-lang__link[aria-current="page"] {
  color: var(--color-dark-strong);
  background: rgb(var(--on-dark) / .1);
}

@media (max-width: 29.99em) {
  .site-lang__item:has(> [aria-current="page"]) {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .site-header .brand__suffix { display: none; }
  .site-header__bar { gap: var(--space-2); }
  .site-header__tools { gap: var(--space-1); }
  .site-lang__link { padding-inline: .55em; }
  .site-nav__toggle { padding-inline: .7em; }
}

/* ------------------------------------------------------------ docs shell */

/* baseof.html: div.site-body > .container.site-body__inner holds the
   sidebar (docs pages only) and <main>. A docs page (.site-body--docs) is a
   grid: below 64em one column — the sidebar a closed disclosure above the
   content; from 64em the sidebar column (sticky) beside a content column
   capped at the measure. Every other page is <main> alone. */
.site-body { flex: 1 0 auto; }

.site-main {
  min-width: 0;
  padding-block: var(--space-5) var(--space-6);
}

.site-body--docs .site-body__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}

.site-body--docs .docs-nav { padding-top: var(--space-4); }

.site-body--docs .site-main { padding-top: var(--space-4); }

@media (min-width: 64em) {
  .site-body--docs .site-body__inner {
    grid-template-columns: var(--sidebar) minmax(0, var(--measure));
    column-gap: clamp(2.5rem, 5vw, 4.5rem);
  }
  .site-body--docs .site-main { padding-top: var(--space-5); }
}

/* The sidebar (partials/docs/sidebar.html). Phone: a details disclosure
   — a zinc bar with a chevron — whose panel lists the section. */
.docs-nav {
  min-width: 0;
  font-size: .9375rem;
  line-height: 1.4;
}

.docs-nav__summary {
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: 2.75rem;
  padding: .625rem 1rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  color: var(--color-text);
  font-weight: 600;
  cursor: pointer;
}

.docs-nav__summary::-webkit-details-marker { display: none; }

.docs-nav__summary::after {
  content: "";
  flex: none;
  width: .5rem;
  height: .5rem;
  margin-top: -.25rem;
  border-right: 2px solid var(--color-muted);
  border-bottom: 2px solid var(--color-muted);
  transform: rotate(45deg);
  transition: transform .15s ease;
}

.docs-nav__details[open] > .docs-nav__summary {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}

.docs-nav__details[open] > .docs-nav__summary::after {
  margin-top: .25rem;
  transform: rotate(-135deg);
}

.docs-nav__panel {
  padding: var(--space-2);
  border: 1px solid var(--color-border);
  border-top: 0;
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}

/* The "All topics" heading; on a phone the summary already says it. */
.docs-nav__heading { display: none; }

.docs-nav__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.docs-nav__list .docs-nav__list {
  margin: .125rem 0 .25rem .875rem;
  padding-left: .5rem;
  border-left: 1px solid var(--color-border);
}

.docs-nav__title,
.docs-nav__link {
  display: block;
  padding: .55rem .75rem;
  border-radius: var(--radius);
  text-decoration: none;
}

.docs-nav__title {
  color: var(--color-text);
  font-weight: 600;
}

.docs-nav__link { color: var(--color-text-soft); }

.docs-nav__title:hover,
.docs-nav__link:hover {
  color: var(--color-text);
  background: var(--color-surface-2);
}

.docs-nav :is(.docs-nav__title, .docs-nav__link)[aria-current="page"] {
  color: var(--color-accent-strong);
  background: var(--color-accent-tint);
  font-weight: 600;
}

/* Desktop: a sticky column. Where the browser can style the details'
   content slot (::details-content), the summary is hidden and the panel
   forced open, so the same markup is a plain sidebar; elsewhere it stays a
   disclosure. */
@media (min-width: 64em) {
  .site-body--docs .docs-nav {
    position: sticky;
    top: 0;
    align-self: start;
    max-height: 100vh;
    overflow-y: auto;
    padding-block: var(--space-5) var(--space-4);
    /* room for focus rings at the scroll edge */
    margin-inline: -4px;
    padding-inline: 4px;
  }
  .docs-nav__title,
  .docs-nav__link { padding-block: .4rem; }
  @supports selector(::details-content) {
    .docs-nav__summary { display: none; }
    .docs-nav__details::details-content {
      display: block;
      content-visibility: visible;
    }
    .docs-nav__panel {
      padding: 0;
      border: 0;
    }
    .docs-nav__heading {
      display: block;
      margin: 0 0 var(--space-2);
      padding-inline: .75rem;
      font-size: .75rem;
      font-weight: 600;
      letter-spacing: .06em;
      text-transform: uppercase;
      color: var(--color-muted);
    }
    .docs-nav__title { margin-bottom: .125rem; }
  }
}

/* Previous / Next (partials/docs/pager.html): two bordered cards under a
   hairline, side by side from 40em; Next always on the right. */
.docs-pager {
  display: grid;
  gap: var(--space-3);
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-border);
}

.docs-pager__link {
  display: flex;
  flex-direction: column;
  gap: .2rem;
  min-width: 0;
  padding: var(--space-3) 1.25rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  text-decoration: none;
  transition: border-color .15s ease, box-shadow .15s ease;
}

.docs-pager__link:hover {
  border-color: var(--color-accent-border);
  box-shadow: var(--shadow-sm);
}

.docs-pager__link--next {
  align-items: flex-end;
  text-align: right;
}

.docs-pager__dir {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--color-muted);
}

.docs-pager__icon svg {
  display: block;
  width: 1em;
  height: 1em;
}

.docs-pager__title {
  font-weight: 600;
  color: var(--color-accent-strong);
  overflow-wrap: break-word;
}

.docs-pager__link:hover .docs-pager__title { text-decoration: underline; text-underline-offset: .2em; }

/* After a section page's teaser cards (already under a hairline) the
   pager needs no second rule. */
.site-main:has(> .page > .ce-teasers) > .docs-pager {
  margin-top: var(--space-5);
  padding-top: 0;
  border-top: 0;
}

@media (min-width: 40em) {
  .docs-pager { grid-template-columns: 1fr 1fr; }
  .docs-pager__link--next { grid-column: 2; }
}

/* ----------------------------------------------------------- page header */

/* The eyebrow (the section, on pages below it), the title at h1 size and
   the lead (the page description), capped at the measure. */
.page__header {
  max-width: var(--measure);
  margin-bottom: var(--space-5);
}

.page__eyebrow {
  margin: 0 0 var(--space-2);
  font-size: var(--text-sm);
  font-weight: 600;
  line-height: 1.4;
  color: var(--color-accent);
}

.page__eyebrow-link {
  color: inherit;
  text-decoration: none;
}

.page__eyebrow-link:hover {
  color: var(--color-accent-strong);
  text-decoration: underline;
}

.page__title {
  font-size: var(--text-h1);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.025em;
  margin: 0;
}

.page__lead {
  text-wrap: pretty;
  margin: var(--space-3) 0 0;
  font-size: 1.1875rem;
  line-height: 1.55;
  color: var(--color-text-soft);
}

/* A page without the docs shell (legal pages): the column alone. */
.site-body:not(.site-body--docs) .page:not(.page--home, .page--notfound) { max-width: var(--measure); }

/* Home: the header is visually hidden (above, with .sr-only) because the
   hero is the lead. A hero that opens the page sits flush under the ink
   bar; the next element gets a band's worth of air. */
.site-main:has(> .page--home > .page__content > .ce-layout > .ce-layout__slot--main:first-child > .ce-frame--hero:first-child) { padding-top: 0; }

.page--home .ce-frame--hero + .ce-frame { --frame-start: var(--space-6); }

/* The 404: a short centred message. */
.page--notfound {
  max-width: 32rem;
  margin: var(--space-6) auto;
  text-align: center;
}

.page--notfound .page__header { margin-bottom: var(--space-3); }

.page--notfound .page__eyebrow { font-size: 1rem; letter-spacing: .08em; }

.page--notfound .page__content { color: var(--color-text-soft); }

/* Section pages (list.html): the child-page teasers follow the body
   under a hairline. */
.page__content ~ .ce-teasers {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-border);
}

/* The no-layout fallback (backendlayout/render.html emitting plain
   .Content, no slot wrappers) needs no rule of its own: every element
   brings its frame, and plain markdown paragraphs keep their own margins. */

/* ---------------------------------------------------------------- footer */

/* A light band under a hairline: brand, description and address; the
   legal links; the copyright line under its own hairline. Two columns
   from 48em. Markup: partials/footer.html. */
.site-footer {
  background: var(--color-surface);
  border-top: 1px solid var(--color-border);
  color: var(--color-text-soft);
  font-size: var(--text-sm);
  line-height: 1.6;
}

.site-footer__inner {
  display: grid;
  gap: var(--space-4);
  padding-block: var(--space-5) var(--space-4);
}

.site-footer__brand {
  margin: 0 0 var(--space-3);
  color: var(--color-text);
}

.site-footer__brand .brand__mark svg { height: 1.5rem; }

.site-footer__brand .brand__suffix {
  font-size: .9375rem;
  color: var(--color-muted);
  border-color: var(--color-border-strong);
}

.site-footer__text,
.site-footer__address {
  max-width: 34rem;
  margin: 0 0 var(--space-2);
}

.site-footer__about > :last-child { margin-bottom: 0; }

.site-footer__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--space-4);
}

.site-footer__link {
  display: inline-block;
  padding-block: .5rem;
  color: var(--color-text-soft);
  font-weight: 500;
  text-decoration: none;
}

.site-footer__link:hover,
.site-footer__link[aria-current="page"] {
  color: var(--color-accent-strong);
  text-decoration: underline;
}

.site-footer__copy {
  margin: 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  color: var(--color-muted);
}

@media (min-width: 48em) {
  .site-footer__inner {
    grid-template-columns: 1fr auto;
    column-gap: var(--space-6);
  }
  .site-footer__nav { justify-self: end; }
  .site-footer__copy { grid-column: 1 / -1; }
}

/* -------------------------------------------------------------- controls */

/* Buttons (.ce-btn: the button element, and the submit of deckle-hosted's
   hosted-contact-form element — README "Hosted contact form"). Inter 600,
   44px tall. Each variant declares background AND colour in every state;
   on the ink cta panel the parent restyles the secondary variant. */
.ce-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .5em;
  min-height: 2.75rem;
  font: inherit;
  font-size: .9375rem;
  font-weight: 600;
  line-height: 1.25;
  text-decoration: none;
  cursor: pointer;
  border: 0;
  border-radius: var(--radius);
  padding: .65em 1.15em;
  transition: background-color .15s ease, box-shadow .15s ease;
}

/* green: white on primary strong (5.02:1), hover primary deep (7.13:1) */
.ce-btn--primary {
  background: var(--color-primary-strong);
  color: var(--color-accent-contrast);
}

.ce-btn--primary:hover {
  background: var(--color-primary-deep);
  color: var(--color-accent-contrast);
}

/* white with a zinc ring; hover zinc-100 fill, ink text (15.00:1) */
.ce-btn--secondary {
  background: var(--color-bg);
  color: var(--color-text);
  box-shadow: inset 0 0 0 1px var(--color-border-strong), var(--shadow-sm);
}

.ce-btn--secondary:hover {
  background: var(--color-surface-2);
  color: var(--color-text);
  box-shadow: inset 0 0 0 1px var(--color-border-strong), var(--shadow-sm);
}

/* on ink: white text, 35% white ring; hover fills faintly */
.ce-cta .ce-btn--secondary {
  background: transparent;
  color: var(--color-dark-strong);
  box-shadow: inset 0 0 0 1px rgb(var(--on-dark) / .35);
}

.ce-cta .ce-btn--secondary:hover {
  background: rgb(var(--on-dark) / .08);
  color: var(--color-dark-strong);
  box-shadow: inset 0 0 0 1px rgb(var(--on-dark) / .6);
}

/* ==================================================================== */
/* Frames                                                               */
/* ==================================================================== */

/* Every block element is wrapped in one div.ce-frame.ce-frame--<name>
   (the shortcode name), emitted by its template; the frame carries the
   id="c-<uid>" anchor and the editor markers (partials/ce/attrs.html).
   The frame does the spacing, and only the frame: --frame (half of
   --section-gap) of padding above and below, so two neighbours sit one
   section gap apart and nothing between elements is ever a margin. A band
   background therefore belongs on the frame (the hero's): it fills the
   padding. The first and last frame of a slot drop the outer edge (rules
   under "backend layouts"); the page header above and the pager or footer
   below set those distances. */
.ce-frame {
  --frame-start: var(--frame);
  --frame-end: var(--frame);
  padding-block: var(--frame-start) var(--frame-end);
}

.ce-frame[id] { scroll-margin-top: var(--space-4); }

/* A full-bleed frame runs to the viewport edge (README, "Full-bleed
   blocks") — only on a page without the docs sidebar (the home page), the
   one place a margin is used, and it is horizontal. The element inside
   adds the same value back as padding-inline to keep its content on the
   shell edge. In the docs column it stays a contained block. */
.ce-frame--bleed { margin-inline: calc(-1 * var(--bleed)); }

.site-body--docs .ce-frame--bleed { margin-inline: 0; }

/* ==================================================================== */
/* Element library (ce- vocabulary), one block per element             */
/* ==================================================================== */

/* --- hero -------------------------------------------------------------- */

/* A calm intro band: zinc-50 with a faint dot grid fading in towards the
   right, a hairline under it; heading, lead and button chips on the shell
   edge. With the optional image (ce-hero--image) the text and the framed
   picture sit side by side from 48em. In the docs column (and in a
   backend layout's columns) the band is a rounded panel on the element
   instead, so the frame's padding keeps two heroes apart, and a picture
   stacks under the text. */
.ce-frame--hero,
.site-body--docs .ce-hero,
.ce-layout__cols .ce-hero {
  background:
    linear-gradient(to right, var(--color-surface) 30%, rgb(250 250 250 / 0)),
    radial-gradient(circle at 1px 1px, var(--color-border-strong) 1px, transparent 1.5px) 0 0 / 22px 22px,
    var(--color-surface);
}

.ce-frame--hero { border-bottom: 1px solid var(--color-border); }

.ce-hero {
  --band-pad: clamp(var(--space-5), 7vw, 5.5rem);
  padding: calc(var(--band-pad) - var(--frame-start)) var(--bleed) calc(var(--band-pad) - var(--frame-end));
}

.ce-hero__inner {
  max-width: 44rem;
  min-width: 0;
}

.ce-hero__heading {
  font-size: var(--text-display);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.03em;
  max-width: 18ch;
  margin-bottom: var(--space-3);
}

.ce-hero__body {
  font-size: 1.1875rem;
  line-height: 1.6;
  color: var(--color-text-soft);
  max-width: 38rem;
}

.ce-hero__body > :last-child { margin-bottom: 0; }

/* Button chips stay inline in their paragraph (text may sit beside
   them); a little margin keeps wrapped chips apart. */
:is(.ce-hero__body, .ce-cta__body) .ce-btn {
  margin-block: .25rem;
  margin-inline-end: .375rem;
  vertical-align: middle;
}

:is(.ce-hero__body, .ce-cta__body) > * + p:has(.ce-btn) { margin-top: var(--space-4); }

.ce-hero--image {
  display: grid;
  gap: var(--space-5);
  align-items: center;
}

.ce-hero__img {
  order: 2;
  width: 100%;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  background: var(--color-bg);
}

@media (min-width: 48em) {
  .ce-hero--image { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

:is(.site-body--docs, .ce-layout__cols) .ce-frame--hero {
  background: none;
  border-bottom: 0;
}

:is(.site-body--docs, .ce-layout__cols) .ce-hero {
  padding: var(--space-5) clamp(var(--space-4), 4vw, var(--space-5));
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}

:is(.site-body--docs, .ce-layout__cols) .ce-hero--image { grid-template-columns: minmax(0, 1fr); }

.site-body--docs .ce-hero__heading { font-size: var(--text-h1); }

/* --- text -------------------------------------------------------------- */

/* The rte workhorse, capped at the measure. Headings step down with air
   above; lists get room between items. */
.ce-text {
  max-width: var(--measure);
  overflow-wrap: break-word;
}

.ce-text > :last-child { margin-bottom: 0; }

.ce-text h2 { margin-top: var(--space-5); }
.ce-text h3 { margin-top: 2rem; margin-bottom: var(--space-2); }
.ce-text h4 { margin-top: var(--space-4); margin-bottom: var(--space-2); }

.ce-text > :is(h2, h3, h4):first-child { margin-top: 0; }

:is(.ce-text, .ce-text-media__body, .ce-accordion__body, .ce-callout__body, .ce-step__body, .ce-cta__body) :is(ul, ol) { padding-left: 1.375em; }

:is(.ce-text, .ce-text-media__body, .ce-accordion__body, .ce-callout__body, .ce-step__body, .ce-cta__body) li + li { margin-top: .375em; }

:is(.ce-text, .ce-text-media__body, .ce-accordion__body, .ce-callout__body, .ce-step__body, .ce-cta__body) li > :is(ul, ol) {
  margin-top: .375em;
  margin-bottom: 0;
}

/* Inline button chips: wrapped lines must not touch. */
.ce-text .ce-btn { margin-block: .15em; }

/* GFM pipe tables in any rich-text body or plain content, wrapped by the
   table render hook (layouts/_default/_markup/render-table.html) in a
   focusable (tabindex="0") box that scrolls sideways instead of
   overflowing the page. Hairline rows, a zinc-50 head, no vertical rules.
   Per-cell alignment arrives as an inline style from the hook and always
   wins; nothing here sets text-align on a cell. */
.ce-table {
  overflow-x: auto;
  margin: var(--space-4) 0;
  border: 1px solid var(--color-rule, var(--color-border));
  border-radius: var(--radius-lg);
}

.ce-table:last-child { margin-bottom: 0; }

.ce-table__table {
  width: 100%;
  border-collapse: collapse;
  text-align: left;
  font-size: .9375rem;
  line-height: 1.55;
  font-variant-numeric: tabular-nums;
}

.ce-table__table :is(th, td) {
  padding: .65em .9em;
  border-bottom: 1px solid var(--color-rule, var(--color-border));
  vertical-align: top;
}

.ce-table__table thead th {
  background: var(--color-panel, var(--color-surface));
  font-size: var(--text-sm);
  font-weight: 600;
  line-height: 1.4;
}

.ce-table__table tbody tr:last-child td { border-bottom: 0; }

/* --- button (inline) --------------------------------------------------- */

/* a.ce-btn.ce-btn--primary|--secondary: see "controls" above. An icon chip
   inside a button takes the button's text colour. */
.ce-btn .ce-icon { color: inherit; }

/* --- icon (inline) ----------------------------------------------------- */

/* Built-in stroke glyph set (layouts/partials/icon.html), sized to the
   text, tinted through currentColor: blue on light (AA on white and
   surface), the surrounding text colour on ink. An unknown name renders
   an empty span. Forks restyle via ce-icon--<name> / [data-icon]. */
.ce-icon {
  display: inline-block;
  vertical-align: -.125em;
  color: var(--color-icon, var(--color-accent));
}

.ce-icon svg {
  display: block;
  width: 1em;
  height: 1em;
}

/* --- screenshot, and the framed figure of a step ------------------------ */

/* figure.ce-screenshot.ce-screenshot--full|medium|small > a.ce-lightbox >
   img, then an optional caption. The link is the frame: a hairline, 8px
   corners, a soft shadow and a thin faux window bar (zinc-50 with three
   zinc dots) drawn above the picture. Medium and small are centred and
   capped (34rem, 22rem), full at every width below that; a picture
   narrower than that keeps its own width. A zoom cursor says it enlarges
   (assets/js/lightbox.js). */
.ce-screenshot {
  margin: 0 auto;
  min-width: 0;
}

.ce-screenshot--medium { max-width: min(100%, 34rem); }

.ce-screenshot--small { max-width: min(100%, 22rem); }

.ce-screenshot__link,
.ce-step__figure > a {
  display: block;
  width: fit-content;
  max-width: 100%;
  overflow: hidden;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-bg);
  box-shadow: var(--shadow);
  transition: border-color .15s ease, box-shadow .15s ease;
}

.ce-screenshot__link::before,
.ce-step__figure > a::before {
  content: "";
  display: block;
  height: 1.125rem;
  border-bottom: 1px solid var(--color-border);
  background:
    radial-gradient(circle at .75rem 50%, var(--color-border-strong) 3px, transparent 3.5px),
    radial-gradient(circle at 1.375rem 50%, var(--color-border-strong) 3px, transparent 3.5px),
    radial-gradient(circle at 2rem 50%, var(--color-border-strong) 3px, transparent 3.5px),
    var(--color-surface);
}

.ce-screenshot__link:hover,
.ce-step__figure > a:hover {
  border-color: var(--color-border-strong);
  box-shadow: 0 1px 2px rgb(var(--overlay) / .06), 0 16px 40px -14px rgb(var(--overlay) / .3);
}

/* Never wider than the file: a small crop keeps its size instead of
   being blown up (the frame shrinks to it, centred in a screenshot). */
.ce-screenshot__img,
.ce-step__img {
  width: auto;
  max-width: 100%;
  height: auto;
}

.ce-screenshot__link { margin-inline: auto; }

.ce-screenshot__caption {
  margin-top: .75rem;
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--color-muted);
  text-align: center;
  text-wrap: balance;
}

.ce-lightbox { cursor: zoom-in; }

/* --- callout ----------------------------------------------------------- */

/* aside.ce-callout.ce-callout--tip|note|warning: a tinted box with a
   hairline border and a 3px bar on its start edge; the title row (icon +
   title) in the variant's strong colour, the rich-text body in ink,
   aligned under the title text. The variant only sets the five hooks. */
.ce-callout {
  --callout-bg: var(--color-primary-wash);
  --callout-border: var(--color-primary-border);
  --callout-bar: var(--color-primary);
  --callout-title: var(--color-primary-strong);
  --callout-icon: var(--color-primary-strong);
  max-width: var(--measure);
  padding: var(--space-3) 1.25rem;
  background: var(--callout-bg);
  border: 1px solid var(--callout-border);
  border-radius: var(--radius-lg);
  box-shadow: inset 3px 0 0 var(--callout-bar);
  overflow-wrap: break-word;
}

/* tip: green title 4.79:1 on the wash */
.ce-callout--tip { --callout-bar: var(--color-primary); }

/* note: blue title 6.16:1, icon 4.75:1 on the tint */
.ce-callout--note {
  --callout-bg: var(--color-accent-tint);
  --callout-border: var(--color-accent-border);
  --callout-bar: var(--color-accent);
  --callout-title: var(--color-accent-strong);
  --callout-icon: var(--color-accent);
}

/* warning: amber title 6.84:1, icon 4.84:1 on the amber background */
.ce-callout--warning {
  --callout-bg: var(--color-warn-bg);
  --callout-border: var(--color-warn-border);
  --callout-bar: var(--color-warn);
  --callout-title: var(--color-warn-strong);
  --callout-icon: var(--color-warn);
}

.ce-callout__title {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin: 0 0 .25rem;
  font-weight: 600;
  line-height: 1.5;
  color: var(--callout-title);
}

.ce-callout__icon {
  flex: none;
  color: var(--callout-icon);
}

.ce-callout__icon svg {
  display: block;
  width: 1.25rem;
  height: 1.25rem;
}

.ce-callout__body {
  padding-left: 1.75rem;
  font-size: .9688rem;
  line-height: 1.65;
}

.ce-callout__body > :last-child { margin-bottom: 0; }

.ce-callout__body .ce-icon { color: var(--callout-icon); }

/* --- steps and step ---------------------------------------------------- */

/* ol.ce-steps > li.ce-step: numbered by a CSS counter (so a disabled step,
   which renders nothing, leaves no gap). Each number is a 36px disc in the
   blue tint; a 2px zinc line joins it to the next one. The title is bold,
   the body follows, then an optional screenshot framed like the screenshot
   element. role="list" keeps the list semantics Safari drops for
   list-style: none. */
.ce-steps {
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: var(--measure);
  counter-reset: ce-step;
}

.ce-step {
  position: relative;
  min-width: 0;
  counter-increment: ce-step;
  padding: 0 0 var(--space-5) 3.25rem;
  overflow-wrap: break-word;
}

.ce-step:last-child { padding-bottom: 0; }

.ce-step::before {
  content: counter(ce-step);
  position: absolute;
  left: 0;
  top: 0;
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  background: var(--color-accent-tint);
  box-shadow: inset 0 0 0 1px var(--color-accent-border);
  color: var(--color-accent-strong);
  font-size: 1rem;
  font-weight: 650;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.ce-step::after {
  content: "";
  position: absolute;
  left: calc(1.125rem - 1px);
  top: 2.75rem;
  bottom: .5rem;
  width: 2px;
  border-radius: 1px;
  background: var(--color-border);
}

.ce-step:last-child::after { display: none; }

/* padding, not margin, above the title: a margin would collapse through
   the li and lift the title off the disc's centre */
.ce-step__title {
  margin: 0 0 .25rem;
  padding-top: .3rem;
  font-size: var(--text-h4);
  font-weight: 600;
  line-height: 1.5;
  letter-spacing: -0.01em;
}

.ce-step__body > :last-child { margin-bottom: 0; }

.ce-step__figure { margin: var(--space-3) 0 0; }

/* Narrow phones: a smaller disc and indent leave the text more room. */
@media (max-width: 29.99em) {
  .ce-step { padding-left: 2.75rem; }
  .ce-step::before {
    width: 2rem;
    height: 2rem;
    font-size: .9375rem;
  }
  .ce-step::after {
    left: calc(1rem - 1px);
    top: 2.5rem;
  }
  .ce-step__title { padding-top: .2rem; }
}

/* --- image, with its hidden img rows ----------------------------------- */

/* .ce-images.ce-images--cols-N[data-lightbox] > figure.ce-figure. One
   column, two from 40em, cols-3/4 reach 3/4 from 48em: cols is a maximum.
   text-media's media pane is the same grid (see text-media). */
.ce-images {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
}

@media (min-width: 40em) {
  .ce-images--cols-2,
  .ce-images--cols-3,
  .ce-images--cols-4 { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 48em) {
  .ce-images--cols-3 { grid-template-columns: repeat(3, 1fr); }
  .ce-images--cols-4 { grid-template-columns: repeat(4, 1fr); }
}

/* img (hidden row) / standalone figure. A linked image wraps in its own
   <a> (always wins); with the lightbox on, a linkless image wraps in
   a.ce-lightbox (dialog styles at the end of this file). */
.ce-figure { margin: 0; min-width: 0; }

.ce-figure > a {
  display: block;
  border-radius: var(--radius-lg);
}

.ce-figure__img {
  width: 100%;
  object-fit: cover;
  border-radius: var(--radius-lg);
}

.ce-images .ce-figure__img { aspect-ratio: 4 / 3; }

.ce-figure__caption {
  font-size: var(--text-sm);
  color: var(--color-muted);
  padding-top: var(--space-2);
}

/* --- text-media -------------------------------------------------------- */

/* Body text beside the media pane (.ce-text-media__media, a ce-images grid
   that always comes first in the markup): stacked below 48em (media
   first), then two equal columns, side right putting the media second.
   Everything stays inside the column; photos keep the 8px corners. */
.ce-text-media {
  display: grid;
  gap: var(--space-4);
  align-items: center;
}

.ce-text-media__media,
.ce-text-media__body { min-width: 0; overflow-wrap: break-word; }

.ce-text-media__body > :last-child { margin-bottom: 0; }

.ce-text-media__body h3 { font-size: var(--text-h2); }

.ce-text-media__media:not(.ce-images--cols-2, .ce-images--cols-3, .ce-images--cols-4) .ce-figure__img { aspect-ratio: 4 / 3; }

.ce-text-media__media:is(.ce-images--cols-2, .ce-images--cols-3, .ce-images--cols-4) { gap: var(--space-2); }

/* no images at all: the body alone, at the measure */
.ce-text-media:not(:has(> .ce-text-media__media)) .ce-text-media__body { max-width: var(--measure); }

@media (min-width: 48em) {
  .ce-text-media {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--space-5);
  }
  .ce-text-media:not(:has(> .ce-text-media__media)) { grid-template-columns: 1fr; }
  .ce-text-media--right .ce-text-media__media { order: 2; }
}

/* --- cards, with its hidden card rows ---------------------------------- */

/* .ce-cards.ce-cards--cols-N > a.ce-card | div.ce-card. A card is a white
   tile with a hairline: the photo (decorative, alt="") on top at 16:10,
   then the heading and text. A linked card lifts on hover (blue border,
   soft shadow, blue heading). One column, two from 40em, cols-3/4 from
   48em. */
.ce-cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
}

@media (min-width: 40em) {
  .ce-cards--cols-2,
  .ce-cards--cols-3,
  .ce-cards--cols-4 { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 48em) {
  .ce-cards--cols-3 { grid-template-columns: repeat(3, 1fr); }
  .ce-cards--cols-4 { grid-template-columns: repeat(4, 1fr); }
}

.ce-card {
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
  padding-bottom: 1.25rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-bg);
  color: var(--color-text);
}

a.ce-card {
  color: var(--color-text);
  text-decoration: none;
  transition: border-color .15s ease, box-shadow .15s ease;
}

a.ce-card:hover {
  color: var(--color-text);
  border-color: var(--color-accent-border);
  box-shadow: var(--shadow);
}

.ce-card__img {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border-bottom: 1px solid var(--color-border);
}

.ce-card__heading {
  font-size: var(--text-h4);
  margin: 1.125rem 1.25rem .25rem;
}

a.ce-card:hover .ce-card__heading { color: var(--color-accent-strong); }

.ce-card__text {
  margin: 0 1.25rem;
  font-size: .9375rem;
  line-height: 1.55;
  color: var(--color-text-soft);
}

/* --- quote ------------------------------------------------------------- */

/* figure > blockquote __body + figcaption __attribution: a blue bar, the
   quote in Inter italic, the attribution muted. */
.ce-quote {
  margin: 0;
  max-width: var(--measure);
  padding: .25rem 0 .25rem 1.25rem;
  border-left: 3px solid var(--color-accent);
}

.ce-quote__body {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 1.1875rem;
  font-style: italic;
  line-height: 1.55;
}

.ce-quote__body > :last-child { margin-bottom: 0; }

.ce-quote__attribution {
  margin-top: .75rem;
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--color-muted);
}

/* --- video-embed (click-to-load facade) -------------------------------- */

/* A 16:9 ink box; the consent button fills it (nothing loads until it is
   pressed, assets/js/consent.js), then the iframe does. The button sits in
   the flow of a one-cell grid, so on a narrow phone, where the play button,
   label and note do not fit 16:9, the box grows instead of clipping them
   (no overflow: hidden; the corners are rounded on the children). */
.ce-video {
  position: relative;
  display: grid;
  aspect-ratio: 16 / 9;
  background: var(--color-dark);
  border-radius: var(--radius-lg);
  color: var(--color-dark-text);
}

.ce-video__consent {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  background: transparent;
  border: 0;
  border-radius: var(--radius-lg);
  color: inherit;
  font: inherit;
  cursor: pointer;
  padding: var(--space-4) var(--space-3);
  text-align: center;
}

/* The button covers the whole box and a ring outside it would be white on
   white, so the white ring is drawn inside the button. */
.ce-video__consent:focus-visible { outline-offset: -6px; }

.ce-video__play {
  width: 4rem;
  height: 4rem;
  flex: none;
  border-radius: 50%;
  background: var(--color-dark-strong);
  position: relative;
  margin-bottom: var(--space-2);
  transition: transform .15s ease;
}

.ce-video__consent:is(:hover, :focus-visible) .ce-video__play { transform: scale(1.06); }

.ce-video__play::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-38%, -50%);
  border-style: solid;
  border-width: .7rem 0 .7rem 1.1rem;
  border-color: transparent transparent transparent var(--color-dark);
}

.ce-video__label {
  font-weight: 600;
  font-size: var(--text-h4);
  line-height: 1.35;
  color: var(--color-dark-strong);
}

.ce-video__note {
  font-size: var(--text-sm);
  color: var(--color-dark-muted);
}

.ce-video__iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  border-radius: var(--radius-lg);
}

/* --- accordion and accordion-item -------------------------------------- */

/* Native details/summary in one bordered group, hairlines between the
   items; a chevron (generated, not read aloud) turns when an item opens. */
.ce-accordion {
  max-width: var(--measure);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}

.ce-accordion__item + .ce-accordion__item { border-top: 1px solid var(--color-border); }

.ce-accordion__summary {
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: .875rem 1.25rem;
  font-weight: 600;
  line-height: 1.45;
  cursor: pointer;
  border-radius: var(--radius-lg);
}

.ce-accordion__summary::-webkit-details-marker { display: none; }

.ce-accordion__summary::after {
  content: "";
  flex: none;
  width: .5rem;
  height: .5rem;
  margin-top: -.25rem;
  border-right: 2px solid var(--color-muted);
  border-bottom: 2px solid var(--color-muted);
  transform: rotate(45deg);
  transition: transform .15s ease;
}

.ce-accordion__item[open] > .ce-accordion__summary::after {
  margin-top: .25rem;
  transform: rotate(-135deg);
}

.ce-accordion__summary:hover { color: var(--color-accent-strong); }

.ce-accordion__summary:focus-visible { outline-offset: -2px; }

.ce-accordion__body {
  padding: 0 1.25rem 1.25rem;
  color: var(--color-text-soft);
}

.ce-accordion__body > :last-child { margin-bottom: 0; }

/* --- spacer ------------------------------------------------------------ */

/* Added on top of the section gap. Renders nothing visible. */
.ce-spacer--s  { height: var(--space-3); }
.ce-spacer--m  { height: var(--space-5); }
.ce-spacer--l  { height: var(--space-6); }
.ce-spacer--xl { height: var(--space-7); }

/* --- page-teasers, and section lists ----------------------------------- */

/* section.ce-teasers > ul __list > li __item (shared with list.html and the
   news / categories bundles). White cards with a hairline in a responsive
   grid (columns of at least 16rem: two in the docs column, four on the
   home page); the title link is stretched over the whole card, a blue
   arrow after it nudges on hover. */
.ce-teasers__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(auto-fill, minmax(min(16rem, 100%), 1fr));
}

.ce-teasers__item {
  position: relative;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: .375rem;
  padding: 1.125rem 1.25rem 1.25rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  transition: border-color .15s ease, box-shadow .15s ease;
}

.ce-teasers__item:hover {
  border-color: var(--color-accent-border);
  box-shadow: var(--shadow);
}

.ce-teasers__title {
  font-size: var(--text-h4);
  font-weight: 600;
  line-height: 1.4;
  margin: 0;
}

.ce-teasers__title::after {
  content: "\2192";
  content: "\2192" / "";
  display: inline-block;
  margin-left: .375em;
  color: var(--color-accent);
  transition: transform .15s ease;
}

.ce-teasers__item:hover .ce-teasers__title::after { transform: translateX(3px); }

.ce-teasers__link {
  color: var(--color-text);
  text-decoration: none;
}

.ce-teasers__link::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}

.ce-teasers__item:hover .ce-teasers__link { color: var(--color-accent-strong); }

.ce-teasers__link:focus-visible { outline: none; }

.ce-teasers__link:focus-visible::after {
  outline: 2px solid var(--color-focus, var(--color-accent));
  outline-offset: 2px;
}

.ce-teasers__summary {
  margin: 0;
  color: var(--color-text-soft);
  font-size: .9375rem;
  line-height: 1.55;
}

/* --- cta --------------------------------------------------------------- */

/* An ink panel inside the column (the editor's chrome colour): heading
   (h3) in white, body in light zinc, buttons green / white-outlined. */
.ce-cta {
  max-width: var(--measure);
  padding: var(--space-5) clamp(var(--space-4), 5vw, var(--space-5));
  background: var(--color-dark);
  color: var(--color-dark-text);
  border-radius: var(--radius-lg);
}

.ce-cta__heading {
  font-size: var(--text-h2);
  color: var(--color-dark-strong);
  margin-bottom: var(--space-2);
}

.ce-cta__body > :last-child { margin-bottom: 0; }


/* --- ce-form (deckle-hosted's hosted-contact-form) -------------------- */

/* deckle ships no contact form. These ce-form* classes are the contract of
   deckle-hosted's hosted-contact-form element (README "Hosted contact
   form") and must survive a redesign. A zinc-50 panel with a hairline.
   Inputs are 16px so phones do not zoom on focus; their zinc-500 border is
   4.83:1 on white; focus is the blue ring with the border turning blue. */
.ce-form {
  max-width: var(--measure);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
}

.ce-form__heading {
  font-size: var(--text-h2);
  margin-bottom: var(--space-4);
}

.ce-form__form {
  display: grid;
  gap: var(--space-3);
}

.ce-form__field { margin: 0; min-width: 0; }

.ce-form__label {
  display: block;
  font-weight: 600;
  font-size: .9375rem;
  margin-bottom: var(--space-1);
}

.ce-form__input,
.ce-form__textarea {
  display: block;
  width: 100%;
  font: inherit;
  font-size: 1rem;
  color: var(--color-text);
  background: var(--color-bg);
  border: 1px solid var(--color-control);
  border-radius: var(--radius);
  padding: .65em .85em;
}

.ce-form__input:focus-visible,
.ce-form__textarea:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 0;
  border-color: var(--color-accent);
}

.ce-form__textarea {
  resize: vertical;
  min-height: 8rem;
}

.ce-form__submit {
  justify-self: start;
  margin-top: var(--space-1);
}

@media (min-width: 48em) {
  .ce-form { padding: var(--space-5); }
  .ce-form__form { grid-template-columns: 1fr 1fr; }
  .ce-form__field:has(.ce-form__textarea),
  .ce-form__submit { grid-column: 1 / -1; }
}

/* --- html (developer raw HTML) ----------------------------------------- */

/* Emitted verbatim; only capped at the measure and scrolls if wider. */
.ce-html {
  overflow-x: auto;
  max-width: var(--measure);
}

/* ------------------------------------------------------- backend layouts */

/* The slot grids emitted by layouts/partials/backendlayouts/*.html. A
   layout partial and its rules here are edited together, like an element
   template and its rules. This block comes after the frames and elements
   on purpose: the slot-edge rules below must win over the frame's own
   padding. */

/* A slot's first and last frame drop the outer edge: the page header
   above and the pager or footer (or the footer band's hairline) below set
   those distances. */
.ce-layout__slot > .ce-frame:first-child { --frame-start: 0px; }

.ce-layout__slot > .ce-frame:last-child { --frame-end: 0px; }

.ce-layout__cols {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
  min-width: 0; /* long words must shrink, not overflow */
}

.ce-layout__slot--main,
.ce-layout__slot--sidebar { min-width: 0; }

/* The footer band: a section gap of padding, then a hairline with 2.5rem
   of air under it, drawn by a generated block so that no margin is
   involved. */
.ce-layout__slot--footer { padding-top: var(--section-gap); }

.ce-layout__slot--footer::before {
  content: "";
  display: block;
  box-sizing: content-box; /* the hairline on top of the 2.5rem, not inside it */
  height: var(--space-5);
  border-top: 1px solid var(--color-border);
}

/* The 3fr/1fr split is the colspan 3 + colspan 1 that two-col.yaml
   declares to the editor — keep them equal or the page module's grid will
   lie about the page. */
@media (min-width: 48em) {
  .ce-layout--two-col .ce-layout__cols { grid-template-columns: 3fr 1fr; }
}

/* Inside the columns a viewport bleed would cross the neighbouring
   column: a hero there is a contained, rounded panel (see hero). */
.ce-layout__cols .ce-frame--bleed { margin-inline: 0; }

/* The sidebar is one narrow column at every width: grids stay single
   column and lead headings step down so words do not break. */
.ce-layout__slot--sidebar :is(.ce-cards, .ce-images, .ce-hero--image) { grid-template-columns: 1fr; }

.ce-layout__slot--sidebar :is(.ce-hero__heading, .ce-cta__heading) { font-size: var(--text-h3); }

/* -------------------------------------------------------------- lightbox */

/* One shared <dialog>, built by assets/js/lightbox.js (close, prev, next,
   img) over an ink scrim. It fills the viewport as a flex box, so any empty
   area is the dialog itself (a click there closes it). Prev/next carry the
   hidden attribute when the group has one image. Controls are round 12%
   white buttons with white glyphs and a white focus ring. */
.ce-lightbox__dialog {
  padding: 0;
  border: 0;
  width: 100vw;
  height: 100vh;
  max-width: 100vw;
  max-height: 100vh;
  background: transparent;
  color: var(--color-dark-strong);
}

.ce-lightbox__dialog[open] {
  display: flex;
  align-items: center;
  justify-content: center;
}

.ce-lightbox__dialog::backdrop { background: rgb(var(--overlay) / .92); }

.ce-lightbox__img {
  display: block;
  max-width: min(94vw, 100rem);
  max-height: 88vh;
  width: auto;
  height: auto;
  border-radius: var(--radius-lg);
  box-shadow: 0 24px 64px -16px rgb(0 0 0 / .5);
}

.ce-lightbox__close,
.ce-lightbox__prev,
.ce-lightbox__next {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  background: rgb(var(--on-dark) / .12);
  color: var(--color-dark-strong);
  border: 0;
  border-radius: 999px;
  cursor: pointer;
  font: inherit;
  font-size: 1.5rem;
  line-height: 1;
}

.ce-lightbox__close:hover,
.ce-lightbox__prev:hover,
.ce-lightbox__next:hover {
  background: rgb(var(--on-dark) / .24);
  color: var(--color-dark-strong);
}

.ce-lightbox__close { top: var(--space-3); right: var(--space-3); }

.ce-lightbox__prev,
.ce-lightbox__next {
  top: 50%;
  transform: translateY(-50%);
}

.ce-lightbox__prev { left: var(--space-3); }

.ce-lightbox__next { right: var(--space-3); }

/* the display: flex above would otherwise override [hidden] */
.ce-lightbox__prev[hidden],
.ce-lightbox__next[hidden] { display: none; }

.ce-lightbox__close:focus-visible,
.ce-lightbox__prev:focus-visible,
.ce-lightbox__next:focus-visible {
  outline: 2px solid var(--color-dark-strong);
  outline-offset: 3px;
}

/* ------------------------------------------------------- feature bundles */

/* The news and categories bundles reuse the teaser markup above and
   inherit the tokens; these few rules only lay out their extra parts. */
.ce-news-list__image,
.news-item__image {
  width: 100%;
  object-fit: cover;
  border-radius: var(--radius-lg);
}

.news-item__media { margin: 0 0 var(--space-5); }

:is(.ce-news-list__date, .news-item__date, .news-item__author) {
  display: block;
  color: var(--color-muted);
  font-size: var(--text-sm);
}

:is(.ce-news-list__categories, .news-item__categories) {
  position: relative; /* category links sit above a stretched teaser link */
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  font-size: var(--text-sm);
}

/* The categories overview (/categories/): one row per category, its
   count in a zinc chip. */
.categories {
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: var(--measure);
  border-top: 1px solid var(--color-border);
}

.categories:not(:has(li)) { border-top: 0; }

.categories__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: .75rem 0;
  border-bottom: 1px solid var(--color-border);
}

.categories__count {
  min-width: 2rem;
  padding: .1em .55em;
  border-radius: 999px;
  background: var(--color-surface-2);
  color: var(--color-text-soft);
  font-size: var(--text-sm);
  font-weight: 600;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin-top: var(--space-5);
}

.pager__pages {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}

.pager__number,
.pager__link {
  display: inline-block;
  min-width: 2.75rem;
  padding: .6em .8em;
  text-align: center;
  border-radius: var(--radius);
  text-decoration: none;
}

.pager__number:hover,
a.pager__link:hover { background: var(--color-surface-2); }

.pager__number[aria-current="page"] {
  color: var(--color-accent-strong);
  background: var(--color-accent-tint);
  font-weight: 600;
}

.pager__link[aria-disabled="true"] { color: var(--color-muted); }

/* --------------------------------------------------------- reduced motion */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition: none !important;
  }
  .ce-video__consent:is(:hover, :focus-visible) .ce-video__play,
  .ce-teasers__item:hover .ce-teasers__title::after { transform: none; }
}
