/* ==========================================================================
   Castleberg Digital, shared stylesheet
   --------------------------------------------------------------------------
   One file for every page. Edit the tokens below to re-skin the whole site.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Design tokens, colours, type, spacing
   -------------------------------------------------------------------------- */
:root {
  /* Colour, Castleberg Digital brand (brand guidelines §3).
     Warm charcoal base, gold accent. Gold is an accent, not a flood:
     small doses on a dark canvas, one clear gold action per screen. */
  --bg:          #141311;  /* charcoal near-black page background */
  --bg-alt:      #1C1A16;  /* surface, cards, panels, section banding */
  --ink:         #F0ECE0;  /* warm light text */
  --ink-soft:    #948C78;  /* muted sub-text, captions */
  --ink-dim:     #66604F;  /* fine print, disabled */
  --accent:      #C9973F;  /* gold, links, CTAs, highlights, contours */
  --accent-hover:#DBAD5C;  /* brighter gold for link/ghost hover on dark */
  /* Button gradient runs toward a slightly deeper gold, kept light
     enough that dark button text holds WCAG AA contrast */
  --accent-grad: linear-gradient(135deg, #C9973F, #B08334);
  --accent-soft: #2A2418;  /* dark gold-tinted fill, tags, subtle panels */
  --success:     #C9973F;  /* warm gold for review stars + confirmations */
  --hairline:    #332E24;  /* dividers */
  --field-line:  #5A5344;  /* control borders, 3:1 against the band (WCAG 1.4.11) */
  --paper:       #221F1A;  /* raised surface, form fields, cards */

  /* Type, Lora (headings/logotype) + Inter (body/UI) */
  --font-display: "Lora", Georgia, "Times New Roman", serif;
  --font-body: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;

  /* Motion, every transition/animation on the site uses these, so the
     whole site moves at one consistent tempo. All motion is disabled by the
     prefers-reduced-motion block further down. */
  --dur-fast: 140ms;       /* hovers, presses, focus feedback */
  --dur-base: 220ms;       /* accordion, small reveals */
  --dur-slow: 450ms;       /* page fade, scroll reveals */
  --ease-out: cubic-bezier(0.22, 0.65, 0.3, 1);

  /* Layout */
  --container: 68rem;      /* ~1088px max content width */
  --gutter: clamp(1.25rem, 4vw, 2.5rem);
  --section-pad: clamp(3.5rem, 9vw, 6.5rem);
  --radius-pill: 999px;    /* buttons + tags */
  --radius-soft: 10px;     /* form fields, mockup frames */
}

/* --------------------------------------------------------------------------
   2. Reset + base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  /* No automatic hyphenation. Long words wrap whole to the next line and
     only break mid-word when there is genuinely nowhere else to go, so
     nothing gets a hyphen dropped into it at the line edge. */
  hyphens: manual;
  overflow-wrap: break-word;
  word-break: normal;
}

img, svg { max-width: 100%; display: block; }

/* Headings: Lora for the big two only, body face elsewhere for restraint */
h1, h2 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.01em;
  margin: 0 0 0.6em;
  text-wrap: balance;
}
h1 { font-size: clamp(2.3rem, 5.5vw, 3.6rem); }
h2 { font-size: clamp(1.6rem, 3.2vw, 2.25rem); }
h3 {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 1.125rem;
  line-height: 1.35;
  margin: 0 0 0.5em;
}
h4 { font-family: var(--font-body); font-weight: 600; font-size: 1rem; margin: 0 0 0.4em; }

p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--accent-hover); }

ul, ol { padding-left: 1.2em; margin: 0 0 1em; }

hr {
  border: 0;
  border-top: 1px solid var(--hairline);
  margin: 2rem 0;
}

/* Visible keyboard focus everywhere */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Utility: visually hidden but available to screen readers */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Skip link (first element on every page) */
.skip-link {
  position: absolute;
  left: 1rem; top: -3rem;
  z-index: 100;
  background: var(--accent);
  color: #141311; /* dark on gold, white would fail contrast */
  padding: 0.5rem 1rem;
  border-radius: 0 0 6px 6px;
  text-decoration: none;
  transition: top var(--dur-fast) ease;
}
.skip-link:focus { top: 0; color: #141311; }

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

/* Sections: hairline top rule + optional band background = the page rhythm.
   No boxes, no shadows. */
.section { padding-block: var(--section-pad); }
.section + .section { border-top: 1px solid var(--hairline); }
.section--band { background: var(--bg-alt); }

/* Narrow measure for long-form copy (About, FAQ, intros) */
.measure { max-width: 42rem; }

/* Lora sets heavier than the Playfair it replaced on 25 July, so stacked
   text was reading tight. Long-form prose gets more leading, more air
   between paragraphs and more room above its headings. */
.measure,
.split p,
.split li { line-height: 1.78; }
.measure p,
.split p { margin-bottom: 1.3em; }
.measure h2,
.measure h3,
.split h3 { margin-top: 1.7em; }
.measure > :first-child,
.split > :first-child,
.split > div > :first-child { margin-top: 0; }

.eyebrow {
  display: block;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 1rem;
}

.lede {
  font-size: 1.15rem;
  line-height: 1.7;
  color: var(--ink-soft);
  max-width: 38rem;
}

/* Emphasis in body copy: bold in the body colour, never the gold accent.
   Gold on scattered keywords competes with the buttons and cheapens the
   accent. 600 not 700, because light text on a dark background already
   reads heavier than its weight. Scoped, never a bare strong { } rule,
   because .site-footer strong is the footer wordmark. */
main p strong,
main li strong,
.faq-answer strong {
  font-weight: 600;
  color: inherit;
}

/* --------------------------------------------------------------------------
   4. Buttons
   -------------------------------------------------------------------------- */
.btn {
  display: inline-block;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.95rem;
  line-height: 1;
  padding: 0.9rem 1.6rem;
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color var(--dur-fast) ease,
    border-color var(--dur-fast) ease,
    color var(--dur-fast) ease,
    box-shadow var(--dur-fast) ease,
    filter var(--dur-fast) ease,
    transform var(--dur-fast) ease;
}
/* Hover feedback: every button darkens and grows slightly; press settles
   it back down a pixel. (Gradient backgrounds can't transition, so the
   darken is a filter shift instead of a colour swap.) */
.btn:hover { transform: scale(1.04); }
.btn:active { transform: scale(1.02) translateY(1px); }
.btn--primary {
  background: var(--accent-grad);
  color: #141311; /* dark text on gold, AA contrast; white fails */
  font-weight: 600;
}
.btn--primary:hover {
  color: #141311;
  filter: brightness(0.88);
  box-shadow: 0 4px 18px rgba(201, 151, 63, 0.3);
}
.btn--primary:active {
  filter: brightness(0.82);
  box-shadow: 0 2px 8px rgba(201, 151, 63, 0.25);
}
.btn--ghost {
  background: transparent;
  color: var(--accent-hover);
  border-color: var(--accent);
}
.btn--ghost:hover {
  background: var(--accent-soft);
  color: var(--ink);
  border-color: var(--accent-hover);
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: center;
}

/* --------------------------------------------------------------------------
   5. Header + nav (shared component, same markup on every page)
   -------------------------------------------------------------------------- */
.site-header {
  border-bottom: 1px solid var(--hairline);
  background: var(--bg);
}
.site-header .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 4.25rem;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.2rem;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}
.brand svg { width: 1.7rem; height: 1.7rem; flex: none; }
/* The full horizontal logo lockup (SVG). 4.5rem tall ≈ 245px wide, well
   above the brand guideline's 140px wordmark minimum. Explicit flex
   sizing: the global img { max-width: 100% } otherwise collapses an
   auto-width SVG to 0 inside the flex header. The mobile step-downs are
   in the 56rem block below: 245px plus the hamburger will not fit a
   320px phone. */
.brand img {
  height: 4.5rem;
  width: calc(4.5rem * 3.4); /* 680:200 aspect */
  max-width: none;
  flex: none;
  display: block;
}

.site-nav ul {
  list-style: none;
  display: flex;
  align-items: center;
  gap: clamp(0.75rem, 1.8vw, 1.4rem);
  margin: 0;
  padding: 0;
}
.site-nav a {
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--ink);
  text-decoration: none;
  padding: 0.35rem 0.1rem;
  border-bottom: 2px solid transparent;
  transition: color var(--dur-fast) ease;
}
.site-nav a:hover { color: var(--accent); }
/* Desktop only: an understated underline that draws in on hover/focus.
   (Mobile menu links keep the plain colour shift.) */
@media (min-width: 56.0625rem) {
  .site-nav a:not(.nav-cta) { position: relative; }
  .site-nav a:not(.nav-cta)::after {
    content: "";
    position: absolute;
    left: 0; right: 0; bottom: -2px;
    height: 2px;
    background: var(--accent);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--dur-base) var(--ease-out);
  }
  .site-nav a:not(.nav-cta):hover::after,
  .site-nav a:not(.nav-cta):focus-visible::after { transform: scaleX(1); }
  /* The current page keeps its static border, no animated double line */
  .site-nav a[aria-current="page"]::after { content: none; }
}
.site-nav a[aria-current="page"] {
  color: var(--accent);
  border-bottom-color: var(--accent);
}
/* The nav's own CTA gets button treatment */
.site-nav a.nav-cta {
  display: inline-block; /* transforms don't apply to inline elements */
  background: var(--accent-grad);
  color: #141311; /* dark on gold */
  border-radius: var(--radius-pill);
  padding: 0.55rem 1.1rem;
  border-bottom: 0;
  transition: filter var(--dur-fast) ease, transform var(--dur-fast) ease;
}
.site-nav a.nav-cta:hover { color: #141311; filter: brightness(0.88); transform: scale(1.04); }

/* Hamburger, hidden on desktop */
.nav-toggle {
  display: none;
  background: none;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  padding: 0.55rem 0.7rem;
  cursor: pointer;
  color: var(--ink);
  /* 44px minimum tap target; the icon stays 1.25rem and is centred. */
  min-width: 44px;
  min-height: 44px;
  place-items: center;
}
.nav-toggle svg { width: 1.25rem; height: 1.25rem; display: block; }
.nav-toggle .icon-close { display: none; }
.nav-toggle[aria-expanded="true"] .icon-open { display: none; }
.nav-toggle[aria-expanded="true"] .icon-close { display: block; }

@media (max-width: 56rem) {
  .nav-toggle { display: grid; }
  /* Step the lockup down once the hamburger appears, and again on the
     narrowest phones, so the header never overflows its gutters. */
  .brand img { height: 3.75rem; width: calc(3.75rem * 3.4); }
  .site-nav {
    display: none;
    position: absolute;
    top: 100%; left: 0; right: 0;
    z-index: 50;
    background: var(--bg);
    border-bottom: 1px solid var(--hairline);
    padding: 0.5rem var(--gutter) 1.25rem;
  }
  .site-header { position: relative; }
  .site-nav.is-open { display: block; }
  .site-nav ul { flex-direction: column; align-items: stretch; gap: 0; }
  .site-nav li { border-bottom: 1px solid var(--hairline); }
  .site-nav li:last-child { border-bottom: 0; padding-top: 0.9rem; }
  .site-nav a { display: block; padding: 0.8rem 0.25rem; font-size: 1rem; }
  .site-nav a[aria-current="page"] { border-bottom-color: transparent; }
  .site-nav a.nav-cta { text-align: center; }
}

@media (max-width: 24rem) {
  .brand img { height: 3.1rem; width: calc(3.1rem * 3.4); }
}

/* --------------------------------------------------------------------------
   6. Footer (shared component, same markup on every page)
   -------------------------------------------------------------------------- */
.site-footer {
  border-top: 1px solid var(--hairline);
  background: var(--bg-alt);
  padding-block: 2.75rem;
  font-size: 0.9rem;
  color: var(--ink-soft);
}
.site-footer .container {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem 3rem;
  justify-content: space-between;
  align-items: flex-start;
}
/* The footer wordmark is text, not the SVG lockup. Scaled 50% with the
   header logo so the two read as the same brand at the same weight. */
.site-footer strong {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.575rem;
  line-height: 1.2;
  color: var(--ink);
  display: block;
  margin-bottom: 0.45rem;
}
.site-footer .footer-blurb { max-width: 26rem; }
/* Brand tagline under the footer name */
.footer-tag {
  color: var(--accent);
  font-size: 0.9rem;
  margin-bottom: 0.4rem;
}
.site-footer ul { list-style: none; margin: 0; padding: 0; }
.site-footer a { color: var(--ink-soft); }
.site-footer a:hover { color: var(--accent); }
.footer-legal { flex-basis: 100%; margin-top: 0.5rem; font-size: 0.8rem; }
/* Filled brand-colour circles with a white glyph, matching the Milners
   footer treatment (public/css/style.css .social-row). Colour lives on the
   background, not the glyph. */
.footer-social {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 0.75rem;
  margin-left: 0;
  padding: 0;
  list-style: none;
}
.site-footer .footer-social a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  padding: 0;
  color: #ffffff;
  transition: transform 0.15s ease;
}
.site-footer .footer-social a:hover { transform: translateY(-2px); }
.footer-social svg { width: 23px; height: 23px; display: block; }
.site-footer .footer-social .is-instagram {
  background: radial-gradient(circle at 30% 110%, #FDF497 0%, #FD5949 45%, #D6249F 60%, #285AEB 90%);
}
.site-footer .footer-social .is-facebook { background: #1877F2; }
.site-footer .footer-social .is-linkedin { background: #0A66C2; }
/* Phones: footer links get comfortable tap heights (44px-ish targets);
   desktop keeps its tight editorial spacing */
@media (max-width: 40rem) {
  .site-footer ul a { display: inline-flex; align-items: center; min-height: 44px; padding: 0.5rem 0; }
  .footer-legal a { display: inline-flex; align-items: center; min-height: 44px; padding-inline: 0.3rem; }
}

/* --------------------------------------------------------------------------
   7. Hero, type on the left, real work on the right
   --------------------------------------------------------------------------
   The homepage signature: a browser frame beside the headline showing the
   real Milners build, a contained scroll area you can browse without
   moving the page. Proof instead of decoration. */
.hero {
  padding-block: clamp(3rem, 7vw, 5.5rem) var(--section-pad);
  /* Signature contour motif behind the hero (brand §5). The left-to-right
     gradient darkens the area behind the headline so contour lines never
     sit directly under text. */
  background-image:
    linear-gradient(90deg, var(--bg) 35%, rgba(20, 19, 17, 0.7) 60%, rgba(20, 19, 17, 0.15) 100%),
    url("assets/contour.svg");
  background-size: cover, cover;
  background-position: center, right top;
  background-repeat: no-repeat, no-repeat;
}
.hero .lede { margin-bottom: 1.75rem; }
/* Hero guarantee pillars: two short supporting columns beneath the sub-line,
   stacked on mobile. Existing tokens only, no new assets. */
.hero-pillars {
  list-style: none;
  margin: 0 0 1.75rem;
  padding: 1.5rem 0 0;
  border-top: 1px solid var(--hairline);
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.25rem 2rem;
}
@media (min-width: 34rem) { .hero-pillars { grid-template-columns: 1fr 1fr; } }
.hero-pillars h2 {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 1.05rem;
  line-height: 1.3;
  margin: 0 0 0.35rem;
  color: var(--ink);
}
.hero-pillars p { color: var(--ink-soft); font-size: 0.92rem; margin: 0; }
/* SEO build spec: the hero H1 is the keyword line, visually styled as the
   eyebrow; the display slogan is a <p class="hero-headline"> that keeps
   the old H1's look. Semantics for search, unchanged design for people. */
h1.eyebrow { font-family: var(--font-body); line-height: 1.5; }
.hero-headline {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(2.3rem, 5.5vw, 3.6rem);
  line-height: 1.12;
  letter-spacing: -0.01em;
  margin: 0 0 0.6em;
  text-wrap: balance;
}
.hero .container {
  display: grid;
  gap: 3rem;
  align-items: center;
  grid-template-columns: 1fr;
}
@media (min-width: 60rem) {
  /* Give the proof frame more weight (~48%) without cramping the headline. */
  .hero .container { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: 4rem; }
  /* On desktop the frame stands taller so it fills about half the first
     screen, real presence for the strongest asset on the page. */
  .hero .walk-viewport--tall { aspect-ratio: auto; height: clamp(30rem, 66vh, 42rem); }
}
/* Taller viewport for the hero frame so the pan shows more of the page */
.walk-viewport--tall { aspect-ratio: 4 / 5; }

/* --------------------------------------------------------------------------
   7b. Statement, an oversized editorial beat that breaks section rhythm
   -------------------------------------------------------------------------- */
.statement { padding-block: clamp(4.5rem, 12vw, 8.5rem); }
.statement blockquote {
  margin: 0 0 0 auto;
  max-width: 26ch;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.7rem, 4.2vw, 2.9rem);
  line-height: 1.22;
  letter-spacing: -0.01em;
  text-wrap: balance;
}
.statement blockquote em { font-style: italic; color: var(--accent-hover); }
.statement .statement-source {
  display: block;
  margin-top: 1.25rem;
  font-family: var(--font-body);
  font-size: 0.9rem;
  color: var(--ink-soft);
}

/* --------------------------------------------------------------------------
   7c. Split, asymmetric two-column prose layout (About)
   -------------------------------------------------------------------------- */
.split {
  display: grid;
  gap: 2rem 4.5rem;
  grid-template-columns: 1fr;
  align-items: start;
}
@media (min-width: 54rem) {
  .split { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
  .split > h2 { position: sticky; top: 2rem; }
  /* The bio reads straight down the page instead: heading, portrait,
     then the prose. No sticky column, no side-by-side. */
  .split--stack { grid-template-columns: 1fr; }
  .split--stack > h2 { position: static; }
}

/* --------------------------------------------------------------------------
   9. Homepage sections
   -------------------------------------------------------------------------- */
/* Build → launch → look after: a genuine sequence, so numbers are earned */
.steps {
  list-style: none;
  counter-reset: step;
  margin: 2rem 0 0;
  padding: 0;
  display: grid;
  gap: 0;
}
.steps li {
  counter-increment: step;
  padding: 1.5rem 0;
  border-top: 1px solid var(--hairline);
  display: grid;
  grid-template-columns: 3.5rem 1fr;
  gap: 1rem;
  align-items: baseline;
}
.steps li::before {
  content: counter(step, decimal-leading-zero);
  font-family: var(--font-display);
  font-size: 1.5rem;
  color: var(--accent);
}
.steps h3 { margin-bottom: 0.25rem; }
.steps p { color: var(--ink-soft); margin: 0; }

/* "Everything covered", the hero-adjacent numbered breakdown of what's in
   every build. Hairline rules per row only (no vertical dividers, no boxes);
   the confidence comes from the type and the numbering, not borders. */
.covered-grid {
  display: grid;
  grid-template-columns: 1fr;
  column-gap: clamp(2rem, 5vw, 4rem);
  margin-top: 2.5rem;
}
@media (min-width: 44rem) { .covered-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 64rem) { .covered-grid { grid-template-columns: 1fr 1fr 1fr; } }
.covered-item {
  padding: 1.6rem 0 1.75rem;
  border-top: 1px solid var(--hairline);
}
.covered-item .covered-num {
  font-family: var(--font-display);
  font-size: 1.05rem;
  color: var(--accent);
  margin-right: 0.75rem;
}
.covered-item h3 { margin-bottom: 0.4rem; }
.covered-item p {
  color: var(--ink-soft);
  font-size: 0.95rem;
  margin: 0;
}

/* Tag pills, trades, towns */
.tag-list {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 1.25rem 0 0;
  padding: 0;
}
.tag-list li {
  background: var(--accent-soft);
  color: var(--ink);
  font-size: 0.85rem;
  font-weight: 500;
  padding: 0.35rem 0.9rem;
  border-radius: var(--radius-pill);
}
/* Town pills that link to a local landing page keep the pill look, with a
   quiet accent underline to show they're clickable */
.tag-list li a {
  /* Fill the pill rather than just the words, so the whole pill is the tap
     target. Negative margin cancels the li padding, so nothing moves. */
  display: block;
  margin: -0.35rem -0.9rem;
  padding: 0.35rem 0.9rem;
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--accent);
}
.tag-list li a:hover { color: var(--accent-hover); }

.two-col {
  display: grid;
  gap: 2.5rem;
  grid-template-columns: 1fr;
}
@media (min-width: 48rem) {
  .two-col { grid-template-columns: 1fr 1fr; gap: 4rem; }
}

/* --------------------------------------------------------------------------
   9b. Homepage pricing teaser, three clickable price cards
   -------------------------------------------------------------------------- */
.price-teaser {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  margin-top: 2.5rem;
}
@media (min-width: 48rem) { .price-teaser { grid-template-columns: 1fr 1fr 1fr; } }
.price-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-soft);
  padding: 1.75rem 1.6rem;
  color: var(--ink);
  text-decoration: none;
  transition:
    border-color var(--dur-fast) ease,
    transform var(--dur-fast) ease,
    box-shadow var(--dur-fast) ease;
}
.price-card:hover,
.price-card:focus-visible {
  border-color: var(--accent);
  transform: translateY(-3px);
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.45);
}
.price-card h3 { font-family: var(--font-display); font-weight: 600; font-size: 1.25rem; margin: 0; }
.price-card .price-figure {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 2rem;
  line-height: 1.1;
}
.price-card .price-from {
  font-family: var(--font-body);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-right: 0.15rem;
}
.price-card p { color: var(--ink-soft); font-size: 0.92rem; margin: 0.35rem 0 0; flex: 1; }
.price-card .price-cue {
  margin-top: 1.1rem;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--accent);
  transition: color var(--dur-fast) ease;
}
.price-card:hover .price-cue { color: var(--accent-hover); }
.price-card--featured { border-color: var(--accent); }
.price-card .price-flag {
  align-self: flex-start;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: var(--accent-grad);
  color: #141311;
  padding: 0.25rem 0.7rem;
  border-radius: var(--radius-pill);
  margin-bottom: 0.6rem;
}
/* Side by side, the flag used to push the featured card's heading, price
   and copy down while its neighbours stayed put. Out of flow here, with
   every card reserving the same space, so the three line up row for row.
   Stacked on a phone there is nothing to line up with, so the flag stays
   in normal flow and no card carries dead space at the top. The same
   defect and the same fix apply to .tier-flag on the pricing page. */
@media (min-width: 48rem) {
  .price-card { padding-top: 3.9rem; }
  .price-card .price-flag {
    position: absolute;
    top: 1.6rem;
    left: 1.6rem;
    margin-bottom: 0;
  }
}

/* --------------------------------------------------------------------------
   10. Pricing, columns divided by hairlines, not boxed cards
   -------------------------------------------------------------------------- */
.tiers {
  display: grid;
  grid-template-columns: 1fr;
  margin-top: 2.5rem;
  border-top: 1px solid var(--hairline);
}
@media (min-width: 52rem) {
  .tiers { grid-template-columns: 1fr 1fr 1fr; }
  .tier + .tier { border-left: 1px solid var(--hairline); border-top: 0; }
}
.tier {
  padding: 2rem clamp(1rem, 2.5vw, 2rem);
  display: flex;
  flex-direction: column;
  position: relative;
}
/* Same flag-in-flow defect as .price-flag on the homepage, different
   class name. Once the tiers sit in a row, every tier in a flagged group
   reserves the same space so the headings and prices stay on one line.
   The care plans below use .tier without a flag, so they are untouched. */
@media (min-width: 52rem) {
  .tiers--flagged .tier { padding-top: 4.4rem; }
}
/* Hover/focus: a 2px accent rule draws in along the tier's top edge to show
   it's interactive. All three tiers are identical at rest, no tier gets
   motion-based prominence by default. */
.tier::before {
  content: "";
  position: absolute;
  top: -1px; left: 0; right: 0;
  height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-base) var(--ease-out);
}
.tier:hover::before,
.tier:focus-within::before { transform: scaleX(1); }
.tier + .tier { border-top: 1px solid var(--hairline); }
.tier--featured { background: var(--bg-alt); }
/* Inside a band section, bg-alt would vanish, lift the featured tier onto
   the paper surface instead so it still reads as highlighted */
.section--band .tier--featured { background: var(--paper); }
.tier .tier-flag {
  align-self: flex-start;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: var(--accent-grad);
  color: #141311;
  padding: 0.25rem 0.7rem;
  border-radius: var(--radius-pill);
  margin-bottom: 0.9rem;
}
@media (min-width: 52rem) {
  .tier .tier-flag {
    position: absolute;
    top: 2rem;
    left: clamp(1rem, 2.5vw, 2rem);
    margin-bottom: 0;
  }
}
.tier h3 { font-family: var(--font-display); font-weight: 600; font-size: 1.4rem; }
.tier .tier-price {
  font-family: var(--font-display);
  font-size: 2.1rem;
  font-weight: 600;
  margin: 0.25rem 0 0.1rem;
}
.tier .tier-from {
  font-family: var(--font-body);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-right: 0.15rem;
}
/* The "/month" suffix on the care plans, so they use the same price
   system as the build tiers instead of one-off inline styles. */
.tier .tier-per {
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 400;
  color: var(--ink-soft);
}
.tier .tier-monthly { color: var(--ink-soft); font-size: 0.9rem; margin-bottom: 1.25rem; }
.tier ul {
  list-style: none;
  margin: 0 0 1.5rem;
  padding: 0;
  flex: 1;
}
.tier li {
  padding: 0.5rem 0 0.5rem 1.5rem;
  border-top: 1px solid var(--hairline);
  font-size: 0.92rem;
  position: relative;
}
.tier li::before {
  content: "";
  position: absolute;
  left: 0; top: 1.05rem;
  width: 0.55rem; height: 0.32rem;
  border-left: 2px solid var(--accent);
  border-bottom: 2px solid var(--accent);
  transform: rotate(-45deg);
}

/* --------------------------------------------------------------------------
   11. Portfolio, offset title-only index + project detail pages
   --------------------------------------------------------------------------
   The listing is deliberately asymmetric: each entry sits at a different
   width/offset on a 12-column grid, linking through to a dedicated project
   page. Each entry pairs its title with a small browser-framed capture of
   the site's front page (or a labelled empty frame while in development). */
.work-index {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  row-gap: clamp(3.5rem, 9vw, 6.5rem);
  margin-top: clamp(1rem, 3vw, 2.5rem);
}
.work-entry {
  display: grid;
  gap: 1.5rem 2.5rem;
  grid-template-columns: 1fr;
  align-items: center;
  text-decoration: none;
}
@media (min-width: 44rem) {
  .work-entry { grid-template-columns: minmax(0, 1fr) minmax(11rem, 15rem); align-items: stretch; }
  /* The framed shot used to sit at a fixed 16:10 whatever the business
     name beside it did, so the two columns never lined up. Let the frame
     fill the row instead and the shot crop to whatever height that is. */
  .work-entry__thumb { display: flex; flex-direction: column; }
  .work-entry__shot { flex: 1; aspect-ratio: auto; min-height: 7.5rem; }
}
.work-entry__text { display: block; max-width: 34ch; }
/* The thumbnail reuses .walk-frame/.walk-chrome (all-span markup, since it
   lives inside the <a>) */
.work-entry__thumb { display: block; }
.work-entry__thumb .walk-chrome { display: flex; }
.work-entry__shot {
  display: block;
  aspect-ratio: 16 / 10;
  overflow: hidden;
}
.work-entry__shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
  transition: transform var(--dur-slow) var(--ease-out);
}
.work-entry:hover .work-entry__shot img,
.work-entry:focus-visible .work-entry__shot img { transform: scale(1.04); }
/* No capture yet (in development / open slot): a quiet labelled frame */
.work-entry__shot--empty {
  display: grid;
  place-items: center;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
/* The offsets: large opener, a right-shifted second, an indented third */
.work-entry--1 { grid-column: 1 / span 9; }
.work-entry--2 { grid-column: 6 / span 7; }
.work-entry--3 { grid-column: 2 / span 8; }
@media (max-width: 44rem) {
  .work-entry--1, .work-entry--2, .work-entry--3 { grid-column: 1 / -1; }
}
.work-entry__meta {
  display: block;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 0.7rem;
}
.work-entry__title {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.06;
  letter-spacing: -0.01em;
  color: var(--ink);
  /* underline draws in on hover/focus via background-size */
  background-image: linear-gradient(var(--accent), var(--accent));
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% 2px;
  transition: background-size var(--dur-base) var(--ease-out), color var(--dur-base) ease;
  padding-bottom: 0.08em;
}
.work-entry--1 .work-entry__title { font-size: clamp(2.4rem, 6vw, 4.4rem); }
.work-entry--2 .work-entry__title { font-size: clamp(2rem, 4.6vw, 3.2rem); }
.work-entry--3 .work-entry__title { font-size: clamp(1.8rem, 4vw, 2.8rem); }
.work-entry__cue {
  display: block;
  margin-top: 1rem;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--accent);
  transition: color var(--dur-fast) ease;
}
.work-entry:hover .work-entry__title,
.work-entry:focus-visible .work-entry__title { background-size: 100% 2px; }
.work-entry:hover .work-entry__cue { color: var(--accent-hover); }

/* ---- Case-study stat cards (template, see work-milners.html) ----
   Three cards: icon + headline number + short label. Only ever populated
   with real, measured numbers (change log item 8 / trust rule item 7). */
.stat-cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1px;
  background: var(--hairline);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-soft);
  overflow: hidden;
  margin-top: 2rem;
}
@media (min-width: 40rem) { .stat-cards { grid-template-columns: 1fr 1fr 1fr; } }
.stat-card {
  background: var(--paper);
  padding: 1.25rem 1.4rem;
  display: flex;
  align-items: center;
  gap: 0.9rem;
}
.stat-card svg { width: 1.4rem; height: 1.4rem; flex: none; color: var(--accent); }
.stat-card .stat-number {
  display: block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.35rem;
  line-height: 1.1;
}
.stat-card .stat-label {
  display: block;
  font-size: 0.82rem;
  color: var(--ink-soft);
}

/* ---- Project detail pages ---- */
.project-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: 1.5rem 2.5rem;
  margin-top: 2.5rem;
  padding-top: 1.75rem;
  border-top: 1px solid var(--hairline);
}
.project-facts h3 {
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 0.2rem;
}
.project-facts p { margin: 0; }

/* Walkthrough frames: real full-page screenshots inside a minimal browser
   chrome. The featured frame pans through the page as you scroll
   (script.js); the rest are directly scrollable. */
.walk-frame {
  border: 1px solid var(--hairline);
  border-radius: var(--radius-soft);
  overflow: hidden;
  background: var(--bg-alt);
}
.walk-chrome {
  display: flex;
  gap: 0.4rem;
  padding: 0.65rem 0.9rem;
  background: var(--bg-alt);
  border-bottom: 1px solid var(--hairline);
}
.walk-chrome span {
  width: 0.6rem; height: 0.6rem;
  border-radius: 50%;
  background: var(--hairline);
}
.walk-viewport {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
}
.walk-viewport img {
  width: 100%;
  height: auto;
  display: block;
}
/* Manually scrollable frames (services/booking/etc), keyboard-focusable.
   Each piece is a contained scroll area: scrolling inside it never moves
   the wider page (overscroll-behavior stops the chaining), and the in-frame
   scrollbar is always visible so it's obvious the capture scrolls. */
.walk-viewport--scroll {
  overflow-y: auto;
  overscroll-behavior-y: contain;
  scrollbar-width: thin;                        /* Firefox */
  scrollbar-color: var(--accent) var(--paper);  /* Firefox */
}
/* Chromium/Safari: styling the scrollbar swaps the auto-hiding overlay
   scrollbar for a classic always-visible one, that visibility is the point */
.walk-viewport--scroll::-webkit-scrollbar { width: 10px; }
.walk-viewport--scroll::-webkit-scrollbar-track { background: var(--paper); }
.walk-viewport--scroll::-webkit-scrollbar-thumb {
  background: var(--accent);
  border-radius: 999px;
  border: 2px solid var(--paper);
}
.walk-viewport--scroll:focus-visible { outline-offset: -2px; }
.walk-featured { max-width: 56rem; margin-top: 2.5rem; }
.walk-caption {
  margin-top: 0.75rem;
  font-size: 0.85rem;
  color: var(--ink-soft);
}
.walk-grid {
  display: grid;
  gap: 3rem 2.5rem;
  grid-template-columns: 1fr;
  margin-top: 3rem;
}
@media (min-width: 52rem) {
  .walk-grid { grid-template-columns: 1fr 1fr; }
}
.walk-grid h3 { margin: 1rem 0 0.25rem; }
.walk-grid p { color: var(--ink-soft); font-size: 0.95rem; }

.badge {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.25rem 0.7rem;
  border-radius: var(--radius-pill);
  background: var(--accent-soft);
  color: var(--ink);
  margin-bottom: 0.75rem;
}

/* --------------------------------------------------------------------------
   11b. About, the person behind the studio
   -------------------------------------------------------------------------- */
/* Heading across the top, then the portrait beside the prose. Stacks to
   one column on a phone, portrait first. */
.bio {
  display: grid;
  gap: 1.75rem;
  grid-template-columns: 1fr;
  align-items: start;
}
@media (min-width: 48rem) {
  .bio {
    grid-template-columns: minmax(0, 24rem) minmax(0, 1fr);
    gap: 2rem 3rem;
  }
}
.bio > div { max-width: 42rem; }
/* Portrait slot, holds the headshot on about.html */
.bio-portrait {
  width: 100%;
  max-width: 18rem; /* stacked on a phone, it should not go full bleed */
  aspect-ratio: 4 / 5;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-soft);
  background: var(--accent-soft);
  overflow: hidden;
  display: grid;
  place-items: center;
  font-family: var(--font-display);
  font-size: 3.5rem;
  color: var(--accent-hover);
}
.bio-portrait img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Beside the prose it fills its column, so it reads as a proper portrait
   rather than a thumbnail dropped next to the text. */
@media (min-width: 48rem) {
  .bio-portrait { max-width: none; }
}

/* --------------------------------------------------------------------------
   12. FAQ accordion, native <details> so it works without JS
   -------------------------------------------------------------------------- */
.faq-list { margin-top: 1.5rem; border-top: 1px solid var(--hairline); }
.faq-list details { border-bottom: 1px solid var(--hairline); }
.faq-list summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding: 1.15rem 0.25rem;
  font-weight: 600;
  font-size: 1.02rem;
}
.faq-list summary::-webkit-details-marker { display: none; }
.faq-list summary::after {
  content: "";
  flex: none;
  width: 0.6rem; height: 0.6rem;
  border-right: 2px solid var(--accent);
  border-bottom: 2px solid var(--accent);
  transform: rotate(45deg);
  transition: transform var(--dur-base) var(--ease-out);
  margin-right: 0.3rem;
}
.faq-list details[open] summary::after { transform: rotate(225deg); }
/* While the JS collapse animation runs, [open] is still true, .is-closing
   lets the chevron start rotating back immediately on click */
.faq-list details.is-closing summary::after { transform: rotate(45deg); }
.faq-list .faq-answer {
  padding: 0 2.5rem 1.25rem 0.25rem;
  color: var(--ink-soft);
  max-width: 42rem;
  overflow: hidden; /* required by the height animation in script.js */
}

/* --------------------------------------------------------------------------
   13. Forms (Contact + Get a Quote)
   -------------------------------------------------------------------------- */
.form { max-width: 36rem; margin-top: 2rem; }
.form-field { margin-bottom: 1.4rem; }
.form-field label {
  display: block;
  font-weight: 600;
  font-size: 0.92rem;
  margin-bottom: 0.35rem;
}
.form-field .hint {
  font-weight: 400;
  color: var(--ink-soft);
  font-size: 0.85rem;
}
.form-field input[type="text"],
.form-field input[type="email"],
.form-field input[type="tel"],
.form-field select,
.form-field textarea {
  width: 100%;
  font: inherit;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--field-line);
  border-radius: var(--radius-soft);
  padding: 0.75rem 0.9rem;
}
.form-field textarea { min-height: 9rem; resize: vertical; }
.form-field input:focus-visible,
.form-field select:focus-visible,
.form-field textarea:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}
.form-field select { appearance: auto; } /* keep native arrow, reliable + accessible */

/* --------------------------------------------------------------------------
   15. Closing CTA band
   -------------------------------------------------------------------------- */
/* Closing CTA: charcoal surface with the contour motif as texture and one
   clear gold action, gold is an accent, never a flood (brand §3). The
   radial gradient quietens the lines behind the centred text. */
.cta-band {
  background-color: var(--bg-alt);
  background-image:
    radial-gradient(ellipse at center, rgba(28, 26, 22, 0.95) 0%, rgba(28, 26, 22, 0.6) 55%, rgba(28, 26, 22, 0.2) 100%),
    url("assets/contour.svg");
  background-size: cover, cover;
  background-position: center, center;
  border-top: 1px solid var(--hairline);
  color: var(--ink);
  text-align: center;
  padding-block: var(--section-pad);
}
.cta-band h2 { color: var(--ink); }
.cta-band p { color: var(--ink-soft); max-width: 34rem; margin-inline: auto; }
.cta-band .btn--primary { margin-top: 1.5rem; }
.cta-band a:not(.btn) { color: var(--accent); }

/* --------------------------------------------------------------------------
   16. Page-level helpers
   -------------------------------------------------------------------------- */
/* Every page header carries the contour motif now, not just the builder.
   The homepage opens on a hero with a live screenshot beside it, so a
   flat text-only band on every other page read as a drop in quality.
   Same text-protection gradient as the hero, pitched lower so the lines
   stay a texture rather than a picture, with the eyebrow in gold above
   the h1 as the brand cue. */
.page-intro {
  padding-block: clamp(3rem, 7vw, 5rem) clamp(2rem, 5vw, 3.5rem);
  background-image:
    linear-gradient(90deg, var(--bg) 30%, rgba(20, 19, 17, 0.85) 62%, rgba(20, 19, 17, 0.55) 100%),
    url("assets/contour.svg");
  background-size: cover, cover;
  background-position: center, right top;
  background-repeat: no-repeat, no-repeat;
  border-bottom: 1px solid var(--hairline);
}
.page-intro .lede { margin-top: 0.25rem; }

/* --------------------------------------------------------------------------
   16c. The package builder (build-your-website.html)
   --------------------------------------------------------------------------
   Interactive configurator: pick a package, toggle add-ons, choose a care
   plan, live estimate stays stuck to the bottom of the screen. Markup and
   pricing come from Sam's quote-form.html, restyled onto the site tokens. */
.builder { max-width: 48rem; }
.builder fieldset.builder-card {
  background: var(--bg);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-soft);
  padding: 1.5rem 1.4rem 1rem;
  margin: 0 0 1.25rem;
  /* A fieldset defaults to min-width: min-content and will not shrink below
     its widest row, which pushed the whole builder past a 320px screen. */
  min-width: 0;
}
/* The legend used to straddle the top border and break the rule. Floating
   it drops it out of the legend slot so it sits inside the box, below an
   unbroken rule and flush left with the options, the same way the pricing
   cards are set. */
.builder legend {
  float: left;
  width: 100%;
  margin: 0;
  padding: 0 0 0.9rem;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
}
.builder legend ~ * { clear: left; }
.builder .opt {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 0.8rem 0.9rem;
  border: 1px solid var(--hairline);
  border-radius: 8px;
  margin-bottom: 0.5rem;
  cursor: pointer;
  transition: border-color var(--dur-fast) ease, background-color var(--dur-fast) ease;
}
.builder .opt:hover { border-color: var(--accent); background: var(--accent-soft); }
.builder .opt:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
.builder .opt input {
  margin-top: 3px;
  accent-color: var(--accent);
  width: 17px; height: 17px;
  flex: none;
}
/* min-width: 0 lets the label wrap instead of setting a floor the row
   cannot go below. flex-basis 8rem keeps the price on the same line on a
   normal phone and lets it drop underneath on a very narrow one. */
.builder .opt .t { flex: 1 1 8rem; min-width: 0; }
.builder .opt .t b { font-weight: 600; }
.builder .opt .t span { display: block; color: var(--ink-soft); font-size: 0.8rem; margin-top: 2px; }
.builder .opt .price { color: var(--accent); font-weight: 600; white-space: nowrap; font-size: 0.9rem; }
.builder .builder-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 1rem;
}
@media (max-width: 35rem) { .builder .builder-grid { grid-template-columns: 1fr; } }
.builder .form-field label .hint { font-weight: 400; }
/* Live estimate, sticks to the bottom while configuring */
.builder .estimate {
  position: sticky;
  bottom: 0.75rem;
  z-index: 40;
  background: var(--paper);
  border: 1px solid #8A6B34; /* soft gold, the one bordered gold moment */
  border-radius: var(--radius-soft);
  padding: 1rem 1.4rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem 1.75rem;
  margin-top: 1.25rem;
  box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.45);
}
@media (max-width: 40rem) {
  /* sit above the fixed Call/Book bar on phones */
  .builder .estimate { bottom: calc(3.6rem + env(safe-area-inset-bottom)); }
}
.builder .estimate .nums { display: flex; gap: 1.75rem; }
.builder .estimate .lab {
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.builder .estimate .val {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.75rem;
  color: var(--accent);
  line-height: 1.1;
}
.builder .estimate .val small { font-size: 0.85rem; color: var(--ink-soft); font-family: var(--font-body); font-weight: 400; }
/* --ink-soft, not --ink-dim: this line carries the payment terms at
   0.72rem, which is below the large-text exemption, so it needs 4.5:1. */
.builder .estimate .note { font-size: 0.72rem; color: var(--ink-soft); flex-basis: 100%; margin: 0; }

/* Definition-style rows (About page, payment terms) */
.fact-rows { border-top: 1px solid var(--hairline); margin-top: 2rem; }
.fact-row {
  display: grid;
  gap: 0.35rem 2.5rem;
  padding: 1.5rem 0;
  border-bottom: 1px solid var(--hairline);
}
@media (min-width: 44rem) {
  .fact-row { grid-template-columns: 14rem 1fr; }
}
.fact-row h3 { margin: 0; }
.fact-row p { color: var(--ink-soft); margin: 0; }

/* --------------------------------------------------------------------------
   16b. Mobile action bar, phones only, injected by script.js
   --------------------------------------------------------------------------
   A thumb-reach Book a call bar fixed to the bottom of the viewport on
   small screens. Progressive enhancement: no JS, no bar, nothing lost.
   One button, so .mobile-bar a carries flex: 1 and fills the width. */
.mobile-bar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 60;
  display: flex;
  gap: 1px;
  background: var(--hairline);
  border-top: 1px solid var(--hairline);
  padding-bottom: env(safe-area-inset-bottom);
}
.mobile-bar a {
  flex: 1;
  text-align: center;
  padding: 0.95rem 0.5rem;
  font-weight: 600;
  font-size: 0.95rem;
  text-decoration: none;
}
.mobile-bar .bar-book { background: var(--accent-grad); color: #141311; }
@media (min-width: 40.0625rem) { .mobile-bar { display: none; } }
@media (max-width: 40rem) {
  body.has-mobile-bar { padding-bottom: calc(3.4rem + env(safe-area-inset-bottom)); }
}

/* --------------------------------------------------------------------------
   17. Motion
   --------------------------------------------------------------------------
   All durations/easings come from the tokens in :root. Everything here is
   neutralised for reduced-motion users by the prefers-reduced-motion block
   near the top of this file (instant state changes, not slower ones), and
   script.js additionally skips its motion behaviours entirely.
   -------------------------------------------------------------------------- */

/* Page load: one brief fade for the whole page, the only entrance motion
   that runs on every navigation */
@media (prefers-reduced-motion: no-preference) {
  body { animation: page-in var(--dur-slow) var(--ease-out); }
}
@keyframes page-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* ---- Scroll reveals ----
   script.js adds .reveal-pending to elements marked data-reveal (and to the
   children of data-reveal-group), then .reveal-visible when they enter the
   viewport, once only, never re-triggered on scroll direction changes.
   Without JS (or with reduced motion) the classes are never added, so
   content is simply visible. transform+opacity only: no layout shift. */
.reveal-pending {
  opacity: 0;
  transform: translateY(16px);
  transition:
    opacity var(--dur-slow) var(--ease-out),
    transform var(--dur-slow) var(--ease-out);
}
.reveal-visible {
  opacity: 1;
  transform: none;
}
/* Group stagger (e.g. the build → launch → look after steps): a short
   ~100ms offset per item, reinforcing the sequence without a slow cascade */
[data-reveal-group] > .reveal-pending:nth-child(2) { transition-delay: 100ms; }
[data-reveal-group] > .reveal-pending:nth-child(3) { transition-delay: 200ms; }
[data-reveal-group] > .reveal-pending:nth-child(4) { transition-delay: 300ms; }
[data-reveal-group] > .reveal-pending:nth-child(5) { transition-delay: 400ms; }
[data-reveal-group] > .reveal-pending:nth-child(n+6) { transition-delay: 500ms; }

/* ---- Portfolio: idle pulse on "coming soon" badges ----
   Slow and shallow, reads as "alive", not as a loading spinner. Ends on
   its resting state so the reduced-motion override (1 iteration, ~0ms)
   leaves the badge looking normal. */
.badge--pulse { animation: badge-pulse 3.2s ease-in-out infinite; }
@keyframes badge-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.55; }
}


/* ---- Form submit states (Contact / Get a Quote) ----
   Calm swap: form fades out, confirmation fades in. The .form-success and
   .form-error blocks live in the page HTML with [hidden]; script.js toggles
   them. Removing [hidden] restarts the fade-in animation. */
.form-fading { opacity: 0; transition: opacity var(--dur-base) ease; }
.form-success {
  max-width: 36rem;
  padding: 1.5rem 0;
  border-top: 2px solid var(--success); /* confirmation = warm gold */
  animation: page-in var(--dur-slow) var(--ease-out);
}
.form-success h2 { font-size: 1.5rem; }
.form-success p { color: var(--ink-soft); }
.form-error {
  color: #F87171;
  font-size: 0.92rem;
  margin: 0 0 1.25rem;
}
