/*
  CALTEC theme — main stylesheet. Ported 1:1 from previews/preview.css
  (the Phase 1 pixel spec; re-ported whole 2026-07-17) with WordPress
  adaptations: the mobile menu opens via JS (assets/js/nav.js toggles
  .nav-open on the <nav>) instead of the previews' CSS checkbox, and a
  WordPress-specific block at the end covers menu markup, logo fallback,
  accessibility, and the admin bar.
  Design source: design/ (Lovable prototype design system).
  Mobile-first: base styles are the phone layout; @media (min-width: …)
  rules add the tablet/desktop enhancements.
  Fonts load from fonts.css (self-hosted, SIL OFL).
  NOTE: front-end colors use exact oklch values; the theme.json editor
  palette carries hex approximations of the same tokens — verify visual
  match during Phase 3 staging review.
*/

:root {
  --bg: oklch(0.985 0.002 90);
  --ink: oklch(0.18 0.005 260);
  --muted: oklch(0.955 0.004 260);
  --muted-fg: oklch(0.5 0.01 260);
  --safety: oklch(0.7 0.19 46);
  /* Same hue and chroma as --safety, lightness dropped so it can carry small
     text. --safety measures 2.74:1 on --bg and fails AA; this measures 5.21:1.
     Use it wherever the brand orange is wanted as TEXT rather than as a fill.
     Do not "simplify" it back to --safety — that is the failure BD ruled on. */
  --safety-deep: oklch(0.54 0.19 46);
  --border: oklch(0.18 0.005 260 / 0.1);
  --on-dark: oklch(0.985 0.002 90);
  --sans: "Inter", ui-sans-serif, system-ui, sans-serif;
  --mono: "Space Mono", ui-monospace, monospace;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
/* Smooth in-page scrolling, but not for anyone who has asked the OS for less
   motion. Pre-existing gap, not something the hero animation introduced: the
   homepage hero CTA links to /process/#occupied-job, so this already fired from
   a primary call to action. Guarded on no-preference for the same reason as the
   hero draw-on at the end of this file.
   section[id] scroll-margin-top below stays unconditional — that is
   positioning, not motion. The `transition: ... .15s` hover rules elsewhere
   also stay unguarded on purpose: reduced motion does not mean "no
   transitions", and a sub-200ms colour change on deliberate hover is not a
   vestibular trigger. Do not "complete" this pass by over-guarding those. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}
section[id] { scroll-margin-top: 4.5rem; }
/* Empty arrival anchors in page-contact.php (#intake-budget,
   #intake-capabilities), which the sitewide CTAs scroll to. They need the same
   offset as section[id] or the form heading lands underneath the sticky menu.
   Zero-size and out of flow so they never affect layout, only scroll position. */
.anchor-target { display: block; height: 0; scroll-margin-top: 4.5rem; }
body { background: var(--bg); color: var(--ink); font-family: var(--sans); line-height: 1.6; }
img { max-width: 100%; display: block; }
a { color: inherit; }
.wrap { max-width: 80rem; margin: 0 auto; padding-left: 1.5rem; padding-right: 1.5rem; }
section { border-bottom: 1px solid var(--border); }
.pad { padding-top: 3rem; padding-bottom: 3rem; }
@media (min-width: 768px) { .pad { padding-top: 4rem; padding-bottom: 4rem; } }

.eyebrow { font-family: var(--mono); font-size: .75rem; color: var(--safety);
  text-transform: uppercase; letter-spacing: .15em; display: flex; align-items: center;
  gap: .75rem; margin-bottom: .75rem; }
.eyebrow::after { content: ""; height: 1px; width: 2rem; background: var(--border); }
.mono-label { font-family: var(--mono); font-size: .625rem; text-transform: uppercase;
  letter-spacing: .12em; color: var(--muted-fg); }
h1, h2, h3 { letter-spacing: -0.02em; line-height: 1.1; text-wrap: balance; }
.lede { color: var(--muted-fg); max-width: 52ch; text-wrap: pretty; }

.btn { display: inline-block; background: var(--ink); color: var(--on-dark);
  font-size: .75rem; font-weight: 500; text-transform: uppercase; letter-spacing: .15em;
  padding: .9rem 1.5rem; text-decoration: none; transition: background .15s; }
/* Ink on orange, not white (Ardi 2026-07-26, BD's recommendation).
   THE ORANGE IS UNCHANGED — only the text sitting on it moves from white to
   ink. Measured from the actual tokens, not taken on trust:
   white on --safety = 2.74:1, which FAILS AA for text this size (.75rem
   uppercase is small text, so the threshold is 4.5:1).
   ink   on --safety = 6.57:1, passes.
   It also reads as more native to the brand: dark-on-safety-orange is the
   real convention of construction and traffic signage; white-on-orange is not.
   Every rule that puts text on --safety is covered below — miss one and the
   failure simply moves rather than being fixed. */
.btn:hover { background: var(--safety); color: var(--ink); }
/* Button hierarchy (adopted 2026-07-13):
   PRIMARY  .btn-primary — safety orange. ONE per screen: the single most
            important action. (.btn-safety = same rank on dark backgrounds.)
   SECONDARY .btn — ink. Next-most-important, 1–2 per screen.
   TERTIARY .btn-ghost / .btn-fill — low-stakes actions (download, view,
            back). Unlimited, but don't mix the two variants in one row.
            NEVER orange. */
.btn-primary { background: var(--safety); color: var(--ink); }
/* Hover flips the background to ink, so the text MUST flip back to light or
   it is ink-on-ink at 1.00:1 — invisible. */
.btn-primary:hover { background: var(--ink); color: var(--on-dark); }
.btn-safety { background: var(--safety); color: var(--ink); }
.btn-safety:hover { background: var(--bg); color: var(--ink); }
.btn-ghost { background: transparent; color: var(--ink); border: 1px solid var(--ink);
  padding: calc(.9rem - 1px) calc(1.5rem - 1px); }
.btn-ghost:hover { background: var(--muted); }
.btn-fill { background: var(--muted); color: var(--ink); }
.btn-fill:hover { background: oklch(0.18 0.005 260 / 0.1); }
/* legacy alias (pre-hierarchy outline) — same look as ghost */
.btn-outline { background: transparent; color: var(--ink); border: 1px solid var(--ink);
  padding: calc(.9rem - 1px) calc(1.5rem - 1px); }
.btn-outline:hover { background: var(--muted); }
.link-arrow { font-family: var(--mono); font-size: .75rem; text-transform: uppercase;
  letter-spacing: .15em; text-decoration: none; border-bottom: 1px solid var(--ink);
  padding-bottom: .2rem; }
/* Hover feedback rides the decorative underline; the text itself stays
   ink — small mono text in orange sits below AA contrast (BD contrast
   ruling 2026-07-25: orange never carries must-read text). */
.link-arrow:hover { border-color: var(--safety); }

/* Nav */
nav { border-bottom: 1px solid var(--border);
  background: oklch(0.985 0.002 90 / 0.85);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  position: sticky; top: 0; z-index: 40; }
.nav-inner { height: 4rem; display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.nav-left { display: flex; align-items: center; gap: 2.5rem; min-width: 0; }
/* Logo sizing. Owner 2026-07-27: "the logo is too small" and "on mobile there's
   no words on the top bar, only the CT logo and the menu icon."

   Both are the same root cause, and it is worth stating because the fix looks
   like a style tweak and is not. header.php renders the uploaded logo OR the
   .nav-logo-text wordmark, NEVER BOTH. So uploading a mark-only logo removes
   the company name from the header entirely — which is exactly what a visitor
   saw on a phone.

   The intended asset is the letterhead LOCKUP (CT mark + "CALTEC CORPORATION",
   1597x329, ratio 4.85:1), which carries the words itself. At the old 2.1rem a
   two-line lockup's text renders around 10px and is unreadable.

   ⚠ Sizing maths, so a later pass does not shrink it back on a hunch: at 2.6rem
   the lockup is ~202px wide; plus the menu button and the wrap's 1.5rem padding
   either side that is ~294px, which still fits a 320px viewport. Going much
   past 2.8rem on mobile starts crowding the menu button — re-measure first. */
.nav-logo img { height: 2.6rem; width: auto; }
@media (min-width: 900px) { .nav-logo img { height: 3rem; } }
.nav-links { display: none; gap: 1.5rem; }
.nav-links a { font-family: var(--mono); font-size: .75rem; text-transform: uppercase;
  letter-spacing: .12em; color: var(--muted-fg); text-decoration: none; }
.nav-links a:hover, .nav-links a.active { color: var(--ink); }
.nav-cta { padding: .65rem 1rem; }
@media (min-width: 900px) { .nav-links { display: flex; } }

/* Mobile menu (below 900px): hamburger toggles a full-width panel.
   assets/js/nav.js adds/removes .nav-open on the <nav> element. */
/* Burger = tertiary-ghost tier: ink outline, muted fill on hover/tap.
   Never orange (not a CTA), never solid black (would compete with nav CTA). */
.nav-burger { display: none; padding: 0; cursor: pointer; flex-direction: column; gap: 5px;
  align-items: center; justify-content: center; width: 2.75rem; height: 2.75rem;
  border: 1px solid var(--ink); background: transparent; flex-shrink: 0;
  transition: background .15s; }
.nav-burger:hover, nav.nav-open .nav-burger { background: var(--muted); }
.nav-burger span { display: block; width: 1.15rem; height: 2px; background: var(--ink); }
.nav-panel { display: none; border-top: 1px solid var(--border); background: var(--bg); }
.nav-panel a { display: block; padding: 1rem 1.5rem; border-bottom: 1px solid var(--border);
  font-family: var(--mono); font-size: .8rem; text-transform: uppercase;
  letter-spacing: .12em; color: var(--ink); text-decoration: none; }
.nav-panel a:active, .nav-panel a:hover { color: var(--safety); }
/* Panel CTA = the same primary ask, so the same primary orange (Ardi 2026-07-13). */
.nav-panel .nav-panel-cta { font-family: var(--sans); background: var(--safety);
  /* Ink on orange, same as .btn-primary (Ardi 2026-07-26). This is the mobile
     menu's primary ask, so it carried the identical 2.74:1 failure. */
  color: var(--ink); text-align: center; margin: 1rem 1.5rem; padding: .9rem 1rem;
  border-bottom: none; font-size: .75rem; font-weight: 500; letter-spacing: .15em; }
/* Hover flips to ink, so the text flips back to light. */
.nav-panel .nav-panel-cta:hover, .nav-panel .nav-panel-cta:active {
  background: var(--ink); color: var(--on-dark); }
nav.nav-open .nav-panel { display: block; }
@media (max-width: 899.98px) { .nav-burger { display: flex; } }
@media (min-width: 900px) { .nav-panel { display: none !important; } }
/* Phones: header CTA hides; the panel carries the full-label CTA instead. */
@media (max-width: 639.98px) { .nav-cta { display: none; } }

/* Orphan-cell fix: when the item count doesn't fill the last row, the final
   cell stretches to fill it (gray phantom areas otherwise). A bare
   "grid-column-end: -1" does NOT do this — with an auto start, CSS grid
   parks a one-column cell AT the end and leaves the hole BEFORE it (bug
   caught on staging 2026-07-17). "span N" from the natural position is the
   correct tool; the :nth-child guards make every rule a no-op when the
   item count divides evenly into rows. */
@media (min-width: 640px) and (max-width: 1023.98px) {
  .services-grid > .cell:last-child:nth-child(odd) { grid-column: span 2; }
}
@media (min-width: 1024px) {
  .services-grid > .cell:last-child:nth-child(3n + 1) { grid-column: 1 / -1; }
  .services-grid > .cell:last-child:nth-child(3n + 2) { grid-column: span 2; }
}
@media (min-width: 768px) {
  .markets-grid > .cell:last-child:nth-child(odd) { grid-column: span 2; }
}

/* Sticky index labels: on desktop, the numbered label column rides alongside
   long section bodies instead of scrolling away (Lovable P1 #7). */
@media (min-width: 900px) {
  .detail-row > div:first-child,
  .exec-item > div:first-child { position: sticky; top: 5.5rem; align-self: start; }
}

/* Photo placeholders read as intentional, not broken: thin inset frame
   around any media box still carrying a placeholder (Lovable P1 #8).
   Disappears automatically once a real image replaces the placeholder. */
.work-media { position: relative; }
.work-media:has(.placeholder)::after { content: ""; position: absolute; inset: 8px;
  border: 1px solid var(--border); pointer-events: none; }

/* Page header (interior pages) */
.page-head h1 { font-size: clamp(2rem, 4vw, 2.75rem); font-weight: 650; margin-bottom: 1rem; }
.page-head .lede { font-size: 1.1rem; }

/* Hero (home) */
.hero h1 { font-size: clamp(2.25rem, 6vw, 4.25rem); font-weight: 650; margin-bottom: 1.25rem; }
.hero .lede { font-size: 1.125rem; margin-bottom: 1.5rem; }
.hero-proof { font-weight: 700; max-width: 56ch; margin-bottom: 2.25rem; text-wrap: pretty; }
.hero-ctas { display: flex; flex-wrap: wrap; align-items: center; gap: 1.25rem; }
.hero-img { margin-top: 2.5rem; aspect-ratio: 21/9; overflow: hidden;
  box-shadow: 0 0 0 1px var(--border); }
.hero-img img { width: 100%; height: 100%; object-fit: cover; }

/* Trust strip — wrapping, all items always visible (no horizontal scroll) */
.trust { background: var(--muted); border-bottom: 1px solid var(--border); }
.trust-inner { display: flex; flex-wrap: wrap; justify-content: center; text-align: center;
  column-gap: 2rem; row-gap: .6rem; padding-top: 1rem; padding-bottom: 1rem; }
.trust-inner span { font-family: var(--mono); font-size: .75rem; text-transform: uppercase;
  letter-spacing: .04em; color: var(--muted-fg); }
/* Items stay on one line only where there's room; on phones they wrap
   internally — a single long item must never be wider than the screen. */
@media (min-width: 768px) { .trust-inner span { white-space: nowrap; } }
.trust-inner .dot { color: var(--safety); }

/* Card grids (1px gap trick from prototype) */
.grid-cells { display: grid; gap: 1px; background: var(--border); border: 1px solid var(--border); }
.cell { background: var(--bg); padding: 1.75rem; }
.cell h3 { font-size: 1.05rem; font-weight: 600; margin: 0 0 .5rem; }
.cell p { font-size: .95rem; color: var(--muted-fg); }
.tick { width: .4rem; height: .4rem; background: var(--safety); margin-bottom: 1rem; }
.services-grid { grid-template-columns: 1fr; }
@media (min-width: 640px) { .services-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .services-grid { grid-template-columns: repeat(3, 1fr); } }

.section-head { display: flex; justify-content: space-between; align-items: flex-end;
  gap: 1.5rem; margin-bottom: 2rem; flex-wrap: wrap; }
.section-head h2 { font-size: clamp(1.6rem, 3vw, 2.1rem); font-weight: 650; }
.section-head .lede { margin-top: 1rem; }

/* Markets */
.market-item h3 { font-size: 1.15rem; font-weight: 600; margin-bottom: .4rem; }
.market-item p { font-size: .95rem; color: var(--muted-fg); max-width: 44ch; }
.markets-grid { grid-template-columns: 1fr; }
@media (min-width: 768px) { .markets-grid { grid-template-columns: repeat(2, 1fr); } }
.market-item .link-arrow { font-size: .65rem; display: inline-block; margin-top: .75rem; }

/* Featured work (home) */
.work-grid { display: grid; gap: 2rem; grid-template-columns: 1fr; }
@media (min-width: 900px) { .work-grid { grid-template-columns: repeat(3, 1fr); } }
.work-card { border: 1px solid var(--border); background: var(--bg);
  display: flex; flex-direction: column; box-shadow: 0 1px 2px oklch(0 0 0 / 0.04); }
.work-media { aspect-ratio: 16/9; background: var(--muted); overflow: hidden;
  display: flex; align-items: center; justify-content: center; }
.work-media img { width: 100%; height: 100%; object-fit: cover; }
.work-media .placeholder { font-family: var(--mono); font-size: .625rem; color: var(--muted-fg);
  text-transform: uppercase; letter-spacing: .1em; text-align: center; padding: 1rem; white-space: normal; }
.work-body { padding: 1.5rem; flex-grow: 1; display: flex; flex-direction: column; }
.work-body h3 { font-size: 1.2rem; font-weight: 700; margin-bottom: .75rem; }
/* ⛔ `flex-grow: 1` WAS HERE AND IT WAS THE BUG. Removed 2026-07-28 after the
   owner reported "random white spaces" in the Medical Examiner and Palm Springs
   cards, and BD traced it to this line.
   `.work-body` is a flex column that grows to fill the card. With flex-grow on
   the paragraphs too, the card's spare height was distributed INSIDE each
   paragraph's box; the text sat at the top of its stretched box and the
   leftover appeared as a hole underneath. The Medical Examiner card has two
   paragraphs so it stretched twice, which is why its gap sat high, between the
   owner/department line and the body.
   With this gone the paragraphs take their natural height, top-align, and the
   spare space collects once at the BOTTOM of .work-body, immediately above the
   status strip, where it reads as padding. Do not put it back to "balance" a
   card: uneven card heights are the grid's job, not the paragraph's. */
.work-body p { font-size: .95rem; color: var(--muted-fg); }
.work-status { margin-top: 1rem; padding: 1rem 1.5rem; background: var(--muted);
  border-top: 1px solid var(--border); }
.work-status .bar { display: inline-block; width: .2rem; height: .8rem; background: var(--safety);
  margin-right: .5rem; vertical-align: middle; }
.work-status strong { font-size: .85rem; line-height: 1.4; display: inline; }
.work-kicker { font-weight: 700; margin-bottom: .75rem; }
/* Project names line on the Palm Springs card (BD 006, revised to four names).
   Deliberately inherits everything from `.work-body p` — same font, size and
   colour as the LACME card's owner/department line, which is the treatment BD
   asked to match. Only the spacing above it is set here.
   The `flex-grow: 0` this rule used to carry is GONE, because the flex-grow it
   was overriding is gone: see the note on `.work-body p` above. It was a
   correct override of a rule that should not have existed.
   ⚠️ The four-name line WRAPS TO THREE LINES and that is accepted, ruled by BD
   in 012 reversing their own shortening instruction: the cards are already
   equal height, so the wrap breaks nothing and shortening would delete a real
   contract name to fix a problem that does not occur. */
.work-body p.work-projects { margin-top: .75rem; }

/* Detail rows (Services + Markets interior pages):
   mono index + heading left, body right on desktop; stacked on phone */
.detail-row { padding-top: 2rem; padding-bottom: 2rem; border-top: 1px solid var(--border);
  display: grid; gap: 1rem; grid-template-columns: 1fr; }
@media (min-width: 900px) { .detail-row { grid-template-columns: 3fr 9fr; gap: 2.5rem; } }
.detail-row:first-of-type { border-top: none; }
.detail-row:last-of-type { padding-bottom: 6rem; }
.detail-row .idx { font-family: var(--mono); font-size: 1.75rem; font-weight: 700;
  color: var(--safety); line-height: 1; display: block; margin-bottom: .75rem; }
.detail-row h2 { font-size: clamp(1.35rem, 2.5vw, 1.75rem); font-weight: 650; }
.detail-row .body p { color: var(--muted-fg); max-width: 62ch; text-wrap: pretty;
  font-size: 1.05rem; }
.detail-row .body p + p { margin-top: 1rem; }
.detail-row .body strong { color: var(--ink); }

/* Process page — prototype-style sections (split header, step grid, numbered list) */
.split { display: grid; gap: 2rem; grid-template-columns: 1fr; }
@media (min-width: 1024px) { .split { grid-template-columns: 5fr 7fr; gap: 3rem; } }
.split h2 { font-size: clamp(1.6rem, 3vw, 2.1rem); font-weight: 650; }
.split .split-body { color: var(--muted-fg); font-size: 1.1rem; line-height: 1.7;
  max-width: 62ch; text-wrap: pretty; }

.step-grid { display: grid; gap: 1px; background: var(--border); border: 1px solid var(--border);
  grid-template-columns: 1fr; }
@media (min-width: 768px) { .step-grid { grid-template-columns: repeat(3, 1fr); } }
.step-cell { background: var(--bg); padding: 2rem 2.25rem; display: flex; flex-direction: column;
  gap: 1.5rem; min-height: 240px; }
.step-cell-head { display: flex; align-items: center; justify-content: space-between; }
.step-num { font-family: var(--mono); font-size: 2.25rem; font-weight: 700;
  color: var(--safety); line-height: 1; }
.step-count { font-family: var(--mono); font-size: .625rem; text-transform: uppercase;
  letter-spacing: .15em; color: var(--muted-fg); }
.step-cell h3 { font-size: 1.25rem; font-weight: 650; margin-bottom: .6rem; }
.step-cell p { font-size: .9rem; color: var(--muted-fg); max-width: 36ch; }

.exec-list { list-style: none; border-top: 1px solid var(--border); }
.exec-item { display: grid; gap: 1.25rem; grid-template-columns: 1fr;
  border-bottom: 1px solid var(--border); padding: 2.5rem 0; }
@media (min-width: 768px) { .exec-item { grid-template-columns: 3fr 9fr; gap: 2.5rem;
  padding: 3rem 0; } }
.exec-label { font-family: var(--mono); font-size: .7rem; text-transform: uppercase;
  letter-spacing: .15em; color: var(--muted-fg); display: block; margin-bottom: .5rem; }
.exec-num { font-family: var(--mono); font-size: clamp(3rem, 5vw, 3.75rem); font-weight: 700;
  color: var(--safety); line-height: 1; display: block; }
.exec-item h3 { font-size: clamp(1.4rem, 2.5vw, 1.8rem); font-weight: 650;
  margin-bottom: .9rem; }
.exec-item .exec-body { color: var(--muted-fg); font-size: 1.1rem; line-height: 1.7;
  max-width: 62ch; text-wrap: pretty; }
.exec-item .exec-body strong { color: var(--ink); }

/* Contact form (visual preview — real one is Gravity Forms in the build) */
.form-wrap { max-width: 44rem; }
.form-row { display: grid; gap: 1.25rem; grid-template-columns: 1fr; margin-bottom: 1.25rem; }
@media (min-width: 640px) { .form-row.two { grid-template-columns: 1fr 1fr; } }
.field label { font-family: var(--mono); font-size: .7rem; text-transform: uppercase;
  letter-spacing: .12em; color: var(--muted-fg); display: block; margin-bottom: .4rem; }
.field input, .field textarea { width: 100%; border: 1px solid var(--border);
  background: var(--bg); padding: .8rem 1rem; font-family: var(--sans); font-size: 1rem;
  color: var(--ink); border-radius: 0; }
.field input:focus, .field textarea:focus { outline: 2px solid var(--safety);
  outline-offset: 0; border-color: transparent; }
.field textarea { min-height: 8rem; resize: vertical; }
.field-note { font-size: .85rem; color: var(--muted-fg); margin-top: .4rem; }
.upload-box { border: 1px dashed var(--border); padding: 1.5rem; text-align: center;
  font-family: var(--mono); font-size: .75rem; text-transform: uppercase;
  letter-spacing: .1em; color: var(--muted-fg); }
input[type="checkbox"] { accent-color: var(--safety); width: 1rem; height: 1rem; }
.check-grid { display: grid; gap: .75rem 1rem; grid-template-columns: repeat(2, 1fr);
  border: 1px solid var(--border); padding: 1.25rem; }
@media (min-width: 768px) { .check-grid { grid-template-columns: repeat(4, 1fr); } }
.check-item { display: flex; align-items: center; gap: .5rem; font-size: .9rem; }
/* Form section headings are must-read, so ink, not orange (BD contrast
   ruling 2026-07-25 — orange is for decorative labels only). */
.form-section-label { font-family: var(--mono); font-size: .75rem; color: var(--ink);
  text-transform: uppercase; letter-spacing: .15em; display: block; margin-bottom: 1.25rem; }
.form-section { margin-bottom: 2.5rem; }

/* Dark CTA band */
.band-dark { background: var(--ink); color: var(--on-dark); }
.band-dark .band-inner { display: flex; flex-direction: column; gap: 1.5rem;
  align-items: flex-start; }
@media (min-width: 768px) { .band-dark .band-inner { flex-direction: row;
  justify-content: space-between; align-items: flex-end; gap: 2rem; } }
.band-dark h2 { font-size: clamp(1.6rem, 3vw, 2.25rem); font-weight: 650; max-width: 40ch; }

/* Process teaser (home) */
.teaser { background: var(--muted); }
.teaser h2 { font-size: clamp(1.75rem, 3.5vw, 2.5rem); font-weight: 650; margin-bottom: 1.25rem; }
.teaser p { max-width: 60ch; color: var(--muted-fg); margin-bottom: 2rem; text-wrap: pretty; }

/* Footer */
footer { background: var(--ink); color: oklch(0.985 0.002 90 / 0.7); }
footer .pad { border: none; }
.foot-cta h2 { color: var(--on-dark); font-size: clamp(1.6rem, 3vw, 2.25rem);
  font-weight: 650; margin-bottom: 1.5rem; }
.foot-counties { font-family: var(--mono); font-size: .7rem; text-transform: uppercase;
  letter-spacing: .08em; margin-top: 3rem; padding-top: 2rem;
  border-top: 1px solid oklch(1 0 0 / 0.1); }
.foot-legal { display: flex; flex-wrap: wrap; gap: 1.5rem; margin-top: 1.5rem; }
.foot-legal a, .foot-legal span { font-family: var(--mono); font-size: .625rem;
  text-transform: uppercase; letter-spacing: .12em; color: oklch(0.985 0.002 90 / 0.6);
  text-decoration: none; }
.foot-legal a:hover { color: var(--safety); }
/* Legal links on their own line (BD 008 §4 item 17). Separated by the LINE
   BREAK and a tighter margin, NOT by lowering contrast. Making legal links
   fainter than the rest would reproduce, in spirit, the exact fault the ruling
   exists to prevent: a policy that is technically present and practically
   unreachable. They inherit .foot-legal's colour deliberately. */
.foot-legal-fine { margin-top: .6rem; }

/* ============================================================
   WordPress-specific additions (theme only — not in previews)
   ============================================================ */

/* The previews put display:flex on .nav-right inline; templates carry no
   inline styles (project standard), so the rule lives here instead. */
.nav-right { display: flex; align-items: center; gap: .75rem; }

/* wp_nav_menu outputs <ul><li><a>; the previews used bare <a> tags.
   These resets make the WP markup lay out identically. */
ul.nav-links, .nav-panel-list, .foot-links { list-style: none; margin: 0; padding: 0; }
.nav-links li { display: inline-flex; }
.nav-links .current-menu-item > a { color: var(--ink); }
.nav-panel-list li { display: block; }
/* Footer links list dissolves into the .foot-legal flex row around it. */
.foot-links { display: contents; }

/* Logo fallback until the vector lockup is uploaded (Customize → Logo). */
.nav-logo-text { font-family: var(--mono); font-weight: 700; font-size: 1.1rem;
  letter-spacing: .05em; color: var(--ink); }

/* Homepage hero slot before the dust-barrier photo lands: same
   intentional-placeholder frame treatment as the work cards. */
.hero-img { position: relative; }
.hero-img .placeholder { font-family: var(--mono); font-size: .625rem; color: var(--muted-fg);
  text-transform: uppercase; letter-spacing: .1em; text-align: center;
  display: flex; align-items: center; justify-content: center; height: 100%; padding: 1rem; }
.hero-img:has(.placeholder) { background: var(--muted); }
.hero-img:has(.placeholder)::after { content: ""; position: absolute; inset: 8px;
  border: 1px solid var(--border); pointer-events: none; }

/* Accessibility: WordPress's standard screen-reader class + a skip link. */
.screen-reader-text { position: absolute; width: 1px; height: 1px; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.skip-link:focus { position: fixed; top: .5rem; left: .5rem; z-index: 100;
  width: auto; height: auto; margin: 0; clip-path: none; overflow: visible;
  background: var(--ink); color: var(--on-dark); padding: .75rem 1.25rem;
  font-family: var(--mono); font-size: .75rem; text-transform: uppercase;
  letter-spacing: .1em; text-decoration: none; }

/* The sticky nav sits below the wp-admin toolbar when logged in. */
body.admin-bar nav { top: 32px; }
@media (max-width: 782px) { body.admin-bar nav { top: 46px; } }

/* ============================================================
   Template utilities — the previews' inline styles, promoted to
   classes (templates carry no inline styles, project standard),
   plus the 404 page's scoped styles and WP content-area rules.
   ============================================================ */

/* Section intros and sub-scale headings (Process/Contact/About/PA). */
.section-intro { margin-bottom: 3rem; }
.section-intro-sm { margin-bottom: 2.5rem; }
.h2-sub { font-size: clamp(1.6rem, 3vw, 2.1rem); font-weight: 650; }

/* Content following a step grid (bold promise line + CTA). */
.after-steps { margin-top: 2.5rem; }
.after-steps > p { font-weight: 700; margin-bottom: 1.25rem; }

/* Emphasis inside muted step/split copy reads in ink. */
.step-cell strong, .split-body strong { color: var(--ink); }
.step-cell > .btn, .step-cell > .btn-ghost { align-self: flex-start; }
.split-body p { margin-bottom: 1.25rem; }
.split-body p:last-child { margin-bottom: 0; }

/* About timeline: years replace the giant step numbers at a smaller scale. */
.exec-num-year { font-size: clamp(1.75rem, 3vw, 2.25rem); }

/* About licensing line: full-width mono label with ink emphasis. */
.mono-label-wide { font-size: .75rem; max-width: none; }
.mono-label-wide strong { color: var(--ink); font-weight: 700; }

/* Stacked ledes in a page head (Public Agencies). */
.page-head .lede + .lede { margin-top: 1rem; }

/* Contact wrong-door strip. */
.trust-inner .strip-lead { color: var(--ink); font-weight: 700; }
.trust-inner a { color: var(--ink); }

/* Vendors: 3-step strip above the form; orange cell numbers. */
.grid-lead { margin-bottom: 4rem; }
.cell-num { color: var(--safety); display: block; margin-bottom: 1rem; }
.form-wrap-wide { max-width: 52rem; }

/* News cards: meta line + read link. */
.card-meta { display: block; margin-bottom: .75rem; }
.card-read { margin-top: 1rem; display: inline-block; }
.work-body h3 a { text-decoration: none; }
.work-body h3 a:hover { color: var(--safety); }

/* Single post media slot. */
.post-media { margin-bottom: 2.5rem; }

/* Legal pages: narrow reading measure, muted body, plain headings. */
.legal-body { max-width: 44rem; color: var(--muted-fg); }
.legal-body p { margin-bottom: 1rem; }
.legal-body h2 { font-size: 1.25rem; font-weight: 650; color: var(--ink); margin: 2rem 0 .75rem; }

/* Editor content (default page template, News singles, landing pages):
   readable measure by default, with the standard block alignments. */
.entry-content > * { max-width: 48rem; }
.entry-content > .alignwide { max-width: 80rem; }
.entry-content > .alignfull { max-width: none; }
.entry-content p, .entry-content ul, .entry-content ol { margin-bottom: 1rem; }
.entry-content h2 { font-size: clamp(1.4rem, 2.5vw, 1.75rem); font-weight: 650; margin: 2rem 0 .75rem; }
.entry-content h3 { font-size: 1.15rem; font-weight: 650; margin: 1.5rem 0 .5rem; }
.entry-content img { height: auto; }

/* Reduced footer (404 + legal): counties line leads, no CTA band above it. */
.foot-counties-lead { margin-top: 0; padding-top: 0; border-top: none; }
/* "Work delivered in:" lead-in (BD ruling 003). Deliberately inline and on the
   SAME line as the counties — BD asked for a lead-in, not a heading, because
   the point is that the list is a record of delivered work rather than a
   coverage boundary. Splitting it onto its own line would rebuild the fence.
   Inherits the parent's mono/uppercase/tracking; only weight and opacity
   change, which is what separates it from the list without detaching it. */
.foot-counties-intro { font-weight: 700; color: oklch(0.985 0.002 90 / 0.92); }

/* ---------- 404 "field report" (from previews/404.html scoped styles) ---------- */
.nf-pad { padding-top: 6rem; padding-bottom: 6rem; }
.nf-grid { display: grid; gap: 3rem; grid-template-columns: 1fr; align-items: start; }
@media (min-width: 1024px) { .nf-grid { grid-template-columns: 7fr 5fr; gap: 4rem; } }
/* Text in ink for legibility; the orange square keeps the accent
   (decorative). */
.nf-label { font-family: var(--mono); font-size: .7rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .18em; color: var(--ink);
  display: flex; align-items: center; gap: .6rem; margin-bottom: 1.5rem; }
.nf-label::before { content: ""; width: .6rem; height: .6rem; background: var(--safety); }
.code-404 { font-weight: 800; color: var(--ink);
  font-size: clamp(5rem, 14vw, 10rem); line-height: 1; letter-spacing: -0.04em; }
.code-404 .zero { position: relative; display: inline-block; }
.code-404 .zero::after { content: ""; position: absolute; left: 50%; top: 54%;
  transform: translate(-50%, -50%); width: .12em; height: .12em; border-radius: 50%;
  background: var(--safety); }
.nf-title { font-size: clamp(1.6rem, 3vw, 2.1rem); font-weight: 650; margin: 1.75rem 0 .75rem; }
.nf-lede { margin-bottom: 2.5rem; }
.nf-lede strong { color: var(--ink); }
.report { border: 1px solid var(--border); }
.report-head { background: var(--ink); color: var(--on-dark); display: flex;
  justify-content: space-between; align-items: center; padding: .7rem 1.25rem;
  font-family: var(--mono); font-size: .7rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .15em; }
.report-head .code { color: var(--safety); }
.report-row { display: grid; grid-template-columns: 5rem 1fr; gap: 1rem;
  padding: 1rem 1.25rem; border-top: 1px solid var(--border); }
.report-row:first-of-type { border-top: none; }
.report-row .k { font-family: var(--mono); font-size: .625rem; text-transform: uppercase;
  letter-spacing: .12em; color: var(--muted-fg); padding-top: .15rem; }
.report-row .v { font-family: var(--mono); font-size: .85rem; color: var(--ink); }

/* ============================================================
   Gravity Forms — the embedded forms wear the design system.
   GF ships its own "Orbital" theme (blue buttons, rounded inputs);
   these rules override it inside our .form-wrap containers. The
   !importants are deliberate: Orbital's selectors are heavyweight,
   and these need to keep winning across GF updates.
   ============================================================ */

/* Where Orbital listens to CSS variables, retune them at the source. */
.form-wrap .gform_wrapper {
  --gf-color-primary: oklch(0.7 0.19 46);
  --gf-ctrl-radius: 0;
  --gf-ctrl-border-color: var(--border);
}

/* "* indicates required fields" legend + the asterisks themselves. */
.form-wrap .gform_wrapper .gform_required_legend {
  font-family: var(--mono); font-size: .625rem; text-transform: uppercase;
  letter-spacing: .12em; color: var(--muted-fg);
}
/* Required marker — distinct HUE, not just a darker ink.

   The problem (owner, 2026-07-27): labels like "CSLB LICENSE # *" put the
   asterisk directly beside a literal "#". In mono at .7rem the two glyphs read
   as the same mark. The previous rule made the asterisk --ink against a
   --muted-fg label, which is a lightness difference only, and at that size it
   is not enough to tell them apart.

   Why not --safety: MEASURED at 2.74:1 on --bg. That is the same failure BD
   ruled on for the buttons on 2026-07-25, and the reason the asterisk was made
   ink in the first place. --safety-deep is the same hue and chroma with
   lightness at 0.54, measured 5.21:1, which clears AA for normal text with
   room to spare.

   Colour is not the sole carrier of meaning here — the asterisk character IS
   the indicator, and the "* indicates required fields" legend explains it — so
   WCAG 1.4.1 holds regardless. The colour is what makes it findable; the
   margin and weight separate it from the "#". */
.form-wrap .gform_wrapper .gfield_required {
  color: var(--safety-deep) !important;
  font-weight: 700;
  margin-left: .35em;
}

/* Labels — mono uppercase, muted (mirror .field label). */
.form-wrap .gform_wrapper .gfield_label,
.form-wrap .gform_wrapper legend.gfield_label,
.form-wrap .gform_wrapper .gform-field-label {
  font-family: var(--mono) !important; font-size: .7rem !important;
  text-transform: uppercase; letter-spacing: .12em;
  color: var(--muted-fg) !important; font-weight: 400 !important;
}

/* Text controls — mirror .field input/textarea. */
.form-wrap .gform_wrapper input[type="text"],
.form-wrap .gform_wrapper input[type="email"],
.form-wrap .gform_wrapper input[type="tel"],
.form-wrap .gform_wrapper input[type="url"],
.form-wrap .gform_wrapper input[type="number"],
.form-wrap .gform_wrapper input[type="date"],
.form-wrap .gform_wrapper select,
.form-wrap .gform_wrapper textarea {
  width: 100%; border: 1px solid var(--border) !important;
  background: var(--bg) !important; padding: .8rem 1rem !important;
  font-family: var(--sans) !important; font-size: 1rem !important;
  color: var(--ink) !important; border-radius: 0 !important;
  box-shadow: none !important;
}
.form-wrap .gform_wrapper input:focus,
.form-wrap .gform_wrapper select:focus,
.form-wrap .gform_wrapper textarea:focus {
  outline: 2px solid var(--safety) !important; outline-offset: 0;
  border-color: transparent !important;
}
.form-wrap .gform_wrapper textarea { min-height: 8rem; resize: vertical; }

/* Field descriptions (mirror .field-note). */
.form-wrap .gform_wrapper .gfield_description {
  font-size: .85rem; color: var(--muted-fg);
}

/* Submit — the page's one primary action (mirror .btn-primary). */
.form-wrap .gform_wrapper .gform_footer button,
.form-wrap .gform_wrapper .gform_footer input[type="submit"],
.form-wrap .gform_wrapper .gform-theme-button:not(.gform-theme-button--secondary) {
  display: inline-block; background: var(--safety) !important;
  /* Ink on orange — mirrors .btn-primary (Ardi 2026-07-26). 6.57:1 vs the
     2.74:1 white failed. */
  color: var(--ink) !important; border: none !important;
  border-radius: 0 !important; font-family: var(--sans) !important;
  font-size: .75rem !important; font-weight: 500 !important;
  text-transform: uppercase !important; letter-spacing: .15em !important;
  padding: .9rem 1.5rem !important; transition: background .15s;
  cursor: pointer;
}
.form-wrap .gform_wrapper .gform_footer button:hover,
.form-wrap .gform_wrapper .gform_footer input[type="submit"]:hover,
.form-wrap .gform_wrapper .gform-theme-button:not(.gform-theme-button--secondary):hover {
  /* Background flips to ink, so the text must flip back to light — otherwise
     ink-on-ink, 1.00:1, invisible. */
  background: var(--ink) !important;
  color: var(--on-dark) !important;
}

/* File upload — mirror .upload-box. "Select files" is a utility action,
   so it wears the ghost tier (never orange, never solid black). */
.form-wrap .gform_wrapper .gform_drop_area {
  border: 1px dashed var(--border) !important; background: transparent !important;
  border-radius: 0 !important; padding: 1.5rem !important;
  font-family: var(--mono); font-size: .75rem; text-transform: uppercase;
  letter-spacing: .1em; color: var(--muted-fg);
}
.form-wrap .gform_wrapper .gform_drop_area .gform_button_select_files {
  background: transparent !important; color: var(--ink) !important;
  border: 1px solid var(--ink) !important; border-radius: 0 !important;
  font-family: var(--sans) !important; font-size: .7rem !important;
  font-weight: 500 !important; text-transform: uppercase !important;
  letter-spacing: .15em !important; padding: .7rem 1.1rem !important;
  transition: background .15s;
}
.form-wrap .gform_wrapper .gform_drop_area .gform_button_select_files:hover {
  background: var(--muted) !important;
}

/* Choices — checkbox/radio option text reads as body copy, not labels. */
.form-wrap .gform_wrapper .gchoice label {
  font-family: var(--sans) !important; font-size: .9rem !important;
  text-transform: none !important; letter-spacing: 0 !important;
  color: var(--ink) !important; font-weight: 400 !important;
}
.form-wrap .gform_wrapper .gchoice input[type="checkbox"],
.form-wrap .gform_wrapper .gchoice input[type="radio"] {
  accent-color: var(--safety);
}

/* Radio rows (Union / Non-Union) sit inline. */
.form-wrap .gform_wrapper .gfield--type-radio .gfield_radio {
  display: flex; flex-wrap: wrap; gap: 1rem 1.5rem;
}

/* The 18-trade grid on the vendor form — mirror .check-grid. */
.form-wrap .gform_wrapper .gfield--type-checkbox .gfield_checkbox {
  display: grid; gap: .75rem 1rem; grid-template-columns: repeat(2, 1fr);
  border: 1px solid var(--border); padding: 1.25rem;
}
@media (min-width: 768px) {
  .form-wrap .gform_wrapper .gfield--type-checkbox .gfield_checkbox {
    grid-template-columns: repeat(4, 1fr);
  }
}
/* ...but a single-choice checkbox (the mailing-list opt-in) drops the box. */
.form-wrap .gform_wrapper .gfield--type-checkbox .gfield_checkbox:has(.gchoice:only-child) {
  border: none; padding: 0; grid-template-columns: 1fr;
}

/* Section breaks on the vendor form — mirror .form-section-label. */
.form-wrap .gform_wrapper .gsection {
  border: none !important; margin-top: 2.5rem; padding: 0;
}
.form-wrap .gform_wrapper .gsection .gsection_title {
  font-family: var(--mono); font-size: .75rem; color: var(--ink);
  text-transform: uppercase; letter-spacing: .15em; font-weight: 400;
}

/* Validation messages: on-palette, square corners. */
.form-wrap .gform_wrapper .gfield_validation_message,
.form-wrap .gform_wrapper .validation_summary {
  border-radius: 0;
}

/* Contact page footer: direct phone + email replace the circular button
   (BD review 2026-07-25) — the non-form conversion path. */
.foot-direct { font-family: var(--sans); font-size: 1.05rem; font-weight: 500;
  display: flex; flex-wrap: wrap; gap: .5rem 1.25rem; align-items: center; }
.foot-direct a { color: var(--on-dark); text-decoration: none;
  border-bottom: 1px solid oklch(0.985 0.002 90 / 0.3); padding-bottom: .1rem;
  transition: color .15s, border-color .15s; }
.foot-direct a:hover { color: var(--safety); border-color: var(--safety); }
.foot-direct-sep { color: oklch(0.985 0.002 90 / 0.4); }

/* Process page technical figures (Ardi's illustrations, 2026-07-25):
   inline SVGs under execution steps 2, 3, and 6. They carry their own
   internal frame/labels; here they just scale responsively. */
.exec-fig { margin: 1.5rem 0 0; }
.exec-fig svg, .exec-fig img { width: 100%; height: auto; display: block; }

/* Quiet project citation under the discipline list (Process page). */
.proj-cite { margin-top: 2.5rem; max-width: none; }

/* ============================================================
   BD checklist round (2026-07-25): stat band, contact promise
   spacing, footer principal note, thank-you page.
   ============================================================ */

/* Occupied-job stat band (Process page): three owner-published numbers
   between the section intro and the discipline list. Same 1px-gap cell
   grid as .step-grid. Values wear ink, not orange — stats are must-read,
   and orange is reserved for decorative labels (BD contrast ruling). */
.stat-band { display: grid; gap: 1px; background: var(--border); border: 1px solid var(--border);
  grid-template-columns: 1fr; margin-bottom: 3rem; }
@media (min-width: 640px) { .stat-band { grid-template-columns: repeat(3, 1fr); } }
.stat { background: var(--bg); padding: 1.75rem 2rem; }
.stat-value { font-family: var(--mono); font-size: clamp(2.25rem, 4vw, 3rem); font-weight: 700;
  color: var(--ink); line-height: 1; display: block; }
.stat-label { font-family: var(--mono); font-size: .7rem; text-transform: uppercase;
  letter-spacing: .15em; color: var(--muted-fg); display: block; margin-top: .6rem; }

/* The 60+ stat links to the agency list (BD rev 2 item 4). Inherits the stat's
   own typography — it must not look like body-copy link furniture sitting inside
   a number. Underline only on the label, and only on hover/focus, so the numeral
   stays a numeral. */
.stat-link { display: block; text-decoration: none; }
.stat-link:hover .stat-label,
.stat-link:focus-visible .stat-label { text-decoration: underline; color: var(--ink); }
.stat-link:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; }

/* Selected public agencies (Public Agencies page, 12.B).
   TEXT ONLY — no logos, no seals, ever. BD ruled them out entirely: no
   countersigned consent exists for any agency mark, California restricts
   official seals by statute, and a seal implies endorsement. If a future pass
   is tempted to "improve" this with logos, that is the reason not to.
   Columns are for density only; the grouping is the argument. */
.agency-groups { display: grid; gap: 2.5rem; grid-template-columns: 1fr; }
@media (min-width: 700px) { .agency-groups { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1100px) { .agency-groups { grid-template-columns: repeat(3, 1fr); } }
.agency-group h3 { font-family: var(--mono); font-size: .7rem; text-transform: uppercase;
  letter-spacing: .15em; color: var(--muted-fg); padding-bottom: .6rem;
  border-bottom: 1px solid var(--border); margin-bottom: .9rem; }
.agency-list { list-style: none; }
.agency-list li { font-size: .95rem; line-height: 1.5; padding: .3rem 0; }

/* Build-time notice. Renders in the page, on purpose: a banner nobody would
   publish by accident is a better gate than a checklist line somebody skips.
   Not a general-purpose alert component — if a second use appears, check it
   really is a "this must not ship yet" case before reusing it. */
.notice-block { border: 2px solid var(--ink); background: var(--muted);
  padding: 1.25rem 1.5rem; margin-bottom: 2.5rem; }
.notice-block strong { font-family: var(--mono); font-size: .8rem; text-transform: uppercase;
  letter-spacing: .1em; display: block; margin-bottom: .6rem; }
.notice-block p { font-size: .9rem; }
.notice-block p + p { margin-top: .6rem; }
.notice-block-stop { border-color: var(--safety); }

/* Response promise under the intake-form heading (Contact page). */
.section-intro-sm .lede { margin-top: .75rem; }

/* Contact/thank-you footer band: the quiet third element after
   phone · email. Plain text, same Inter scale as the links. */
.foot-direct-note { font-weight: 400; color: oklch(0.985 0.002 90 / 0.7); }

/* Thank-you page (page-thank-you.php): headline left, "what happens
   next" card right — same 7/5 grid and report-card language as the 404
   field report. The card reuses .report/.report-head; only the numbered
   rows are new. */
.ty-grid { display: grid; gap: 3rem; grid-template-columns: 1fr; align-items: start; }
@media (min-width: 1024px) { .ty-grid { grid-template-columns: 7fr 5fr; gap: 4rem; } }
.ty-grid h1 { font-size: clamp(2rem, 4vw, 2.75rem); font-weight: 650; margin-bottom: 1rem; }
.ty-grid .lede { font-size: 1.1rem; margin-bottom: 2.5rem; }
.ty-ctas { display: flex; flex-wrap: wrap; gap: 1rem; }
.ty-next { list-style: none; }
.ty-next li { display: flex; gap: 1rem; padding: 1.25rem; border-top: 1px solid var(--border); }
.ty-next li:first-child { border-top: none; }
/* Row numbers are decorative (source order carries the sequence). */
.ty-next .ty-num { font-family: var(--mono); font-size: .75rem; font-weight: 700;
  color: var(--safety); padding-top: .15rem; }
.ty-next .ty-text { font-size: .9rem; color: var(--muted-fg); line-height: 1.6; }

/* ============================================================
   Illustration round 2 (2026-07-26): split homepage hero (CT axon
   right), Markets figures m1–m5, About annotated-axon band.
   ============================================================ */

/* Split hero: mobile stacks copy over drawing; from 900px the axon takes
   roughly half the width (Ardi's placement spec). Only applies when no
   featured image is set — the photo state keeps the original stacked
   .hero-img layout untouched. */
.hero-grid { display: grid; grid-template-columns: 1fr; gap: 2.5rem; align-items: center; }
@media (min-width: 900px) { .hero-grid { grid-template-columns: 1.05fr .95fr; gap: 3.5rem; } }
.hero-fig { margin: 0; }
.hero-fig svg { width: 100%; height: auto; display: block; }
/* The axon reads badly at postage-stamp size; cap how small it gets on
   phones by letting it fill the content column. */
@media (max-width: 899px) { .hero-fig { max-width: 26rem; margin: 0 auto; } }

/* Markets figures: same treatment as the Process .exec-fig, sized to the
   body-copy column, quiet top margin. */
.market-fig { margin: 1.75rem 0 0; max-width: 34rem; }
.market-fig svg { width: 100%; height: auto; display: block; }

/* Vendors: quiet pointer to the Outreach Notices archive, below the form. */
.vendor-notices { margin-top: 2.5rem; max-width: none; }

/* About: the annotated cover-sheet drawing as a quiet full-width band.
   Muted ground + hairlines so it reads as a drawing sheet, not a photo. */
.axon-band { background: var(--muted); border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border); }
.axon-fig { margin: 0 auto; max-width: 62rem; }
.axon-fig svg { width: 100%; height: auto; display: block; }

/* Homepage CT-axon draw-on (BD ruling 2026-07-26). A one-time reveal that
   mimics a plotter laying down a drawing. The drawing appears to be drawn; it
   does not perform. Explicitly rejected: looping, crane rotation, parallax,
   scroll-triggered replay, hover states, floating, particles, bouncy easing.

   Three rules this block obeys, in priority order:

   1. STATIC IS THE DEFAULT, NOT A FALLBACK. The hidden start state exists
      ONLY inside the @keyframes below, held during the delay by
      animation-fill-mode: backwards. Nothing outside a keyframe ever hides a
      line. If this file fails to parse, arrives late, or lands in a browser
      that ignores `animation`, the finished drawing is what paints. There is
      no code path that can strand an element invisible — which matters in a
      700+ line hand-edited stylesheet where a parse error above would
      otherwise take 83 elements with it.
   2. MOTION IS OPT-IN. Gated on (prefers-reduced-motion: no-preference),
      which does NOT match in a browser that has never heard of the feature.
      Unknown UA -> static. An @media (reduce) override would fail the other
      way, handing motion to exactly the clients least able to opt out.
   3. NOTHING HERE TOUCHES LAYOUT. Only fill-opacity, stroke-opacity and
      stroke-dashoffset animate, so CLS stays 0. The svg's viewBox already
      reserves its box before first paint.

   SCOPE: .hero-split exists only when the front page has NO featured image.
   Setting one — the wp-admin-only revert lever — removes the drawing and this
   block with it. The About band and the Markets/Process figures are
   deliberately not matched.

   Hooks (.ct-* classes and --ct-len) are written into the asset by
   scripts/prep-figures.php. Total run: 1150ms. */
@media (prefers-reduced-motion: no-preference) {

  /* Beat 1 — paper tone. Every filled face and both ground shadows fade up
     FIRST. This ordering is why the reveal cannot x-ray: fading faces in AFTER
     the edges would show normally-occluded linework behind three white
     knockouts and then erase it, which reads as a glitch, not as drafting. */
  .hero-split .hero-fig .ct-tone  { animation: ct-fill-in 240ms ease-out 0ms backwards; }

  /* Beat 1b — the pencil underlay. Grey (#6E6863) means existing-or-future
     condition: ground ticks and the setback frame. A drafter lays out in
     pencil and inks the work, so grey fades and only black draws. */
  .hero-split .hero-fig .ct-ghost { animation: ct-stroke-in 300ms ease-out 60ms backwards; }

  /* Beat 2 — the ink. */
  .hero-split .hero-fig .ct-ink   { animation: ct-draw 320ms linear 260ms backwards; }

  /* Stagger, assigned by prep-figures.php: heaviest stroke-width first,
     document order within a weight — heavy structure blocked in, fine detail
     added last. The compound selector means a bucket cannot leak onto a
     non-ink element. Keep in sync with STAGGER_BUCKETS in prep-figures.php. */
  .hero-split .hero-fig .ct-ink.ct-d1 { animation-delay: 315ms; }
  .hero-split .hero-fig .ct-ink.ct-d2 { animation-delay: 370ms; }
  .hero-split .hero-fig .ct-ink.ct-d3 { animation-delay: 425ms; }
  .hero-split .hero-fig .ct-ink.ct-d4 { animation-delay: 480ms; }
  .hero-split .hero-fig .ct-ink.ct-d5 { animation-delay: 535ms; }
  .hero-split .hero-fig .ct-ink.ct-d6 { animation-delay: 590ms; }
  .hero-split .hero-fig .ct-ink.ct-d7 { animation-delay: 645ms; }
  /* ct-d0 keeps the 260ms base delay set above. */

  /* Beat 3 — the orange, last and alone. BD's one non-negotiable: the hatch is
     the "active work" marker, so it must read as the last thing added.
     fill-opacity, NOT opacity: the tint layers carry opacity="0.06" as a
     presentation attribute, which any author `opacity` rule outranks —
     animating opacity would blow the 6% wash out to solid orange.
     fill-opacity multiplies with it instead (0.06 x 1 = 0.06).
     Only the hatch fill is targeted; the white knockout beneath it stays
     static because it is what hides the floor lines behind the sealed bay. */
  .hero-split .hero-fig .ct-hatch { animation: ct-fill-in 240ms ease-out 900ms backwards; }
  .hero-split .hero-fig .ct-mark  { animation: ct-draw   250ms linear  900ms backwards; }

  @keyframes ct-fill-in   { from { fill-opacity: 0; }   to { fill-opacity: 1; } }
  @keyframes ct-stroke-in { from { stroke-opacity: 0; } to { stroke-opacity: 1; } }

  /* --ct-len is each element's TRUE stroke length in user units, written into
     the asset by prep-figures.php.

     This deliberately avoids pathLength="1", the textbook approach, which would
     let one dasharray serve every element. Safari's pathLength support is
     unreliable — absent for years, and where present it has open bugs (it
     rescales on zoom, breaking stroke animations mid-flight). A hero that
     silently refuses to animate on iPhone is the wrong failure on a site whose
     audience is property managers looking at it on their phones. Real lengths
     in user units have worked in every engine for over a decade.

     This is NOT a visual compromise. dasharray 1 with offset 1->0 and
     dasharray L with offset L->0 both draw 0->100% of their element over the
     same 320ms — pathLength only renormalises the units. Identical output,
     fewer browser assumptions.

     Every element takes 320ms regardless of its length, so a long mast rail
     covers more ground per frame than a short tick. That reads correctly for a
     plotter, and it was true of the pathLength version too.

     stroke-dasharray sits inside the keyframes deliberately. With `backwards`
     (not `forwards`) the element reverts to its authored, undashed stroke when
     the animation ends, which also kills the hairline seam some renderers show
     where a closed path's dash wraps at exactly its own length. */
  @keyframes ct-draw {
    from { stroke-dasharray: var(--ct-len); stroke-dashoffset: var(--ct-len); }
    to   { stroke-dasharray: var(--ct-len); stroke-dashoffset: 0; }
  }
}

/* ============================================================
   Lead band (2026-07-31): the occupied-renovation checklist offer.
   Front page only, per BD's ruling in from-bd/020 — /process/ already
   carries the stat band, and two full-width bands on one scroll is the
   objection BD raised about two adjacent airport interiors in Featured
   Work. Deferred on evidence, not refused.
   ============================================================ */

/* --muted, matching .teaser rather than introducing a third band colour.
   The band sits immediately after the teaser, so a different ground would
   read as two competing closes instead of one. */
.lead-band { background: var(--muted); border-top: 1px solid var(--border); }
.lead-band-grid { display: grid; grid-template-columns: 1fr; gap: 2rem; align-items: start; }
/* Copy left, form right from 900px — the same breakpoint the split hero
   and the ty-grid use, so the page has one column-drop point, not three. */
@media (min-width: 900px) {
  .lead-band-grid { grid-template-columns: 1.1fr .9fr; gap: 3.5rem; align-items: center; }
}
.lead-band h2 { font-size: clamp(1.5rem, 3vw, 2.125rem); font-weight: 650;
  margin-bottom: 0; text-wrap: balance; max-width: 24ch; }

/* Gravity Forms renders its own markup here and we do not control its
   class names, so this styles by structure and stays deliberately light:
   two fields and a button. Anything more specific breaks the next time
   the plugin changes its DOM, which it has done before. */
.lead-band-form .gform_wrapper { margin: 0; }
.lead-band-form .gform_fields { display: grid; gap: 1rem; }
.lead-band-form input[type="text"],
.lead-band-form input[type="email"] { width: 100%; }
.lead-band-form .gform_footer { margin: 1.25rem 0 0; padding: 0; }

/* The offer must not out-shout the process teaser directly above it. The
   headline is one step down from .teaser h2 and the band carries no
   accent ground — the button is the only saturated element. */

/* ============================================================
   Planning Note (2026-08-01): the public occupied-building
   questions page. from-bd/022, which dropped the gate and made
   this a public indexed content asset rather than a lead magnet.
   PRINT IS A FIRST-CLASS OUTPUT HERE, not an afterthought — BD
   ruled a printed copy the stronger signal, because it means the
   note is going into a meeting.
   ============================================================ */

.pn-head .lede { max-width: 46ch; }
.pn-actions { margin-top: 2rem; }

.pn-list { list-style: none; margin: 0; padding: 0; counter-reset: none; }
.pn-item { display: grid; grid-template-columns: 3rem 1fr; gap: 1rem;
  padding: 2rem 0; border-top: 1px solid var(--border); }
@media (min-width: 768px) { .pn-item { grid-template-columns: 5rem 1fr; gap: 2rem; } }
.pn-item:first-child { border-top: none; }

/* Deliberately --safety-deep, not --safety. These numerals are the same
   shape as .exec-num but this page was built after the contrast finding,
   so it does not inherit the failure. --safety measures ~2.8:1 and clears
   neither the 4.5:1 nor the 3:1 threshold; this measures ~5.4:1. */
.pn-num { font-family: var(--mono); font-size: clamp(1.5rem, 3vw, 2rem);
  font-weight: 700; color: var(--safety-deep); line-height: 1; }

.pn-q { font-size: clamp(1.125rem, 2vw, 1.375rem); font-weight: 650;
  line-height: 1.3; margin: 0 0 .5rem; text-wrap: pretty; }
.pn-note { color: var(--muted-fg); max-width: 62ch; margin: 0 0 1.25rem;
  text-wrap: pretty; }

/* Owner + Date. BD 022 §8: these are the reason the sheet works — they turn
   a handout into a document someone brings to a meeting with names in it.
   ⛔ Do not remove them to simplify the print layout.
   Ruled lines rather than inputs: no form on this page, nothing to lose on
   refresh, and identical behaviour on screen and paper. */
.pn-fields { display: flex; flex-wrap: wrap; gap: 1.5rem; margin: 0; }
.pn-field { display: flex; align-items: baseline; gap: .5rem; flex: 1 1 12rem; }
.pn-field-label { font-family: var(--mono); font-size: .625rem;
  text-transform: uppercase; letter-spacing: .12em; color: var(--muted-fg);
  white-space: nowrap; }
.pn-rule { flex: 1 1 auto; border-bottom: 1px solid var(--border);
  height: 1.25rem; min-width: 6rem; }
.pn-rule-short { max-width: 8rem; }

.pn-close { margin-top: 2.5rem; padding-top: 2rem;
  border-top: 1px solid var(--border); font-size: 1.125rem; max-width: 60ch;
  text-wrap: pretty; }

/* ── Print ──────────────────────────────────────────────────
   The note has to survive being printed and handed round a table. */
@media print {
  /* Chrome and Safari drop backgrounds by default; the ruled lines are
     borders rather than fills precisely so they survive that. */
  header, footer, .pn-actions, .pn-cta, .skip-link { display: none !important; }
  .pn-head, .pn-list-section { padding: 0 !important; }
  .pad { padding-top: 0 !important; padding-bottom: 0 !important; }
  .wrap { max-width: none !important; padding: 0 !important; }
  body { font-size: 10.5pt; }
  .pn-head h1 { font-size: 18pt; }
  .pn-q { font-size: 11.5pt; }
  .pn-note { color: #333; }
  /* Never split a question from its Owner/Date line across a page break —
     a name written against the wrong question is worse than a blank page. */
  .pn-item { break-inside: avoid; page-break-inside: avoid;
    padding: .55rem 0; grid-template-columns: 2.5rem 1fr; gap: 1rem; }
  .pn-num { font-size: 12pt; }
  .pn-close { break-inside: avoid; page-break-inside: avoid; font-size: 10.5pt; }
  .pn-rule { border-bottom: 1px solid #999; }
}

/* Vendor footer variant (2026-08-01, BD 028 §3). The whole line is the link
   on /vendors/ children, so it has to read as a headline first and a link
   second — underline on hover only, never the default link blue, and it
   inherits the .foot-cta h2 size so the three footer variants stay the same
   shape. */
.foot-cta-vendor h2 a { color: inherit; text-decoration: none;
  border-bottom: 1px solid oklch(1 0 0 / 0.3); }
.foot-cta-vendor h2 a:hover,
.foot-cta-vendor h2 a:focus-visible { border-bottom-color: var(--safety); }
