/* sparsr.com — palette carried over from the WordPress Extendable theme:
   background #17181A, foreground #FFFCFC, secondary #C4C4C4, primary #FFFFFF.
   Deliberately monochrome, as the original was.

   --accent is the one hook for a brand colour. It defaults to --fg, so the
   site stays exactly as monochrome as it is today and setting it is a
   one-line, fully reversible change. Everything that would carry a brand
   colour — primary buttons, the logo mark, focus rings, the featured pricing
   tier, list ticks, the step numerals — already reads from it. Choosing an
   actual colour is a brand decision, not a CSS one, so none is committed
   here. */

:root {
  --bg: #17181A;
  --surface: #1E2023;
  --surface-2: #232629;
  --fg: #FFFCFC;
  --muted: #C4C4C4;
  --border: rgba(255, 252, 252, 0.14);
  --border-strong: rgba(255, 252, 252, 0.28);

  --accent: var(--fg);
  --accent-contrast: var(--bg);
  --accent-soft: rgba(255, 252, 252, 0.08);

  --wrap: 1200px;
  --wrap-narrow: 760px;
  /* Width of the content column. Everything in a prose page — headings, body
     text, lists and images — shares this width so they align on one edge.
     Letting images run to the full 1200px wrap while text stayed narrow made
     the images dominate the page. Tune here; nothing else needs changing. */
  --measure: 90ch;
  --radius: 10px;
  --gap: 1.5rem;
  --section-y: clamp(3.5rem, 8vw, 6rem);

  /* No webfont downloads. The WordPress theme pulled ~30 font families on
     every page; Inter is used when the visitor already has it, otherwise the
     platform UI face. */
  --font: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* The header is sticky and about 4.25rem tall, so an in-page jump used to
     land with the target's first 69px underneath it. That hit the skip link
     (#main) and the partnership form's own anchor. */
  scroll-padding-top: 5.5rem;
  /* Tells the browser this is a dark page, so native controls it draws for
     us — the GDPR checkbox, scrollbars, form autofill — come out dark
     instead of as a bright white square in the middle of the form. */
  color-scheme: dark;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font);
  font-size: 1rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; display: block; }

h1, h2, h3, h4 { line-height: 1.2; margin: 0 0 0.5em; font-weight: 650; letter-spacing: -0.015em; }
h1 { font-size: clamp(2rem, 5vw, 3.25rem); }
h2 { font-size: clamp(1.6rem, 3.4vw, 2.4rem); }
h3 { font-size: 1.15rem; }
p { margin: 0 0 1rem; }
p:last-child { margin-bottom: 0; }

a { color: var(--fg); text-decoration-color: var(--border-strong); text-underline-offset: 0.2em; }
a:hover { text-decoration-color: currentColor; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 3px; }

.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: clamp(1rem, 4vw, 2rem); }
.wrap.narrow { max-width: var(--wrap-narrow); }

.lead { color: var(--muted); font-size: clamp(1.05rem, 1.6vw, 1.2rem); }
.section-heading { text-align: center; max-width: 22ch; margin-inline: auto; }
.section-lead { text-align: center; max-width: 65ch; margin: 0 auto 2.5rem; }
.section-actions { text-align: center; margin-top: 2.5rem; }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--fg); color: var(--bg); padding: 0.75rem 1.25rem; border-radius: 0 0 var(--radius) 0;
}
.skip-link:focus { left: 0; }

/* Off-screen for the eye, present for a screen reader, and still focusable.
   Used for the mobile menu button's name. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

/* ---------- buttons ---------- */

.btn {
  display: inline-block;
  padding: 0.8rem 1.5rem;
  border-radius: var(--radius);
  font-weight: 600;
  text-decoration: none;
  border: 1px solid transparent;
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.btn-primary { background: var(--accent); color: var(--accent-contrast); }
.btn-primary:hover { filter: brightness(1.12); }
.btn-secondary { background: transparent; color: var(--fg); border-color: var(--border-strong); }
.btn-secondary:hover { border-color: var(--fg); background: rgba(255, 252, 252, 0.06); }

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

.site-header {
  position: sticky; top: 0; z-index: 50;
  background: rgba(23, 24, 26, 0.85);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}
.header-inner { display: flex; align-items: center; justify-content: space-between; gap: 1rem; min-height: 4.25rem; }

.brand { display: inline-flex; align-items: center; text-decoration: none; color: var(--fg); }
/* The logo is one piece — mark plus wordmark, drawn as outlines — so the
   header no longer sets the name as live text. Height drives it and the width
   follows: the lockup is 8.5 times wider than it is tall, which is the one
   practical cost of an intentionally overwide mark. */
.site-logo { height: 1.5rem; width: auto; flex: none; color: var(--accent); }

.site-nav ul { display: flex; align-items: center; gap: 0.35rem; list-style: none; margin: 0; padding: 0; }
.site-nav a {
  display: block; padding: 0.5rem 0.6rem; border-radius: 8px;
  text-decoration: none; color: var(--muted); font-weight: 500; font-size: 0.9rem;
  /* A two-word item — "Kernel Study", "Developer Zone" — broke across two
     lines the moment the row ran out of room, which made the bordered
     external links twice as tall as everything beside them. The bar has to
     stay one line: if it stops fitting, the fix is the breakpoint below, not
     a wrapped word. */
  white-space: nowrap;
}
.site-nav a:hover { color: var(--fg); background: rgba(255, 252, 252, 0.07); }
.site-nav a[aria-current="page"] { color: var(--fg); }
.site-nav .nav-external { border: 1px solid var(--border-strong); margin-left: 0.4rem; color: var(--fg); }
.site-nav .nav-external:hover { border-color: var(--fg); }

.nav-toggle, .nav-toggle-label { display: none; }

/* The hamburger takes over at 1140px, not at 940px. Eight page links plus two
   external ones need 1013px beside the logo, and below about 1077px that does
   not fit inside the 1200px wrap — so the old 940px breakpoint left a ~140px
   band where the bar wrapped and pushed the whole page sideways by 22px.
   1140px rather than 1080px leaves 64px of slack at the narrowest desktop
   width, which is the room a future menu item gets before this moves again. */
@media (max-width: 1140px) {
  /* Focusable, not `display: none` and not `hidden`: this checkbox is the
     menu's only control, and both of those took it out of the tab order. */
  .nav-toggle {
    display: block; position: absolute;
    width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  .nav-toggle-label {
    display: flex; align-items: center; justify-content: center;
    width: 2.75rem; height: 2.75rem; padding: 0.7rem; cursor: pointer;
    border: 1px solid var(--border); border-radius: 8px;
  }
  .nav-toggle-bars { display: flex; flex-direction: column; justify-content: center; gap: 5px; width: 100%; }
  .nav-toggle-bars span { display: block; height: 2px; background: var(--fg); border-radius: 2px; }
  .nav-toggle:focus-visible ~ .nav-toggle-label { outline: 2px solid var(--accent); outline-offset: 3px; }
  .site-nav {
    display: none; order: 3; width: 100%;
    border-top: 1px solid var(--border); padding: 0.75rem 0 1rem;
  }
  .nav-toggle:checked ~ .site-nav { display: block; }
  .site-nav ul { flex-direction: column; align-items: stretch; gap: 0.15rem; }
  .site-nav a { padding: 0.7rem 0.5rem; font-size: 1rem; white-space: normal; }
  .site-nav .nav-external { margin-left: 0; text-align: center; margin-top: 0.5rem; }
  .header-inner { flex-wrap: wrap; }
}

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

.hero { position: relative; padding-block: clamp(4rem, 11vw, 8rem); overflow: hidden; }
.hero-bg { position: absolute; inset: 0; z-index: 0; }
.hero-bg img { width: 100%; height: 100%; object-fit: cover; opacity: 0.28; }
.hero.has-image::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(23, 24, 26, 0.55) 0%, rgba(23, 24, 26, 0.9) 100%);
}
.hero-inner { position: relative; z-index: 2; max-width: 62ch; }
.hero-title { margin-bottom: 1rem; }
.hero-lead { color: var(--muted); font-size: clamp(1.1rem, 2vw, 1.3rem); margin-bottom: 2rem; }
.hero-actions { margin: 0; }

/* ---------- page title band ---------- */

.page-title-band { border-bottom: 1px solid var(--border); padding-block: clamp(2.5rem, 6vw, 4.5rem); background: var(--surface); }
.page-title-band h1 { margin: 0; }

/* ---------- embedded demo app (`embed:` in a page's frontmatter) ---------- */

/* The demo applications sit inside a window frame titled with the machine's name, so the
   claim the pages make in prose - a machine is running in your tab - is also made by the
   shape of the thing. SPR-349. No decorative controls: the dot is a state light. */
/* The window has to sit ABOVE the page, not flush with it. The first version matched the
   page's own background and read as a paragraph rather than a machine. A stronger edge and a
   shadow give it a plane of its own; the interior colour comes from the demo's own stylesheet,
   which now starts one step up from --bg for the same reason. */
.vm-window {
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .35), 0 12px 32px rgba(0, 0, 0, .45);
}
.vm-titlebar { display: flex; align-items: center; gap: .6rem; padding: .55rem .9rem; background: var(--surface-2); border-bottom: 1px solid var(--border); font-size: .8rem; }
.vm-light { width: .5rem; height: .5rem; border-radius: 50%; flex: none; background: var(--muted); }
.vm-light[data-vm-state="starting"] { background: var(--muted); animation: vm-pulse 1.2s ease-in-out infinite; }
.vm-light[data-vm-state="ready"] { background: #6FD08C; animation: none; }
@keyframes vm-pulse { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .vm-light[data-vm-state="starting"] { animation: none; } }
.vm-name { font-weight: 600; letter-spacing: .01em; }
.vm-state { color: var(--muted); font-variant-numeric: tabular-nums; }
.vm-note { color: var(--muted); }
@media (max-width: 620px) { .vm-note { display: none; } }

.embed-frame { display: block; width: 100%; border: 0; background: var(--surface); }

/* A hairline under the title bar, so the bar reads as chrome and the area below as content. */
.vm-titlebar { box-shadow: inset 0 -1px 0 var(--border); }
.embed-fallback { color: var(--muted); }

/* The answer to "so how do I run the real thing?", under the VM window. */
.vm-elsewhere { margin: .8rem 0 0; font-size: .875rem; color: var(--muted); }

/* The scientific field, above the page title. SPR-348. */
.page-field { margin: 0 0 .4rem; font-size: .78rem; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.page-field a { color: inherit; text-decoration-color: var(--border-strong); text-underline-offset: .25em; }
.page-field a:hover { color: var(--fg); text-decoration-color: currentColor; }

/* ---------- generic sections ---------- */

section { padding-block: var(--section-y); }
.band-muted { background: var(--surface); }

.band-inner { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 5vw, 4rem); align-items: center; }
.band-flip .band-media { order: -1; }
.band-media img { border-radius: var(--radius); border: 1px solid var(--border); }
.band-actions { margin-top: 1.75rem; }
@media (max-width: 820px) {
  .band-inner { grid-template-columns: 1fr; }
  .band-flip .band-media { order: 0; }
}

/* ---------- stats ---------- */

.stat-grid, .card-grid, .step-grid, .tier-grid { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--gap); }
.stat-grid { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.stat {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 2rem 1.75rem; text-align: center;
}
.stat-value { font-size: clamp(2.4rem, 5vw, 3.4rem); font-weight: 700; letter-spacing: -0.03em; margin: 0 0 0.25rem; line-height: 1; }
.stat-label { margin: 0 0 0.75rem; font-size: 1rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600; }
.stat-text { color: var(--muted); font-size: 0.95rem; margin: 0; }

/* ---------- cards ---------- */

/* cols-2 is stated exactly rather than as an auto-fit minmax: sections that
   declare `columns: 2` carry two or four items, and an auto-fit track large
   enough for a two-up row still fits three of them across the 1180px wrap,
   which would put a four-item section on a 3 + 1 grid. Until this rule
   existed there was no cols-2 at all, so every such section fell back to the
   grid's single implicit column and stacked full width. */
.card-grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
.card-grid.cols-3 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.card-grid.cols-4 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
@media (max-width: 820px) { .card-grid.cols-2 { grid-template-columns: 1fr; } }
.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden;
}
.card-body { padding: 1.75rem; }
.card-image, .step-image, .tier-image {
  width: 100%; aspect-ratio: 3 / 2; object-fit: cover;
  border-bottom: 1px solid var(--border);
}
.card-title { margin-bottom: 0.5rem; }
.card p { color: var(--muted); margin: 0; font-size: 0.97rem; }

/* Optional lines a card item can carry, used by /team. `role` sits directly
   under the name as a label rather than as the first words of the sentence;
   `meta` is the credential chain, set apart from the body so it scans as a
   list without being marked up as one. */
.card .card-role {
  margin: -0.25rem 0 0.75rem; font-size: 0.82rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--accent);
}
.card .card-meta {
  margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--border);
  font-size: 0.88rem; line-height: 1.6;
}
/* The label carries the tense for the whole line — "Before Sparsr" — so it is
   lifted out of the muted grey. A chain of employers with no qualifier reads
   as a list of current ones. */
.card .card-meta-label { color: var(--fg); font-weight: 600; margin-right: 0.35em; }

/* Team portraits (/team). Every other card image is a 3:2 full-bleed banner,
   which is wrong for a head-and-shoulders crop: in a two-column grid it would
   render the face about 560px wide. These are square 320x320 exports shown in
   a 160px slot, inset to the card padding rather than bleeding to its edges. */
#people .card-image {
  display: block; width: 160px; height: 160px; aspect-ratio: 1 / 1;
  margin: 1.75rem auto 0; border: 1px solid var(--border);
  border-radius: var(--radius);
}

/* ---------- steps ---------- */

.step-grid { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.step {
  position: relative; border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden; background: var(--surface);
}
.step-body { padding: 1.75rem; }
.step-title { margin-bottom: 0.5rem; }
.step p { color: var(--muted); margin: 0; font-size: 0.97rem; }

/* ---------- pricing ---------- */

.tier-grid { grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); align-items: start; }
.tier {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 2rem 1.75rem; height: 100%;
  display: flex; flex-direction: column;
}
.tier-featured { border-color: var(--accent); background: var(--surface-2); }
.tier-badge {
  display: inline-block; margin-bottom: 0.75rem; padding: 0.2rem 0.6rem;
  border-radius: 100px; background: var(--accent-soft); border: 1px solid var(--accent);
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
}
.tier-image { margin: -2rem -1.75rem 1.5rem; width: calc(100% + 3.5rem); border-radius: var(--radius) var(--radius) 0 0; }
.tier-name { font-size: 1.35rem; margin-bottom: 0.35rem; }
.tier-note { color: var(--muted); font-size: 0.85rem; margin: 0 0 0.75rem; font-family: var(--font-mono); }
.tier-text { color: var(--muted); font-size: 0.97rem; }
.tier-features { list-style: none; margin: 1.25rem 0 0; padding: 0; display: grid; gap: 0.6rem; }
.tier-features li { position: relative; padding-left: 1.6rem; font-size: 0.95rem; }
.tier-features li::before {
  content: ""; position: absolute; left: 0; top: 0.55em;
  width: 0.7rem; height: 0.38rem; border-left: 2px solid var(--accent); border-bottom: 2px solid var(--accent);
  transform: rotate(-45deg);
}
.tier-actions { margin-top: auto; padding-top: 1.75rem; }

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

.cta { background: var(--surface); border-block: 1px solid var(--border); }
.cta-inner { max-width: 62ch; margin-inline: auto; text-align: center; }
.cta-actions { margin-top: 1.75rem; }

/* ---------- prose ---------- */

.prose { max-width: var(--measure); }
.prose h1 { margin-bottom: 1rem; }
.prose h2 { margin-top: 3rem; }
.prose h3 { margin-top: 2rem; }
/* Images share the content column. Source images are 800px wide (the hero
   1280), so at this width they render at or below natural size, never
   upscaled. */
.prose img {
  width: 100%;
  max-width: 100%;
  height: auto;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  margin: 1.5rem 0;
}
/* Buttons in prose are opt-in via a trailing `{.btn}`-free convention: the
   page declares them as `actions` on a section instead. The previous rule
   promoted *any* paragraph whose only child was a link into a full-size
   button, so an ordinary reference on its own line silently became a CTA. */
.prose a { text-decoration-color: var(--border-strong); }
.prose ul, .prose ol { padding-left: 1.25rem; }
.prose li { margin-bottom: 0.4rem; }
.prose code { font-family: var(--font-mono); font-size: 0.9em; background: var(--surface-2); padding: 0.15em 0.4em; border-radius: 4px; }

/* The contact address is published as an image rather than as text, which is
   how WordPress had it — an anti-scraping measure. Ported as-is, but with the
   address in alt text so screen readers and copy-paste still work.

   It renders as part of its section rather than inside .prose, because the
   prose rule sets images to width:100% — which stretched this 198x24 PNG
   across the whole content column. */
.email-image { margin: 1.75rem 0 0; }
.email-inline { width: auto; max-width: 100%; margin-inline: auto; }

/* ---------- forms (Grav form plugin markup) ---------- */

.form-section .form-field { margin-bottom: 1.25rem; }
.form-section label { display: block; font-weight: 600; margin-bottom: 0.4rem; font-size: 0.95rem; }
/* Colour only — no `content`. Grav's form template already writes the marker
   as `<span class="required">*</span>`, so adding one here printed every
   required label as "Name * *". */
.form-section .required { color: var(--muted); font-weight: 400; }
.form-section input[type="text"],
.form-section input[type="email"],
.form-section textarea,
.form-section select {
  width: 100%; padding: 0.75rem 0.9rem;
  background: var(--surface); color: var(--fg);
  border: 1px solid var(--border-strong); border-radius: 8px;
  font: inherit;
}
.form-section textarea { min-height: 10rem; resize: vertical; }
.form-section input:focus, .form-section textarea:focus { border-color: var(--fg); outline: none; }
.form-section .checkboxes, .form-section .form-field-checkbox { display: flex; align-items: flex-start; gap: 0.6rem; }
.form-section .checkboxes label, .form-section .form-field-checkbox label { font-weight: 400; margin: 0; }
/* The GDPR consent box is the one control the browser draws itself. Left
   alone it is a small bright square in the middle of a dark form. */
.form-section input[type="checkbox"] {
  accent-color: var(--accent); width: 1.05rem; height: 1.05rem;
  margin: 0.3rem 0 0; flex: none; cursor: pointer;
}
.form-section .buttons { margin-top: 1.5rem; }
.form-section button, .form-section input[type="submit"] {
  padding: 0.8rem 1.75rem; border-radius: var(--radius);
  background: var(--accent); color: var(--accent-contrast); border: 0; font: inherit; font-weight: 600; cursor: pointer;
}
.form-section button:hover { filter: brightness(1.12); }
.form-section .form-messages, .form-section .alert { padding: 1rem; border-radius: var(--radius); border: 1px solid var(--border-strong); margin-bottom: 1.5rem; }
.form-section .form-errors { color: #ff8f8f; }

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

.site-footer { border-top: 1px solid var(--border); padding-top: var(--section-y); margin-top: 0; }
.footer-inner { display: grid; grid-template-columns: 2fr 1fr; gap: 2.5rem; padding-bottom: 2.5rem; }
.footer-brand .brand { margin-bottom: 1rem; }
.footer-tagline { color: var(--muted); max-width: 48ch; font-size: 0.95rem; }
.footer-nav ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.6rem; }
.footer-nav a { color: var(--muted); text-decoration: none; font-size: 0.95rem; }
.footer-nav a:hover { color: var(--fg); text-decoration: underline; }
.footer-legal { display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap; border-top: 1px solid var(--border); padding-block: 1.5rem; }
.footer-social { list-style: none; display: flex; gap: 1rem; margin: 0; padding: 0; }
.copyright { color: var(--muted); font-size: 0.9rem; margin: 0; }
@media (max-width: 700px) { .footer-inner { grid-template-columns: 1fr; } }

/* ---------- error ---------- */

.error-page { text-align: center; padding-block: clamp(5rem, 14vw, 9rem); }
.error-page h1 { font-size: clamp(4rem, 14vw, 8rem); margin-bottom: 0.25rem; }


/* ---------- section rhythm ---------- */

/* Consecutive sections that share a background would otherwise read as one
   long block; the muted band alternation does the separating. Sections carry
   their own vertical padding (var(--section-y)) from the `section` rule above. */
main > section + section { border-top: 1px solid transparent; }
main > section.band-muted + section.band-muted { padding-top: 0; }

/* Pulls the lead up under its heading. -1.5rem cancelled the heading's own
   0.5em bottom margin and then some, so the two boxes overlapped by ~5px and
   a two-line heading read as if the lead were a third line of it. */
.section-heading + .section-lead { margin-top: -0.5rem; }

/* ---------- cards: plain variant ---------- */

/* `features` sections use the card shell without an image. Without this the
   card looks unbalanced, because .card-body's padding was tuned to sit under
   a 3:2 image rather than to be the whole card. */
.card-plain .card-body { padding: 1.75rem; }
.card-plain { background: transparent; border-color: var(--border); }
.card-link { margin-top: 0.9rem !important; }
.card-link a { color: var(--fg); font-weight: 600; font-size: 0.95rem; }
/* Divider between links when a card carries several. Decorative, so it is
   aria-hidden in the template and must not be bold like the links it sits
   between. */
.card-link-sep { color: var(--muted); font-weight: 400; margin-inline: 0.55rem; }

/* ---------- steps: visible numbering ---------- */

/* The steps were numbered in the copy itself ("1) Quickly Write and Test
   Locally"), which put the ordinal inside the heading text and therefore into
   the document outline and any search snippet. The number is presentation, so
   it comes from the list instead. */
.step-grid { counter-reset: none; }
.step-number {
  margin: 0 0 0.5rem; font-family: var(--font-mono); font-size: 0.85rem;
  font-weight: 700; letter-spacing: 0.1em; color: var(--accent); opacity: 0.85;
}

/* ---------- band ---------- */

.band-text .lead { margin-bottom: 1.25rem; }
/* Both band images are now purpose-drawn SVG authored at exactly 4:3, so this
   crops nothing. It stays as the slot's contract: whatever goes here next gets
   the ratio the column was designed around, rather than dictating its own.
   Before, the two images were stock photography and one of them was portrait,
   which made the column two screens tall. */
.band-image { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }

/* ---------- tables ---------- */

/* Wide content scrolls inside its own container rather than making the page
   scroll horizontally on a phone. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.data-table { border-collapse: collapse; width: 100%; min-width: 34rem; font-size: 0.95rem; }
.data-table th, .data-table td { text-align: left; padding: 0.85rem 1rem; border-bottom: 1px solid var(--border); vertical-align: top; }
.data-table thead th { font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); border-bottom-color: var(--border-strong); }
.data-table tbody tr:last-child td { border-bottom: 0; }
.prose table { border-collapse: collapse; width: 100%; }
.prose th, .prose td { text-align: left; padding: 0.75rem 1rem; border-bottom: 1px solid var(--border); }

/* ---------- form band ---------- */

/* The form used to render inside the prose column immediately after the body,
   with no heading of its own. On /research-partnerships the page's own CTA
   links to it by anchor, so it needs to be an addressable section with a
   heading rather than an unlabelled block at the bottom of the article. */
.form-band { background: var(--surface); border-top: 1px solid var(--border); }
.form-band h2 { margin-bottom: 0.5rem; }
.form-band .lead { margin-bottom: 2rem; }

/* ---------- header: room for a longer menu ---------- */

/* The nav is eight page links plus two external ones, and the header shares
   the 1200px content wrap, so the bar has about 1136px to work with. The
   tighter tracking is now on `.site-nav a` for every desktop width rather
   than only inside a band: it used to stop at 1180px, and above that the row
   needed 1144px and wrapped instead — which is to say the menu was broken on
   every ordinary desktop screen and correct only on a narrow one.
   Measured budget above 1180px: 205px logo + 16px gap + 819px nav = 1040px. */
@media (min-width: 1141px) and (max-width: 1180px) {
  .site-logo { height: 1.3rem; }
}

/* Below the hamburger breakpoint the logo shares the bar with the toggle
   only, but a 205px logo still crowds a 360px phone. */
@media (max-width: 480px) {
  .site-logo { height: 1.15rem; }
}

/* A card another page links to by anchor. The offset keeps the card title clear of the
   header after the jump; the six live demos point their field label here (SPR-356). */
.card[id] { scroll-margin-top: 5rem; }
