/* SolidDev site styles (plain CSS, no frameworks): tokens, shell, pages,
   contact form. Palette, type scale, and the layout system are ratified
   brand law (brand/BRANDING.md §3–4, §6; layout ratified 2026-07-27 with
   this build). All color values live in the tokens block below — no raw
   hex or rgb() past it. */

:root {
  /* Ratified palette (BRANDING.md §3) */
  --cyan-light: #1CD7E8;    /* accent + CTA fill; NEVER text on light (1.7:1 fails) */
  --cyan-deep: #16B0C5;     /* hover/pressed partner; never text on light (2.6:1 fails) */
  --iris: #A78BFA;          /* alternate accent (ratified 2026-07-28); cyan law applies: never text on light (2.5:1), never a state signal, never replaces cyan in an existing component */
  --ink: #252936;           /* text on light (13.2:1+); dark surfaces */
  --off-white: #F9F9FA;     /* text on ink (13.8:1) */
  --surface-light: #F1F5F5; /* light page/panel ground */
  /* Semantic surfaces — ratified 2026-07-27 with dark mode (issue #5):
     ground/text/panel flip per scheme; component tokens reference these. */
  --ground: #F1F5F5;
  --text: var(--ink);
  --panel: #FFFFFF;
  /* Ratified 2026-07-27: card/panel ground — white is a sanctioned light ground (BRANDING §2 lockup table, §6) */
  --surface-card: #FFFFFF;

  /* Semantic state colors — ratified 2026-07-27 (issue #2). All pass AA as
     text on light grounds (5.0:1+); all fail on ink, so they never appear on
     ink bands (state text there is off-white). Info mints no hue: cyan-deep
     accent + ink text covers it, and cyan never signals state elsewhere. */
  --error: #B42318;
  --success: #147A44;
  --warning: #8A5300;         /* no consumer yet; reserved */
  --info: var(--cyan-deep);   /* accent only, never text on light */

  /* Ratified 2026-07-27: hairline divider — ink at 14%, quiet but visible on light */
  --hairline: rgba(37, 41, 54, 0.14);
  /* Ratified 2026-07-27: form-control border — ink at 55% (~3.4:1 on white) because the
     14% hairline fails the 3:1 non-text minimum for input boundaries */
  --border-control: rgba(37, 41, 54, 0.55);
  /* Ratified 2026-07-27: reserved elevation shadow (unused in Phase 1; borders preferred) */
  --shadow-1: 0 8px 24px rgba(37, 41, 54, 0.10);

  /* Ratified 2026-07-27: spacing scale — 8px base with a 4px half step */
  --space-half: 4px;
  --space-1: 8px;
  --space-2: 16px;
  --space-3: 24px;
  --space-4: 32px;
  --space-6: 48px;
  --space-8: 64px;
  --space-12: 96px;

  /* Ratified 2026-07-27: radii — 8px controls, 12px cards/panels */
  --radius-control: 8px;
  --radius-card: 12px;

  /* Ratified 2026-07-27: layout — 1120px container, fluid gutters, 68ch prose measure */
  --container-max: 1120px;
  --gutter: clamp(20px, 5vw, 40px);
  --measure: 68ch;

  /* Ratified 2026-07-27: focus ring — ink on light (13.2:1 beats the 3:1 non-text
     minimum); ink-surface sections override this to cyan-light (8.2:1) */
  --focus-ring: var(--ink);

  /* Band background accents — ratified 2026-07-27 (Paul's pick): cyan-light
     at low alpha for the ink-band glows; the footer adds a mark watermark. */
  --glow: rgba(28, 215, 232, 0.16);
  --glow-soft: rgba(28, 215, 232, 0.12);

  /* Band surfaces — ratified 2026-07-27: contrast moments invert per scheme
     (Paul's call). Light pages carry ink bands; dark pages carry light
     bands. Hover on a light band stays ink: cyan text on light is banned. */
  --band-ground: var(--ink);
  --band-text: var(--off-white);
  --band-focus: var(--cyan-light);
  --band-hover: var(--cyan-light);
  --footer-mark: url("/assets/img/logo-mark-reversed.png");

  /* Brand face — ratified 2026-07-27 (issue #3): Poppins, self-hosted
     (zero external requests). Matches the wordmark's letterforms. */
  --font-stack: "Poppins", system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

/* Poppins (OFL), latin subset, self-hosted from assets/fonts/poppins/ */
@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/poppins/poppins-400.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/fonts/poppins/poppins-500.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/fonts/poppins/poppins-600.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/poppins/poppins-700.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Dark mode — ratified and shipped 2026-07-27 (issue #5). Two tiers per the
   vps-default-page precedent: the media query follows the OS; the data-theme
   attributes exist for a future manual toggle and win over the media query.
   The ink-band sections keep their literal ink ground in both schemes; in
   dark they read as a slightly lifted band over the deeper ground. Dark
   semantic accents (--error/--success) are the lightened dark-tier variants
   (the light-tier hues fail contrast on the dark ground; BRANDING §3). */
:root { color-scheme: light; }

@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
    --ground: #171A21;      /* deepened ink; ink bands stay #252936 above it */
    --text: var(--off-white);
    --panel: #232835;
    --hairline: rgba(249, 249, 250, 0.14);
    --border-control: rgba(249, 249, 250, 0.55);
    --focus-ring: var(--cyan-light); /* 8.2:1 on dark grounds */
    --error: #F97066;       /* 4.7:1 on --ground */
    --success: #3FBF7F;     /* 7.0:1 on --ground */
      --band-ground: #959DAA;  /* dimmed light band: inverts against the dark ground without glaring (ink text 4.9:1) */
    --band-text: var(--ink);
    --band-focus: var(--ink);
    --band-hover: var(--ink);
    --footer-mark: url("/assets/img/logo-mark-light.png");
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --ground: #171A21;
  --text: var(--off-white);
  --panel: #232835;
  --hairline: rgba(249, 249, 250, 0.14);
  --border-control: rgba(249, 249, 250, 0.55);
  --focus-ring: var(--cyan-light);
  --error: #F97066;
  --success: #3FBF7F;
  --band-ground: #959DAA;
  --band-text: var(--ink);
  --band-focus: var(--ink);
  --band-hover: var(--ink);
  --footer-mark: url("/assets/img/logo-mark-light.png");
}
:root[data-theme="light"] {
  color-scheme: light;
  --ground: #F1F5F5;
  --text: var(--ink);
  --panel: #FFFFFF;
  --hairline: rgba(37, 41, 54, 0.14);
  --border-control: rgba(37, 41, 54, 0.55);
  --focus-ring: var(--ink);
  --error: #B42318;
  --success: #147A44;
  --band-ground: var(--ink);
  --band-text: var(--off-white);
  --band-focus: var(--cyan-light);
  --band-hover: var(--cyan-light);
  --footer-mark: url("/assets/img/logo-mark-reversed.png");
}

/* ---- Reset-lite ---- */
*, *::before, *::after { box-sizing: border-box; }
body, h1, h2, h3, h4, p, ul { margin: 0; }
img { display: block; max-width: 100%; height: auto; }
ul[class] { list-style: none; padding: 0; }

/* ---- Base typography (ratified scale: 18/1.7 body; 38/32/26/22 headings) ---- */
html { -webkit-text-size-adjust: 100%; }
body {
  font-family: var(--font-stack);
  font-size: 18px;
  line-height: 1.7;
  color: var(--text);
  background: var(--ground);
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
main { flex: 1; } /* pins the footer to the viewport bottom on short pages */

h1, h2, h3, h4 { line-height: 1.2; font-weight: 700; text-wrap: balance; }
h1 { font-size: clamp(30px, 5.5vw, 38px); } /* ratified 2026-07-27 (mobile downscale; 38 at desktop) */
h2 { font-size: 32px; }
h3 { font-size: 26px; }
h4 { font-size: 22px; }

/* Link treatment on light — ratified 2026-07-27: ink, always underlined (cyan text on
   light is banned by the contrast facts); hover thickens the underline */
a {
  color: var(--text);
  text-decoration: underline;
  text-decoration-thickness: 1.5px;
  text-underline-offset: 3px;
}
a:hover { text-decoration-thickness: 2.5px; }

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

/* ---- Layout ---- */
.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* ---- Skip link (ink chip; off-white on ink is 13.8:1) ---- */
.skip-link {
  position: absolute;
  left: var(--space-2);
  top: var(--space-2);
  z-index: 10;
  padding: var(--space-1) var(--space-2);
  background: var(--ink);
  color: var(--off-white);
  border-radius: var(--radius-control);
  font-size: 16px;
  font-weight: 600;
  text-decoration: none;
  transform: translateY(-300%);
}
.skip-link:focus { transform: none; }
.skip-link:focus-visible { outline-color: var(--cyan-light); } /* ring on ink chip */

/* ---- Header ---- */
.site-header {
  background: var(--ground);
  border-bottom: 1px solid var(--hairline);
  padding-block: var(--space-2);
}
.site-header-inner {
  display: flex;
  flex-wrap: wrap; /* nav drops below the lockup on narrow screens — no JS */
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2) var(--space-4);
}
/* flex:none + max-width:none protect the lockup's aspect: under flex squeeze
   the img reset's max-width:100% capped the width while the fixed height
   held, distorting/cropping the lockup on narrow screens. */
.site-logo-link { display: inline-block; line-height: 0; text-decoration: none; flex: none; }
.site-logo { height: 48px; width: auto; max-width: none; } /* renders 162px wide, above the 120px lockup minimum */

.site-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  list-style: none; /* the nav ul is classless, so the ul[class] reset skips it */
  padding: 0;
}
.site-nav a {
  display: inline-block;
  font-size: 17px; /* ratified nav size */
  font-weight: 500;
  text-decoration: none;
  padding: var(--space-1) 2px;
  border-bottom: 3px solid transparent;
}
.site-nav a:hover {
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 6px;
}
.site-nav a[aria-current="page"] {
  font-weight: 600;
  /* cyan bar is decorative accent; weight + aria-current carry the state */
  border-bottom-color: var(--cyan-light);
}

/* ---- Theme toggle (header nav): icon-only button, ships [hidden] until
   site.js claims it. The tiers decide which icon shows: moon = "go dark",
   sun = "go light"; data-theme rules outrank the OS media query. ---- */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  min-height: 44px; /* touch-target floor */
  padding: var(--space-1);
  background: none;
  border: 0;
  color: var(--text);
  cursor: pointer;
}
.theme-toggle[hidden] { display: none; }
.theme-toggle:hover { color: var(--cyan-deep); } /* icon is a shape, not text */
.theme-icon-sun { display: none; }
@media (prefers-color-scheme: dark) {
  .theme-icon-sun { display: block; }
  .theme-icon-moon { display: none; }
}
:root[data-theme="dark"] .theme-icon-sun { display: block; }
:root[data-theme="dark"] .theme-icon-moon { display: none; }
:root[data-theme="light"] .theme-icon-sun { display: none; }
:root[data-theme="light"] .theme-icon-moon { display: block; }

/* ---- CTA button ---- */
/* Ratified 2026-07-27: cyan-light fill + ink label (8.2:1 AA); hover deepens to
   cyan-deep (ink on cyan-deep is 5.6:1, still AA at 16px/600) */
.btn {
  display: inline-block;
  background: var(--cyan-light);
  color: var(--ink);
  font-family: var(--font-stack);
  font-size: 16px; /* ratified button size */
  font-weight: 600;
  line-height: 1.25;
  padding: 12px 24px; /* 44px rendered height: touch-target floor */
  border: 0;
  border-radius: var(--radius-control);
  text-decoration: none;
  cursor: pointer;
}
.btn:hover { background: var(--cyan-deep); }

/* ---- Hero (composed with .ink-band on every page: the BRANDING §6 "dark
   hero" contrast moment; the light header above carries the lockup) ---- */
.hero { padding-block: clamp(48px, 9vw, 96px); } /* ratified 2026-07-27 */
.breadcrumbs { margin-bottom: var(--space-2); }
.breadcrumbs ol {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-half) var(--space-1);
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 15px; /* meta size */
}
.breadcrumbs li + li::before {
  content: "/";      /* fallback for engines without alt-text content syntax */
  content: "/" / ""; /* empty alt keeps the slash out of the accessibility tree */
  margin-right: var(--space-1);
  opacity: 0.6;
}
.hero.ink-band { /* soft cyan light source, top right (Paul's hero-bg pick) */
  background-image: radial-gradient(720px 460px at 80% 15%, var(--glow), transparent 70%);
}
.hero h1 { max-width: 24ch; }
.hero .lede {
  margin-top: var(--space-3);
  max-width: var(--measure);
  font-size: 20px; /* subhead size from the role ladder */
  line-height: 1.6;
}
.hero-actions {
  margin-top: var(--space-4);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}

/* ---- Footer (ink surface; reversed lockup, off-white text; cube watermark
   + soft glow per Paul's band-background pick) ---- */
.site-footer {
  position: relative; /* anchors the watermark layer */
  overflow: hidden;
  background: var(--band-ground);
  background-image: radial-gradient(640px 400px at 82% 20%, var(--glow-soft), transparent 70%);
  color: var(--band-text);
  --focus-ring: var(--band-focus);
  padding-block: var(--space-6);
  margin-top: var(--space-8);
}
.site-footer::after { /* ghost of the reversed mark; decorative only */
  content: "";
  position: absolute;
  inset: 0;
  background: var(--footer-mark) no-repeat right 6% center / 300px auto;
  opacity: 0.08;
  pointer-events: none;
}
.site-footer-inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.footer-logo { height: 40px; width: auto; } /* renders 130px wide, above the 120px minimum */
.site-footer a { color: var(--band-text); }
.site-footer a:hover { color: var(--band-hover); }
.footer-line { font-size: 16px; }
.footer-legal { font-size: 15px; } /* meta size from the role ladder; above the 14px floor */
.footer-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  list-style: none;
  margin: 0;
  padding: 0;
}
.footer-nav a { font-size: 16px; }

/* ---- Phase 2 additions: interior sections, cards, prose, ink band ---- */

/* Ratified 2026-07-27: rhythm for stacked interior sections */
.section { padding-block: clamp(40px, 7vw, 72px); }
.section > .container > h2 { margin-bottom: var(--space-3); }
.section-lede { max-width: var(--measure); margin-bottom: var(--space-3); }
.section-follow { margin-top: var(--space-3); }

/* Cards — RATIFY item 11 direction, now specced: white panel on the
   surface-light ground, hairline border, 12px radius, no shadow. Card
   headings keep their real outline level (h2 or h3) but render at the
   role ladder's 20px component-title size; card body is 16px reading text. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-3);
}
.card {
  background: var(--panel);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-card);
  padding: var(--space-3);
}
/* What-we-do media cards (home page; photography ratified 2026-07-28):
   16:10 photo above the copy, mirroring the feature-shot's seam. */
.card-media { padding: 0; overflow: hidden; }
.card-media .card-shot {
  aspect-ratio: 16 / 10;
  background: var(--surface-light);
  border-bottom: 1px solid var(--hairline);
}
.card-media .card-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.card-media .card-body { padding: var(--space-3); }
.card :is(h2, h3),
.form-success h2 { font-size: 20px; line-height: 1.3; } /* component titles */
.card p,
.form-success p,
.post-list p { margin-top: var(--space-1); font-size: 16px; line-height: 1.6; }

/* ---- Featured work (Work page): white card, hairline frame, stretched link.
   Ratified 2026-07-27: hover border-firming + stretched-link focus ring ---- */
.feature-grid {
  /* mirrored by the sizes attr in lib/images.php card_grid_image() — change both together */
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-3);
}
.feature-card {
  position: relative; /* anchors the stretched link */
  background: var(--panel);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-card);
  overflow: hidden; /* clips the screenshot to the card radius */
}
.feature-card:hover { border-color: var(--border-control); }
.feature-shot {
  aspect-ratio: 16 / 10;
  background: var(--surface-light); /* neutral mat while the image loads */
  border-bottom: 1px solid var(--hairline); /* seam between client pixels and our card */
}
.feature-shot img { width: 100%; height: 100%; object-fit: cover; object-position: top; }
.feature-body { padding: var(--space-3); }
.feature-tag {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  font-size: 15px; /* meta size from the role ladder */
  font-weight: 600;
}
.feature-tag::before { /* two-tone pyramid, the logo cube's top face as it reads at header size; a shape, never cyan text (1.7:1 on light) */
  content: "";
  width: 16px;
  height: 9px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 18'%3E%3Cpath fill='%231CD7E8' d='M16 0 L0 18 L16 18 Z'/%3E%3Cpath fill='%2316B0C5' d='M16 0 L16 18 L32 18 Z'/%3E%3C/svg%3E");
  background-size: contain;
  background-repeat: no-repeat;
  flex: none;
}
.feature-card h3 { font-size: 20px; margin-top: var(--space-1); } /* component-title size */
.feature-card h3 a { text-decoration: none; }
.feature-card h3 a::after { content: ""; position: absolute; inset: 0; } /* whole card is the link */
.feature-card:hover h3 a { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 3px; }
.feature-card h3 a:focus-visible { outline: none; }
.feature-card h3 a:focus-visible::after { /* ring the card; inset so overflow:hidden can't clip it */
  outline: 2px solid var(--focus-ring);
  outline-offset: -2px;
  border-radius: var(--radius-card);
}
.feature-body p:last-child { margin-top: var(--space-1); font-size: 16px; line-height: 1.6; }

/* Disciplines (Work page) — an index under the featured grid, not more cards:
   hairline-ruled rows in a two-column flow, cyan square echoing .feature-tag */
.discipline-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(420px, 1fr)); /* two wide columns, not three ragged ones */
  column-gap: var(--space-8);
}
.discipline {
  padding-block: var(--space-3);
  border-top: 1px solid var(--hairline);
}
.discipline h3 {
  font-size: 20px; /* component-title size */
  line-height: 1.3;
}
.discipline p { margin-top: var(--space-1); font-size: 16px; line-height: 1.6; }

/* Service cards (Services page): the ratified card surface at a width that
   carries real copy — two columns at desktop, one on narrow screens.
   min(440px, 100%) keeps the track from overflowing below 440px. */
.service-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(440px, 100%), 1fr));
  gap: var(--space-3);
}
.service-card {
  background: var(--panel);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-card);
  padding: var(--space-3);
}
.service-num { font-size: 15px; font-weight: 600; } /* meta-size eyebrow */
.service-card h2 { font-size: 20px; line-height: 1.3; margin-top: var(--space-1); } /* component-title size */
.service-card p:not(.service-num) { font-size: 16px; line-height: 1.6; }
.service-card h2 + p { margin-top: var(--space-1); }
.service-card p + p { margin-top: var(--space-2); }
.service-more a { font-weight: 600; }

/* Prose pages (About) */
.prose { max-width: var(--measure); }
.prose p + p { margin-top: var(--space-2); }

/* Ink band — BRANDING §6 contrast moment (page heroes, home closing CTA) */
.ink-band {
  background: var(--band-ground);
  color: var(--band-text);
  --focus-ring: var(--band-focus); /* cyan on ink bands, ink on light bands */
}
.ink-band a { color: var(--band-text); }
.ink-band a:hover { color: var(--band-hover); }
.ink-band .btn,
.ink-band .btn:hover { color: var(--ink); } /* CTA label stays ink on cyan fill */

/* FAQ (service pages): stacked question/answer pairs, all visible, prose measure */
.faq { max-width: var(--measure); }
.faq h3 { font-size: 20px; line-height: 1.3; margin-top: var(--space-4); } /* component-title size */
.faq h3 + p { margin-top: var(--space-1); }

/* ---- Contact form ---- */

/* Off-screen utility (honeypot wrapper; the skip link keeps its own pattern) */
.visually-hidden {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.lead-form { max-width: 560px; } /* Ratified 2026-07-27: single-column form width */
.field { margin-top: var(--space-3); }
.field label {
  display: block;
  font-size: 16px;
  font-weight: 600;
  margin-bottom: var(--space-1);
}
.field-optional { font-weight: 400; font-size: 15px; } /* meta size from the role ladder */
.field input,
.field textarea {
  width: 100%;
  font-family: var(--font-stack);
  font-size: 16px; /* component reading text; also prevents iOS focus zoom */
  line-height: 1.5;
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--border-control);
  border-radius: var(--radius-control);
  padding: 12px 14px;
}
.field textarea { min-height: 160px; resize: vertical; }
.field input[aria-invalid="true"],
.field textarea[aria-invalid="true"] { border: 2px solid var(--error); }
.field-error {
  margin-top: var(--space-half);
  font-size: 15px;
  font-weight: 600;
  color: var(--error); /* 5.99:1 on surface-light, 6.57:1 on white */
}
.lead-form .btn { margin-top: var(--space-4); }
.lead-form .btn[disabled] { opacity: 0.6; cursor: default; }

.form-banner,
.form-success {
  max-width: 560px;
  background: var(--panel);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-card);
  padding: var(--space-3);
}
.form-banner { border-left: 4px solid var(--error); margin-bottom: var(--space-3); } /* error-only surface */
.form-success { border-left: 4px solid var(--success); } /* semantic accent; cyan never signals state */
.contact-alt { margin-top: var(--space-4); }

/* ---- Blog ---- */
.post-list { max-width: var(--measure); }
.post-list li + li {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--hairline);
}
.post-list :is(h2, h3) { font-size: 22px; line-height: 1.3; } /* h4-level title size (role ladder); h2 on /blog/, h3 inside related/next-reading modules */
.post-meta { margin-top: var(--space-half); font-size: 15px; } /* meta size */

/* Lead photo: a post's opening image, or a service page's intro art. Capped
   at the measure so the 1280px master keeps 2x headroom — lib/images.php
   lead_image() mirrors this width in its sizes attribute. */
.post-lead {
  max-width: var(--measure);
  margin: var(--space-6) 0 0;
  border-radius: var(--radius-card);
  overflow: hidden; /* clips the photo to the radius */
}
.post-lead img { width: 100%; height: auto; }
.section .post-lead { margin-top: var(--space-3); } /* inside a section the hero clearance is already spent */

.post-body { max-width: var(--measure); padding-block: var(--space-6); } /* top space clears the ink hero */
.post-body > * + * { margin-top: var(--space-2); }
.post-body h2 { margin-top: var(--space-6); }
.post-body h3 { margin-top: var(--space-4); }
.post-body ul, .post-body ol { padding-left: var(--space-3); }
.post-body li + li { margin-top: var(--space-half); }
.post-body code {
  font-family: ui-monospace, "Cascadia Mono", Menlo, Consolas, monospace;
  font-size: 16px;
  background: var(--panel);
  border: 1px solid var(--hairline);
  border-radius: 4px;
  padding: 1px 6px;
}
.post-body pre { /* ink surface moment: off-white on ink, 13.8:1 */
  background: var(--ink);
  color: var(--off-white);
  border-radius: var(--radius-control);
  padding: var(--space-2);
  overflow-x: auto;
}
.post-body pre code { background: none; border: 0; padding: 0; color: inherit; }

/* ---- Post figures: an image plus its caption inside the prose measure. ---- */
.post-figure { margin-top: var(--space-4); margin-inline: 0; } /* zero the UA figure indent */
.post-figure figcaption {
  margin-top: var(--space-2);
  font-size: 15px; /* meta size from the role ladder */
}

/* ---- Icon specimens: brand icons rendered at their true pixel size so the
   reader sees a 16px favicon as 16px. The plate stays light in both schemes
   because these are the light-background lockups (BRANDING §2), and the
   labels on it are therefore always ink. Never give these images a width,
   a max-width, or an object-fit: scaling them destroys what they show. ---- */
.icon-specimens {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-3) var(--space-6);
  width: fit-content; /* the plate wraps the specimens; dead space would read as part of the scale */
  max-width: 100%;
  padding: var(--space-3);
  background: var(--surface-light);
  border: 1px solid rgba(37, 41, 54, 0.14); /* the light-scheme hairline, since the plate never darkens */
  border-radius: var(--radius-card);
}
.icon-specimen { text-align: center; color: var(--ink); }
.icon-specimen img { margin: 0 auto; max-width: none; }
.icon-specimen b {
  display: block;
  margin-top: var(--space-2);
  font-size: 15px; /* meta size; the 14px floor is a hard brand minimum */
}
.icon-specimen span { display: block; font-size: 15px; }

/* ---- UI kit page scaffolding — /ui-kit only. Specimen chips and one-off
   sizes use inline styles in the page markup: the values are the content. */
.kit-kicker {
  font-size: 14px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: var(--space-1);
}
.kit-note { font-size: 15px; line-height: 1.6; margin-top: var(--space-1); max-width: var(--measure); }
.kit-swatch-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.kit-swatch-chip {
  height: 72px;
  border-radius: var(--radius-control);
  border: 1px solid var(--hairline);
  margin-bottom: var(--space-2);
}
.kit-logo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-3);
  margin-top: var(--space-3);
}
.kit-ground-light,
.kit-ground-ink,
.kit-ground-deep {
  padding: var(--space-4);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-card);
}
.kit-ground-light { background: #F1F5F5; } /* literal: stays light in dark scheme by design */
.kit-ground-ink { background: var(--ink); }
.kit-ground-deep { background: #171A21; } /* mirrors the dark --ground token in the tiers above — change together */
.kit-type-row { padding-block: var(--space-2); border-bottom: 1px solid var(--hairline); }
.kit-fake-h1 { /* mirrors h1 + .hero h1 — change together */
  font-size: clamp(30px, 5.5vw, 38px);
  line-height: 1.2;
  font-weight: 700;
  text-wrap: balance;
  max-width: 24ch;
}
.kit-state-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
  margin-top: var(--space-3);
}
.kit-state-hover .btn { background: var(--cyan-deep); } /* static specimen — mirrors .btn:hover; change together */
.kit-state-focus .btn { outline: 2px solid var(--focus-ring); outline-offset: 3px; } /* mirrors :focus-visible; change together */
.kit-space-bar { height: 12px; background: var(--cyan-light); border-radius: 2px; }

/* ---- Mobile header: burger menu ---- */

.menu-toggle { display: none; } /* desktop: full nav row, no toggle */

/* Ratified 2026-07-27: first (and only) breakpoint, 720px — below it the lockup plus
   five 17px links no longer share a row comfortably */
@media (max-width: 719px) {
  .site-logo { height: 40px; } /* renders 135px wide, above the 120px lockup minimum */

  .menu-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    min-height: 44px; /* touch-target floor */
    padding: var(--space-1) var(--space-2);
    font-family: var(--font-stack);
    font-size: 16px; /* ratified button size */
    font-weight: 600;
    color: var(--text);
    background: none;
    border: 1px solid var(--border-control);
    border-radius: var(--radius-control);
    cursor: pointer;
  }
  .menu-toggle[hidden] { display: none; } /* no-JS: nav falls back to wrap */

  .menu-toggle-icon span {
    display: block;
    width: 18px;
    height: 2px;
    background: currentColor; /* ink bars, no image */
  }
  .menu-toggle-icon span + span { margin-top: 4px; }

  /* Drawer: in-flow full-width panel on its own wrap row; opening pushes
     page content down (no overlay). Active only once JS claims the header. */
  .site-header.js-menu .site-nav { display: none; width: 100%; }
  .site-header.js-menu .site-nav.is-open {
    display: block;
    border-top: 1px solid var(--hairline);
    padding-top: var(--space-1);
  }
  .site-header.js-menu .site-nav ul { flex-direction: column; gap: 0; }
  .site-header.js-menu .site-nav a { display: block; } /* full-width 45px rows */
  .site-header.js-menu .site-nav li + li { border-top: 1px solid var(--hairline); }
}

/* ---- Intake questionnaire (/q/<token>) ---- */
/* A standalone private surface like the proposal gate: no site header, no nav,
   no footer. Unlike the gate it links THIS stylesheet instead of carrying its
   own token copy, so every color below resolves to a ratified semantic token
   and the two dark tiers already declared at :root apply to it unchanged. The
   only per-scheme declaration in this block is the lockup swap at the bottom,
   and it is written in all three tiers (media query, [data-theme="dark"],
   [data-theme="light"]) so the site's manual toggle still wins.

   THE ONE FIXED-VALUE EXCEPTION: a CTA's fill (#1CD7E8) and label (#252936)
   are brand constants, not theme tokens. .btn already names both, through
   --cyan-light and --ink, neither of which is redefined by either dark tier.
   Everything here that could lose one of them re-pins it by name:
   .intake-finish .btn keeps the ink label on the cyan fill (8.24:1), and
   .intake .btn:focus-visible draws its ring INSIDE the fill in ink, because
   --focus-ring becomes --cyan-light in dark and a cyan ring on a cyan button
   measures 1.00:1 — the proposal gate's invisible-CTA bug, one layer over. */

.intake {
  --intake-lockup: url("/assets/img/logo-horizontal-light@480w.png");
  width: 100%;
  max-width: calc(560px + var(--gutter) * 2); /* the ratified 560px form column plus its gutters */
  margin-inline: auto;
  padding-inline: var(--gutter);
  padding-bottom: var(--space-8);
  /* Radios and checkboxes: never cyan. #1CD7E8 on white is 1.76:1 and a
     control indicator is a non-text component needing 3:1. --text is ink on
     light (14.49:1 on the panel) and off-white on dark (13.99:1). */
  accent-color: var(--text);
}

/* ---- Head: lockup and progress ---- */
.intake-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-1) var(--space-3);
  padding-block: var(--space-3) var(--space-2);
  border-bottom: 1px solid var(--hairline);
}
/* The real lockup as an image, never the wordmark re-typeset. It rides a CSS
   token rather than a <picture media> source because a media source cannot see
   [data-theme]: on a dark-OS phone with the toggle forced to light, <picture>
   would still serve the reversed lockup onto a light ground (1.05:1). */
.intake-brand {
  flex: none;
  width: 129px;  /* the 480x164 lockup at 44px tall; above the 120px minimum */
  height: 44px;
  /* Both dimensions are explicit because this box has no in-flow content: its
     only child is the visually-hidden wordmark, which is positioned out of
     flow, so a width left to the content collapses to zero and the background
     lockup never paints. */
  background: var(--intake-lockup) no-repeat left center / auto 44px;
}
.intake-progress {
  font-size: 15px; /* meta size from the role ladder; above the 14px floor */
  line-height: 1.4;
}

/* ---- Save status: the element that makes it safe to close the tab ----
   Sticky at every width, not only on phones: it is the page's one answer to
   "did my typing survive", and a client scrolling a 7-question section must
   never have to hunt for it. min-height reserves two lines at this size, so
   the box does not resize when the wording changes length or wraps at 360px,
   and nothing below it moves. State is carried by the words; the color and the
   4px bar are the second signal, never the only one. */
.intake-status {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  min-height: 64px; /* 2 x (16px x 1.4) + 16px padding + 2px borders */
  margin-top: var(--space-2);
  padding: var(--space-1) var(--space-2);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.4;
  background: var(--panel); /* opaque: content scrolls under it */
  border: 1px solid var(--border-control); /* 3.49:1 light, 5.30:1 dark */
  border-left: 4px solid var(--text);
  border-radius: var(--radius-control);
}
/* A server-rendered banner reuses this shape. Only the live status line
   sticks: two elements pinned at top:0 overlap the moment the second one
   reaches the first. */
.intake-status ~ .intake-status { position: static; }
.intake-status.is-saved { color: var(--success); border-left-color: var(--success); }    /* 5.39:1 light, 6.29:1 dark */
.intake-status.is-saving { color: var(--text); border-left-color: var(--text); }         /* 14.49:1 light, 13.99:1 dark */
.intake-status.is-problem { color: var(--error); border-left-color: var(--error); }      /* 6.57:1 light, 5.28:1 dark */

/* ---- Section: one per screen ---- */
.intake-section { margin-top: var(--space-4); }
.intake-section h1 { margin-bottom: var(--space-2); }
.intake-section p + p { margin-top: var(--space-2); }

/* ---- One question ---- */
.intake-q {
  min-width: 0; /* a fieldset's intrinsic min-width otherwise overflows at 360px */
  margin: var(--space-3) 0 0;
  padding: var(--space-2);
  background: var(--panel);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-card);
}
/* The prompt. Grouped controls (choice, scale, confirm) use a real fieldset
   and legend; a single control uses a real <label for>, because a legend does
   not label a control. Both render identically. float is the reliable way to
   pull a legend out of the fieldset's border notch into normal flow. */
.intake-q legend,
.intake-q .intake-prompt {
  display: block;
  float: left;
  width: 100%;
  padding: 0;
  margin-bottom: var(--space-1);
  font-size: 18px; /* group-title size from the role ladder */
  font-weight: 600;
  line-height: 1.4;
}
.intake-q legend ~ *,
.intake-q .intake-prompt ~ * { clear: both; }

/* Help text separates from the prompt by size and weight, not by color: this
   brand has no ratified muted-text token, and a dimmed grey would be inventing
   one. --text on --panel is 14.49:1 light, 13.99:1 dark. */
.intake-help {
  margin: 0 0 var(--space-2);
  font-size: 16px;
  font-weight: 400;
  line-height: 1.6;
}

/* Text controls. Mirrors the ratified spec on .field input / .field textarea —
   change both together. One deliberate difference: 18px, not 16px. She writes
   paragraphs here on a phone, readability outranks consistency, and both sizes
   clear the 16px floor that stops iOS zooming on focus. */
.intake-q input:not([type="radio"]):not([type="checkbox"]),
.intake-q textarea {
  width: 100%;
  font-family: var(--font-stack);
  font-size: 18px;
  line-height: 1.5;
  color: var(--text);
  background: var(--panel);
  border: 1px solid var(--border-control);
  border-radius: var(--radius-control);
  padding: 12px 14px;
}
.intake-q textarea { min-height: 160px; resize: vertical; }

/* Choice: a stack of full-width tap rows. The native radio stays visible, so
   the control works with no JS, no :has(), and no custom paint. */
.intake-options {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.intake-option {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 48px; /* above the 44px touch floor */
  padding: var(--space-1) var(--space-2);
  font-size: 16px;
  line-height: 1.4;
  border: 1px solid var(--border-control);
  border-radius: var(--radius-control);
  cursor: pointer;
}
.intake-option input { flex: none; width: 22px; height: 22px; margin: 0; }

/* Scale: five labelled steps. At 360px the card's inner width is 288px, so a
   step is 54px wide and 64px tall — clear of the touch floor without a
   horizontal scroller. */
.intake-scale {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--space-half);
}
.intake-scale label {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-half);
  min-height: 64px;
  padding: var(--space-1) 2px;
  font-size: 16px;
  font-weight: 600;
  line-height: 1.2;
  border: 1px solid var(--border-control);
  border-radius: var(--radius-control);
  cursor: pointer;
}
.intake-scale input { width: 22px; height: 22px; margin: 0; }
.intake-scale-ends {
  display: flex;
  justify-content: space-between;
  gap: var(--space-2);
  margin-top: var(--space-half);
  font-size: 15px; /* meta size; the visible end labels repeat text the radios
                      already carry for screen readers, so the markup hides
                      this line from the accessibility tree */
}
/* Selected state. The inset ring reads as a 2px edge without a 1px border
   change reflowing the row; the tint is a mix of the text color into the
   panel, so it composites correctly in both schemes with no second rule
   (text on it: 12.50:1 light, 10.33:1 dark). Both are enhancements — the
   native radio dot is what actually reports the state. */
.intake-option:has(:checked),
.intake-scale label:has(:checked) {
  background: color-mix(in srgb, var(--text) 8%, var(--panel));
  box-shadow: inset 0 0 0 1px var(--text);
}

/* Confirm: one checkbox whose meaning comes from the legend above it. */
.intake-check {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  min-height: 44px;
  padding-block: var(--space-1);
  font-size: 16px;
  line-height: 1.5;
  cursor: pointer;
}
.intake-check input { flex: none; width: 24px; height: 24px; margin: 0; }

/* The optional "anything to add" field under a choice or a scale. */
.intake-note {
  margin-top: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px solid var(--hairline);
}
.intake-note label {
  display: block;
  margin-bottom: var(--space-1);
  font-size: 16px;
  font-weight: 600;
}
.intake-note textarea { min-height: 88px; }

/* ---- The quiet control chip: bordered, no fill, ink label. Used by the
   section links (which drop the prose underline the way .site-nav a does; the
   border is the affordance) and by the recovery button, so this surface has
   exactly one non-CTA control shape. Cyan is reserved for the two actions that
   matter, Save and Finish. NOT a ratified site-wide secondary button: if it
   wants to leave /q/, it needs Paul. ---- */
.intake-nav a,
.intake-recovered button {
  display: flex;
  align-items: center;
  min-height: 48px;
  padding: var(--space-1) var(--space-2);
  font-family: var(--font-stack);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.25;
  color: var(--text);
  background: none;
  text-decoration: none;
  border: 1px solid var(--border-control);
  border-radius: var(--radius-control);
  cursor: pointer;
}
.intake-nav {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  margin-top: var(--space-4);
}
.intake-nav a:hover { text-decoration: underline; text-decoration-thickness: 2px; }

/* A local backup the browser kept when a save failed, offered back inside the
   question it belongs to. It carries the error accent because the status line
   is in its is-problem state whenever this is on screen: one story, told the
   same way in both places. */
.intake-recovered {
  margin-top: var(--space-2);
  padding: var(--space-2);
  font-size: 16px;
  line-height: 1.6;
  border: 1px solid var(--hairline);
  border-left: 4px solid var(--error);
  border-radius: var(--radius-control);
}
.intake-recovered p { margin-bottom: var(--space-2); }
.intake-recovered button { width: 100%; justify-content: center; }

/* ---- Save ---- */
.intake-actions { margin-top: var(--space-4); }
.intake-actions .btn { width: 100%; }
.intake-actions .intake-help { margin: var(--space-2) 0 0; }

/* Never a cyan ring on a cyan button. --focus-ring is --ink on light grounds
   but --cyan-light in dark, and cyan on the cyan fill is 1.00:1. The ring is
   drawn inside the fill in ink instead: 8.24:1 on #1CD7E8 and 5.56:1 on the
   #16B0C5 hover, identical in both schemes because neither value flips. */
.intake .btn:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: -5px;
}

/* ---- Finish: a deliberate act, never a mis-click ----
   A literal ink surface in BOTH schemes rather than the --band-* tokens: those
   invert to #959DAA in dark, where the cyan fill measures 1.55:1 against the
   band and the one button she must not miss stops being findable as a shape.
   On ink it is 8.24:1. Same literal-surface precedent as .post-body pre. The
   block sits a --space-8 gap below Save, on its own ground, behind its own
   confirmation checkbox: three separations, not one. */
.intake-finish {
  margin-top: var(--space-8);
  padding: var(--space-3);
  background: var(--ink);
  color: var(--off-white); /* 13.77:1 */
  border-radius: var(--radius-card);
  --focus-ring: var(--cyan-light); /* an ink ring on an ink ground is 1.00:1 */
  accent-color: var(--off-white);  /* --text is ink in the light theme: invisible here */
  color-scheme: light;             /* keeps the native checkbox light-rendered, which is what reads on ink */
}
.intake-finish h2 { font-size: 20px; line-height: 1.3; } /* component-title size */
.intake-finish p { margin-top: var(--space-1); font-size: 16px; line-height: 1.6; }
.intake-finish .intake-check { margin-top: var(--space-2); }
.intake-finish .btn { margin-top: var(--space-2); width: 100%; }
.intake-finish .btn,
.intake-finish .btn:hover { color: var(--ink); } /* names the label: ink on the cyan fill, 8.24:1 */

/* ---- Thank-you screen ---- */
.intake-done {
  margin-top: var(--space-4);
  padding: var(--space-3);
  background: var(--panel);
  border: 1px solid var(--hairline);
  border-left: 4px solid var(--success); /* semantic accent bar, as .form-success; cyan never signals state */
  border-radius: var(--radius-card);
}
.intake-done p { margin-top: var(--space-2); }

/* ---- 720px and up: the site's one ratified breakpoint, taken from the top ---- */
@media (min-width: 720px) {
  .intake-q { padding: var(--space-3); }
  .intake-nav { flex-direction: row; justify-content: space-between; }
  .intake-actions .btn,
  .intake-finish .btn,
  .intake-recovered button { width: auto; }
}

/* Lockup per scheme, all three tiers, mirroring --footer-mark: the media query
   follows the OS, the data-theme rules outrank it so the manual toggle wins. */
@media (prefers-color-scheme: dark) {
  .intake { --intake-lockup: url("/assets/img/logo-horizontal-reversed@480w.png"); }
}
:root[data-theme="dark"] .intake { --intake-lockup: url("/assets/img/logo-horizontal-reversed@480w.png"); }
:root[data-theme="light"] .intake { --intake-lockup: url("/assets/img/logo-horizontal-light@480w.png"); }
