/* ============================================================
   Hearth & Haus — site styles
   Built to the brand guide in 1_Website_Hearth&Haus.docx
   ============================================================ */

:root {
  /* ---- BRAND COLOR PALETTE (from the brand guide) ---- */
  --deep-haus:   #162326;  /* Primary. Navigation, footers, headings, key elements */
  --hearth-ivory:#FFFFFF;  /* Main background. Pure white. (Brand guide
                              names #EFECE4 here — swap it back in this
                              one line if the cream is ever wanted.) */
  --haus-blue:   #6BA0DE;  /* Accent. Links, icons, active states, highlights */
  --warm-stone:  #DDD8CF;  /* Secondary background. Cards, sections, subtle UI */
  --taupe:       #A69F94;  /* Secondary text. Metadata, labels, supporting copy */
  --champagne:   #C8B58A;  /* Premium accent. Details, numbers, special highlights */
  --border-gray: #CFCAC1;  /* Borders and dividers */
  --charcoal:    #2B2B2B;  /* Main body text */
  --muted:       #5F6867;  /* Secondary text. Descriptions, captions */
  --soft-white:  #FAF9F6;  /* Overlays, cards, light sections */

  /* Haus Blue is 2.6:1 on ivory — fine for large text and UI, not for
     small body copy. This darkened step is for links and small labels. */
  --blue-ink:    #2C5F9E;

  /* A pale wash of the accent over white. It rises out of the bottom
     right corner and runs out before it reaches the opposite one, so the
     card reads as lit from that corner rather than evenly tinted. Radial
     rather than linear because a corner is a point, not a direction.
     Kept under .20 alpha — past that the type on it starts to look
     tinted rather than black on a tinted card. */
  --card-wash:
    radial-gradient(135% 115% at 100% 100%,
      rgba(107,160,222,.19)   0%,
      rgba(107,160,222,.11)  28%,
      rgba(107,160,222,.04)  55%,
      rgba(107,160,222,0)    78%),
    #fff;

  /* Champagne has the same problem, worse: #C8B58A on the ivory glass of
     the proof card is about 1.9:1, which is not a colour so much as a
     rumour. This darkened step is for gold marks that have to be read
     rather than merely sensed — the rating stars, small gold labels. */
  --champagne-ink: #8F7024;

  /* ---- type ----
     The brand reference is Aeonik Bold (taxpartners.ca). Aeonik is a
     commercial licence, so Figtree stands in — the same geometric
     grotesque proportions, and the face taxpartners.ca itself falls
     back to. Swap the stack here if the Aeonik licence is bought. */
  --sans: Figtree, "Aeonik", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, Menlo, monospace;

  /* ---- scale ---- */
  --step--1: clamp(0.82rem, 0.80rem + 0.1vw, 0.88rem);
  --step-0:  clamp(1.00rem, 0.97rem + 0.15vw, 1.06rem);
  --step-1:  clamp(1.15rem, 1.08rem + 0.32vw, 1.35rem);
  --step-2:  clamp(1.55rem, 1.35rem + 0.9vw, 2.15rem);
  --step-3:  clamp(2.10rem, 1.70rem + 1.9vw, 3.30rem);
  --step-4:  clamp(2.70rem, 2.00rem + 3.3vw, 4.80rem);
  --step-5:  clamp(3.20rem, 2.10rem + 5.4vw, 7.00rem);

  /* ---- rhythm ---- */
  --gutter:  clamp(1.25rem, 4vw, 3.25rem);
  --measure: 62ch;
  --shell:   1280px;
  --radius:      16px;  /* media and cards — the references use generous corners */
  --radius-sm:   10px;
  --pill:      999px;
}

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
/* Someone who has asked the system to cut motion gets the jump instead:
   a long smooth scroll is exactly the kind of movement that setting is
   there to stop. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}
/* The nav is fixed, so an anchor lands its heading underneath the glass
   bar unless the target reserves room for it. Sized against the scrolled
   bar, which is what is on screen by the time the scroll settles. */
#main, #services, #work, #process, #quality, #contact, #faq, #start, .cf-form {
  scroll-margin-top: clamp(80px, 9vw, 104px);
}
body {
  margin: 0;
  background: var(--hearth-ivory);
  color: var(--charcoal);
  font-family: var(--sans);
  font-size: var(--step-0);
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}
img, svg, video { max-width: 100%; height: auto; display: block; }
/* The browser's own [hidden] rule is element-level, so any class that
   sets `display` outranks it and the element stays on screen with the
   attribute set. The testimonial slides are toggled with `hidden` and
   `.tmnl` sets `display: grid`, which is exactly that collision — all
   three were laying out at once. */
[hidden] { display: none !important; }
h1, h2, h3, h4 { margin: 0; font-weight: 700; letter-spacing: -.025em; line-height: 1.08; text-wrap: balance; }
p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
a { color: inherit; }
button { font: inherit; color: inherit; }
:focus-visible { outline: 2px solid var(--haus-blue); outline-offset: 3px; }

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--deep-haus); color: var(--hearth-ivory);
  padding: .75rem 1.25rem; text-decoration: none;
}
.skip-link:focus { left: 0; }

/* Available to screen readers, invisible on screen. The honeypot input
   and the tag fieldset's legend both depend on this — without it they
   render as visible form fields. */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; white-space: nowrap;
  clip: rect(0 0 0 0); clip-path: inset(50%);
}

/* ---------- layout ---------- */
.shell { width: 100%; max-width: var(--shell); margin-inline: auto; padding-inline: var(--gutter); }
.shell--narrow { max-width: 860px; }

/* 8rem top and bottom put 256px of nothing between one section's last
   line and the next one's first. Two thirds of that still separates them
   without the page reading as a series of islands. */
.section { padding-block: clamp(2.2rem, 4.7vw, 4rem); }
.section--minor { padding-block: clamp(1.5rem, 2.9vw, 2.2rem); }
.section--major { padding-block: clamp(2.9rem, 6.1vw, 5.1rem); }

/* The FAQ sits between two dark blocks — the closing plate above it and
   the contact block below — so it is boxed in by colour on both sides
   and needs less padding than a section that has only the gap to
   separate it. Both ends run short of the standard rhythm, and the
   plate above gives up some of its own bottom padding too: the two were
   stacking to 136px of nothing between the plate and the eyebrow. */
#faq {
  padding-top: clamp(1.5rem, 3.2vw, 2.75rem);
  padding-bottom: clamp(1.45rem, 3.05vw, 2.6rem);
}
.section--stone { background: var(--warm-stone); }
.section--white { background: var(--soft-white); }
.section--dark  { background: var(--deep-haus); color: #fff; }
.section--dark .muted { color: #9AA3A2; }
.section--dark .eyebrow { color: var(--champagne); }

.stack { display: flex; flex-direction: column; }
.stack--sm { gap: .85rem; }
.stack--md { gap: 1.6rem; }
.stack--lg { gap: clamp(2.25rem, 4.5vw, 3.75rem); }

/* One column, then two at 810px. That was where auto-fit happened to
   collapse anyway; naming it means the reorder rule below can share the
   number instead of guessing where the layout changed under it. */
.split {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(2rem, 5vw, 5rem);
  align-items: center;
}
@media (min-width: 810px) {
  .split { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* The portrait is capped at 400px but its track was half the shell —
     552px — so 152px of empty column sat between the picture and the
     copy on top of the gap itself. Sizing the track to the picture takes
     that back and leaves the gap doing the separating on its own. */
  .split--bio { grid-template-columns: minmax(0, 400px) minmax(0, 1fr); }
}
.split--reverse > :first-child { order: 2; }

/* ---- stacked: heading, then the picture, then the body ----
   The heading names the section, the picture shows it, the copy explains
   it — so the media belongs between the two halves of the copy, not
   above all of it or below all of it. The copy is one element, which is
   why `display: contents` is here: it flattens the stack so its children
   become grid items in their own right and can be ordered around the
   media. The alternative is a second copy of the markup. */
@media (max-width: 809.98px) {
  .split { gap: 1.55rem; }
  .split > .stack { display: contents; }
  .split > .stack > * { order: 4; }
  .split > .stack > .eyebrow { order: 1; }
  .split > .stack > h2 { order: 2; }
  .split > .media,
  .split > .pair,
  .split > .reach,
  .split > .founder__frame { order: 3; }
  /* the frame is capped at 400px, so it needs telling where to sit once
     it is a row of its own rather than a column */
  .split > .founder__frame { justify-self: center; }
  /* both are desktop-column fine-tuning that means nothing in a stack */
  .split > .reach { align-self: auto; margin-bottom: 0; }
}

.grid { display: grid; gap: clamp(1.25rem, 2.5vw, 1.75rem); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); }

.rule { border: 0; border-top: 1px solid var(--border-gray); margin: 0; }
.section--dark .rule { border-color: rgba(239,236,228,.16); }

/* Section header — heading left, supporting copy right.
   Straight from the reference layouts. */
.sec-head {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 4rem);
  align-items: end;
}
.sec-head__aside { color: var(--muted); max-width: 40ch; padding-bottom: .35rem; }
.section--dark .sec-head__aside { color: #9AA3A2; }
@media (max-width: 820px) {
  .sec-head { grid-template-columns: 1fr; gap: 1.25rem; align-items: start; }
}

/* ---------- type ---------- */
.h-2xl { font-size: var(--step-5); }
.h-xl  { font-size: var(--step-4); }
.h-lg  { font-size: var(--step-3); }
.h-md  { font-size: var(--step-2); }
.h-sm  { font-size: var(--step-1); letter-spacing: -.015em; }

/* Section indicator: a soft-filled pill with a dot, not a rule and a
   line of caps. The two colours are the one accent and a tint of it, so
   the pill takes its whole look from `color` — which is what lets the
   dark and photo sections restate the colour in one line and get the
   fill and the dot along with it. */
/* One word per headline carries the accent. Inherits everything else
   from the heading, so it never breaks the line's weight or tracking. */
.hl { color: var(--haus-blue); }

.eyebrow {
  font-family: var(--sans);
  display: inline-flex; align-items: center; gap: .5rem;
  /* A grid or flex item is blockified, so `inline-flex` alone does not
     stop the pill stretching the full column. Both of these are needed:
     fit-content for the block contexts, justify-self for the grid ones. */
  width: fit-content; justify-self: start;
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .01em;
  padding: .34rem .8rem .34rem .62rem;
  border-radius: var(--pill);
  color: var(--blue-ink);
  background: color-mix(in srgb, currentColor 11%, transparent);
}
.eyebrow::before {
  content: ""; flex: none;
  width: 6px; height: 6px; border-radius: 50%;
  background: currentColor;
}
/* No color-mix: fall back to a flat tint of the blue rather than to no
   pill at all. The recoloured sections below keep their own fill. */
@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  .eyebrow { background: rgba(44,95,158,.11); }
}

/* ---- the place pill ----
   A location earns a pin where a section label earns a dot, so this
   variant swaps the one for the other and drops the tinted fill for
   real glass — there is a photograph behind it to blur, which the
   section pills elsewhere never have. */
.eyebrow--pin::before { display: none; }
.eyebrow__pin {
  width: 13px; height: 13px; flex: none;
  fill: none; stroke: currentColor;
  stroke-width: 1.3; stroke-linecap: round; stroke-linejoin: round;
  /* the marker reads as a mark beside the words, not as a second word */
  opacity: .78;
}
/* Doubled class on purpose. `.hero .eyebrow` sets the champagne further
   down the file at the same specificity, so at equal weight it wins on
   source order and the pill came out gold. */
.hero .eyebrow.eyebrow--pin {
  color: rgba(255,255,255,.95);
  padding: .4rem .95rem .4rem .68rem;
  background: rgba(16,25,28,.40);
  -webkit-backdrop-filter: blur(11px) saturate(150%);
          backdrop-filter: blur(11px) saturate(150%);
  border: 1px solid rgba(255,255,255,.17);
  /* the glass carries the copy off the picture, so the wide shadow the
     rest of the hero type uses would only fog it */
  text-shadow: none;
}
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .hero .eyebrow.eyebrow--pin { background: rgba(16,25,28,.72); }
}

.lede { font-size: var(--step-1); line-height: 1.5; color: var(--muted); }
.muted { color: var(--muted); }
.prose { max-width: var(--measure); color: var(--muted); }
.prose p + p { margin-top: 1em; }

/* short accent rule — used under headings in the brand mockup */
.tick { width: 46px; height: 3px; background: var(--haus-blue); border-radius: 2px; }
.tick--champagne { background: var(--champagne); }

.link-arrow {
  display: inline-flex; align-items: center; gap: .5em;
  font-size: .92rem; font-weight: 600;
  text-decoration: none; color: var(--blue-ink);
  transition: gap .2s ease;
}
.link-arrow::after { content: "\2192"; transition: translate .2s ease; }
.link-arrow:hover { gap: .75em; }
.section--dark .link-arrow { color: var(--haus-blue); }

/* ---------- buttons — pill with circular arrow badge ---------- */
.btn {
  display: inline-flex; align-items: center; gap: .7em;
  font-size: .95rem; font-weight: 600; letter-spacing: -.01em;
  padding: .6rem .7rem .6rem 1.35rem;
  border-radius: var(--pill);
  border: 1px solid transparent;
  text-decoration: none; cursor: pointer;
  transition: background-color .22s ease, color .22s ease, border-color .22s ease;
}
.btn::after {
  content: "\2192";
  display: grid; place-items: center;
  width: 1.9em; height: 1.9em; flex: none;
  border-radius: 50%; font-size: .85em; line-height: 1;
  transition: background-color .22s ease, color .22s ease;
}
.btn--primary { background: var(--deep-haus); color: var(--soft-white); border-color: var(--deep-haus); }
.btn--primary::after { background: var(--soft-white); color: var(--deep-haus); }
.btn--primary:hover { background: var(--haus-blue); border-color: var(--haus-blue); color: var(--deep-haus); }
.btn--primary:hover::after { background: var(--deep-haus); color: var(--haus-blue); }

.btn--secondary { background: var(--soft-white); color: var(--deep-haus); border-color: var(--border-gray); }
.btn--secondary::after { background: var(--deep-haus); color: var(--soft-white); }
.btn--secondary:hover { border-color: var(--deep-haus); }

.btn--champagne { background: var(--champagne); color: var(--deep-haus); border-color: var(--champagne); }
.btn--champagne::after { background: var(--deep-haus); color: var(--champagne); }
.btn--champagne:hover { background: #B9A375; border-color: #B9A375; }

.btn--blue {
  background: var(--haus-blue); color: var(--deep-haus); border-color: var(--haus-blue);
  position: relative; isolation: isolate; overflow: hidden;
}
.btn--blue::after { background: var(--deep-haus); color: var(--haus-blue); }

/* Same travelling specular sweep as the glass nav bar. isolation keeps the
   z-index:-1 layer above the button's own background but under its label. */
.btn--blue::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  border-radius: inherit;
  background: linear-gradient(105deg,
    transparent 12%,
    rgba(255,255,255,.22) 38%,
    rgba(255,255,255,.48) 48%,
    rgba(255,255,255,.22) 58%,
    transparent 84%);
  background-size: 240% 100%;
  background-position: 120% 0;
  transition: background-position 1.1s cubic-bezier(.25,.8,.3,1);
}
.btn--blue:hover::before { background-position: -40% 0; }
.btn--blue:hover { box-shadow: 0 6px 18px -8px rgba(107,160,222,.85); }

.btn--ghost { background: transparent; color: inherit; border-color: currentColor; }
/* NOT currentColor — this rule sets `color` too, so currentColor would
   resolve to the dark ink and hide the arrow inside its own badge. */
.btn--ghost::after { background: #fff; color: var(--deep-haus); }
.btn--ghost:hover { background: rgba(255,255,255,.1); }

/* a smaller pill, for secondary actions inside a card or a row */
.btn--sm { padding: .45rem .45rem .45rem 1.05rem; font-size: .86rem; }

/* ---- shiny edge ----
   Ported from a React component into plain CSS, since this site has no
   build step. The mechanism is the good part: the conic gradient is
   painted into the BORDER box and the button's own fill into the PADDING
   box, so the travelling light genuinely is the border — no square
   spinning behind a mask, no backdrop to keep in sync with the fill.

   @property is what makes it move: a custom property has to be
   registered with a type before it can be animated, otherwise the angle
   jumps from 0 to 360 with nothing in between. Where it is unsupported
   the border simply sits still, which is a fine resting state.

   The heavier layers — the dot field and the inner sweep — only run on
   hover. A conic gradient repaints every frame it animates, and seven of
   those going at once on a page is exactly the kind of thing that made
   scrolling stutter earlier. */
@property --gradient-angle {
  syntax: "<angle>"; initial-value: 0deg; inherits: false;
}
@property --gradient-percent {
  syntax: "<percentage>"; initial-value: 11%; inherits: false;
}
@property --gradient-shine {
  syntax: "<color>"; initial-value: #C8B58A; inherits: false;
}

.btn--shiny {
  --shiny-bg: var(--deep-haus);
  --shiny-edge: var(--haus-blue);
  --shiny-duration: 5s;

  position: relative; isolation: isolate;
  animation: shiny-angle var(--shiny-duration) linear infinite;
  transition: --gradient-percent 700ms cubic-bezier(.25,1,.5,1),
              --gradient-shine   700ms cubic-bezier(.25,1,.5,1),
              box-shadow .3s ease;
}
/* Doubled-up selector on purpose. Every button variant sets a plain
   `background`, and at equal specificity the variant wins and wipes out
   the two-layer gradient — which is exactly why the border painted
   nothing at first. This has to outrank them. */
.btn.btn--shiny,
button.contact__submit.btn--shiny {
  border: 2px solid transparent;
  background:
    linear-gradient(var(--shiny-bg), var(--shiny-bg)) padding-box,
    conic-gradient(
      from var(--gradient-angle),
      transparent,
      var(--shiny-edge) var(--gradient-percent),
      var(--gradient-shine) calc(var(--gradient-percent) * 2),
      var(--shiny-edge) calc(var(--gradient-percent) * 3),
      transparent calc(var(--gradient-percent) * 4)) border-box;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.05);
}
/* Every shiny button except the form's submit runs a hairline edge. The
   submit is the page's last action and keeps the fuller 2px band. */
.btn.btn--shiny { border-width: .75px; }

/* a wider, brighter band on hover */
.btn.btn--shiny:is(:hover, :focus-visible),
button.contact__submit.btn--shiny:is(:hover, :focus-visible) {
  --gradient-percent: 20%;
  --gradient-shine: #E6DCC2;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.10),
              0 8px 26px -14px rgba(107,160,222,.85);
}
@keyframes shiny-angle { to { --gradient-angle: 360deg; } }

/* the dot field and the inner sweep, both idle until hover */
.btn__spark { position: absolute; inset: 0; z-index: -1; border-radius: inherit; overflow: hidden; pointer-events: none; }
.btn__spark::before,
.btn__spark::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  translate: -50% -50%;
  opacity: 0; transition: opacity .5s ease;
}
/* dots */
.btn__spark::before {
  width: 100%; height: 100%;
  background: radial-gradient(circle at 2px 2px, rgba(255,255,255,.75) .5px, transparent 0) padding-box;
  background-size: 4px 4px;
  border-radius: inherit;
}
/* inner sweep */
.btn__spark::after {
  width: 100%; aspect-ratio: 1;
  background: linear-gradient(-50deg, transparent, var(--shiny-edge), transparent);
  animation: shiny-sweep calc(var(--shiny-duration) * .8) linear infinite paused;
}
.btn--shiny:is(:hover, :focus-visible) .btn__spark::before { opacity: .32; }
.btn--shiny:is(:hover, :focus-visible) .btn__spark::after  { opacity: .5; animation-play-state: running; }
@keyframes shiny-sweep { to { rotate: 360deg; } }

/* each variant says what its own fill is, since that is the padding-box layer */
.btn--blue.btn--shiny  { --shiny-bg: var(--haus-blue); --shiny-edge: #EAF2FF; color: var(--deep-haus); }
.btn--light.btn--shiny { --shiny-bg: var(--soft-white); --shiny-edge: var(--blue-ink); color: var(--deep-haus); }
.contact__submit.btn--shiny { --shiny-bg: var(--deep-haus); }
.gal__cta .btn--ghost.btn--shiny { --shiny-bg: var(--deep-haus); color: #fff; }
/* the blue pill's old specular sweep would fight this one */
.btn--blue.btn--shiny::before { display: none; }
.btn--blue.btn--shiny { overflow: visible; }

@media (prefers-reduced-motion: reduce) {
  .btn--shiny { animation: none; }
  .btn__spark::after { animation: none; }
}

.btn-row { display: flex; flex-wrap: wrap; gap: .8rem; align-items: center; }

/* ---- the primary call to action carries no arrow ----
   The badge is a navigation cue and it earns its place on "See the work",
   which takes you somewhere to look; starting a project is a commitment,
   not a continuation. Two identical badges side by side also flattened
   the hierarchy, and on the blue button the dark badge was the weakest
   contrast on the page. The ::after is emptied rather than removed so
   both buttons still stand exactly the same height. */
.btn--bare { gap: 0; padding-inline: 1.55rem; }
.btn--bare::after { content: ""; width: 0; margin: 0; background: none; }

/* ---------- floating glass nav ----------
   Pill bar that floats over the hero and stays with the viewer.
   Two states: translucent over the dark hero video, then lighter
   and more opaque once it is sitting over ivory sections.
   ----------------------------------------------------------- */
.site-header {
  position: fixed; inset: 0 0 auto 0; z-index: 60;
  padding-inline: var(--gutter);
  /* even top and bottom: the -4px here used to pull the bar up under
     the utility row that sat above it, and there is no row now */
  padding-block: clamp(.5rem, 1.1vw, .85rem);
  pointer-events: none;
}
.site-header > * { pointer-events: auto; }

.glassbar {
  /* Wide over the hero, narrower once the page is moving. Both states
     are real lengths so the browser can interpolate between them —
     `fit-content` has no numeric value to ease toward, which is why this
     is a max-width and not a width. */
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem;
  padding: .62rem .62rem .62rem 1.7rem;
  border-radius: 100px;
  position: relative;
  isolation: isolate;

  /* the glass itself */
  background:
    linear-gradient(180deg, rgba(255,255,255,.20) 0%, rgba(255,255,255,.07) 46%, rgba(255,255,255,.12) 100%),
    rgba(22,35,38,.28);
  -webkit-backdrop-filter: blur(22px) saturate(185%);
          backdrop-filter: blur(22px) saturate(185%);
  border: 1px solid rgba(255,255,255,.24);
  box-shadow:
    0 1px 0 rgba(255,255,255,.42) inset,     /* top specular edge */
    0 -1px 0 rgba(255,255,255,.10) inset,    /* soft bottom lip   */
    0 18px 44px -18px rgba(10,18,20,.55),    /* cast shadow       */
    0 2px 10px -4px rgba(10,18,20,.30);
  transition: background .324s ease, border-color .324s ease, box-shadow .324s ease,
              max-width .446s cubic-bezier(.4,0,.2,1), padding .365s cubic-bezier(.4,0,.2,1);
}

/* moving sheen across the top — the "liquid" part */
.glassbar::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  border-radius: inherit;
  background: linear-gradient(105deg,
    transparent 12%,
    rgba(255,255,255,.16) 38%,
    rgba(255,255,255,.28) 48%,
    rgba(255,255,255,.16) 58%,
    transparent 84%);
  background-size: 240% 100%;
  background-position: 120% 0;
  opacity: .75;
  transition: background-position 1.1s cubic-bezier(.25,.8,.3,1);
}
.glassbar:hover::before { background-position: -40% 0; }

/* Scrolled: it pulls in and sits up, and the glass lifts because it is
   now over light ground rather than the dark hero. */
.site-header.is-scrolled .glassbar {
  max-width: 940px;
  padding: .4rem .4rem .4rem 1.2rem;
  background:
    linear-gradient(180deg, rgba(255,255,255,.72) 0%, rgba(255,255,255,.52) 100%),
    rgba(250,249,246,.55);
  border-color: rgba(22,35,38,.10);
  box-shadow:
    0 1px 0 rgba(255,255,255,.9) inset,
    0 14px 34px -20px rgba(22,35,38,.40),
    0 2px 8px -4px rgba(22,35,38,.14);
}

/* ---- brand ---- */
/* The real lockup, two colourways. The supplied file has a white
   wordmark, so a Deep Haus version is swapped in once the bar goes light. */
.brand { display: block; text-decoration: none; line-height: 0; }
.brand__logo { height: clamp(24px, 2.1vw, 30px); width: auto; }

/* Pure-white lockup, used wherever it sits on a dark ground — the nav
   before it scrolls, and the footer. brightness(0) invert(1) flattens the
   colour, but the artwork is partly transparent, so on its own that lands
   around rgb(203) and reads duller than the type beside it. Each zero-blur
   drop-shadow stamps another white silhouette underneath, compounding the
   alpha until it is genuinely white. */
.brand__logo--onDark,
.footer__logo {
  filter: brightness(0) invert(1)
          drop-shadow(0 0 0 #fff)
          drop-shadow(0 0 0 #fff);
}
.brand__logo--onLight { display: none; }
.is-scrolled .brand__logo--onDark { display: none; }
.is-scrolled .brand__logo--onLight { display: block; }

/* ---- links ---- */
.nav { display: flex; align-items: center; gap: 1.6rem; }
.nav__link {
  text-decoration: none; font-size: .9rem; font-weight: 500;
  color: rgba(255,255,255,.82);
  padding: .3rem 0; position: relative;
  transition: color .22s ease;
}
.nav__link::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -2px; height: 2px;
  background: var(--haus-blue); border-radius: 2px;
  scale: 0 1; transform-origin: left; transition: scale .26s cubic-bezier(.2,.7,.3,1);
}
.nav__link:hover { color: #fff; }
.nav__link:hover::after, .nav__link[aria-current="page"]::after { scale: 1 1; }
.is-scrolled .nav__link { color: var(--muted); }
.is-scrolled .nav__link:hover, .is-scrolled .nav__link[aria-current="page"] { color: var(--deep-haus); }
.is-scrolled .nav__link::after { background: var(--blue-ink); }



.nav__util {
  text-decoration: none; font-size: .82rem; font-weight: 500;
  color: rgba(239,236,228,.68); transition: color .22s ease;
}
.nav__util:hover { color: var(--hearth-ivory); }
.is-scrolled .nav__util { color: var(--taupe); }
.is-scrolled .nav__util:hover { color: var(--deep-haus); }

/* Over the hero the bar is blue glass, so a Haus Blue pill sat on its own
   colour. Black holds that corner instead, and the arrow badge takes the
   blue. Once the bar scrolls onto white ground it goes back to the blue
   pill, which is what the light bar needs. */
.site-header:not(.is-scrolled) .nav .btn--blue {
  background: var(--deep-haus);
  border-color: var(--deep-haus);
  color: #fff;
}
.site-header:not(.is-scrolled) .nav .btn--blue::after {
  background: var(--haus-blue); color: var(--deep-haus);
}
.site-header:not(.is-scrolled) .nav .btn--blue:hover {
  box-shadow: 0 6px 18px -8px rgba(22,35,38,.9);
}

/* north-east arrow variant, per the reference */
.btn--ne::after { content: "\2197"; }
.nav .btn { padding: .5rem .5rem .5rem 1.15rem; font-size: .88rem; }

/* Scrolled, the bar gets smaller as a whole rather than only shorter in
   length. Scaling each piece separately left the arrow badge, radius,
   borders and shadow at full size, so the width change dominated and it
   read as a length-only shrink. One `scale` on the bar shrinks every part
   of it together by 7%, on the same curve as the width. Pinned to the top
   edge so it tucks up rather than drifting down. Desktop only — below
   1041px the links live in the dropdown, not the bar. */
@media (min-width: 1041px) {
  .glassbar {
    transform-origin: 50% 0;
    transition: background .324s ease, border-color .324s ease, box-shadow .324s ease,
                max-width .446s cubic-bezier(.4,0,.2,1), padding .365s cubic-bezier(.4,0,.2,1),
                scale .446s cubic-bezier(.4,0,.2,1);
  }
  .site-header.is-scrolled .glassbar { scale: .93; }
}

/* ---- mobile ---- */
.nav-toggle {
  display: none; align-items: center; gap: .55rem;
  background: rgba(255,255,255,.10); color: var(--hearth-ivory);
  border: 1px solid rgba(255,255,255,.22);
  border-radius: 100px; padding: .5rem 1.05rem;
  font-size: .8rem; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase; cursor: pointer;
}
.nav-toggle__bars { display: grid; gap: 4px; width: 15px; }
.nav-toggle__bars i { display: block; height: 1.5px; background: currentColor; border-radius: 2px; transition: translate .25s ease, rotate .25s ease; }
.nav-toggle[aria-expanded="true"] .nav-toggle__bars i:first-child { translate: 0 2.75px; rotate: 45deg; }
.nav-toggle[aria-expanded="true"] .nav-toggle__bars i:last-child  { translate: 0 -2.75px; rotate: -45deg; }
.is-scrolled .nav-toggle { background: rgba(22,35,38,.06); color: var(--deep-haus); border-color: rgba(22,35,38,.14); }

@media (max-width: 1040px) {
  .nav-toggle { display: inline-flex; }
  /* no shrink on a narrow screen — there is no width to give back */
  .glassbar,
  .site-header.is-scrolled .glassbar { max-width: none; padding-left: 1.15rem; }
  .nav {
    display: none;
    position: absolute; top: calc(100% + .6rem); left: 0; right: 0;
    flex-direction: column; align-items: stretch; gap: .15rem;
    padding: 1rem 1.15rem 1.25rem;
    border-radius: 26px;
    background:
      linear-gradient(180deg, rgba(255,255,255,.16) 0%, rgba(255,255,255,.06) 100%),
      rgba(22,35,38,.62);
    -webkit-backdrop-filter: blur(26px) saturate(180%);
            backdrop-filter: blur(26px) saturate(180%);
    border: 1px solid rgba(255,255,255,.20);
    box-shadow: 0 1px 0 rgba(255,255,255,.35) inset, 0 22px 50px -22px rgba(10,18,20,.6);
  }
  .nav.is-open { display: flex; }
  .nav__link { padding-block: .7rem; font-size: 1rem; }
  .nav__link::after { display: none; }
  .nav__util { padding-block: .45rem; }
  .nav .btn { margin-top: .9rem; justify-content: space-between; }
  .is-scrolled .nav {
    background: linear-gradient(180deg, rgba(255,255,255,.82) 0%, rgba(255,255,255,.68) 100%), rgba(250,249,246,.6);
    border-color: rgba(22,35,38,.10);
  }
}

/* Backdrop-filter unsupported (older Firefox): fall back to solid-ish. */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .glassbar { background: rgba(22,35,38,.88); }
  .site-header.is-scrolled .glassbar { background: rgba(250,249,246,.96); }
  .nav { background: rgba(22,35,38,.94); }
}

/* ---------- 01 hero ---------- */
.hero { position: relative; display: grid; overflow: hidden; }
.hero__media, .hero__content { grid-area: 1 / 1; }

.hero__media {
  width: 100%; height: 100%; object-fit: cover;
  min-height: 78vh; max-height: 860px;
  /* Slow push in and back out, so the still carries the same drift the
     footage did. Scaling from just over 1 means the frame is always
     covered — at exactly 1 a rounding error can show a hairline edge.
     Zoom is `scale` from the centre; framing is `object-position`, never
     transform-origin, which would uncover the opposite edge. */
  /* Only bites where the container is taller than 16:9 and the crop
     turns horizontal, i.e. phones. It favours the main facade there. */
  object-position: 62% 50%;
  scale: 1.02;
  transform-origin: center;
  animation: hero-zoom 13.5s ease-in-out infinite alternate;
}
@keyframes hero-zoom {
  from { scale: 1.02; }
  to   { scale: 1.14; }
}

/* The scrim has to run the way the copy does. This picture has no quiet
   corner — the house fills it edge to edge and the lit windows sit right
   where the headline lands — so the weight goes across rather than up:
   heavy on the left third where the words are, gone by two thirds so the
   facade, the stone and the glow stay at full contrast.

   The angle is 100deg rather than a flat 90, so the edge of the scrim
   falls with the roofline instead of cutting a vertical band across it.
   A light foot underneath settles the bottom edge into the section below.

   Anything under 860px has the copy spanning the full width, so a
   left-weighted scrim would protect nothing there; that case is handled
   in the media query below. */
.hero::after {
  content: ""; grid-area: 1 / 1; z-index: 1;
  background:
    linear-gradient(100deg,
      rgba(11,20,23,.88)  0%,
      rgba(11,20,23,.80) 26%,
      rgba(11,20,23,.56) 46%,
      rgba(11,20,23,.20) 64%,
      rgba(11,20,23,0)   80%),
    linear-gradient(180deg,
      rgba(11,20,23,0)   42%,
      rgba(11,20,23,.30) 80%,
      rgba(11,20,23,.52) 100%);
}
@media (max-width: 860px) {
  /* copy runs full width here, so the picture is weighted overall
     instead, a little heavier towards the foot where the buttons sit */
  .hero::after {
    background: linear-gradient(180deg,
      rgba(11,20,23,.34)  0%,
      rgba(11,20,23,.46) 42%,
      rgba(11,20,23,.72) 100%);
  }
}
.hero__content {
  position: relative; z-index: 2;
  /* Centred in the hero, not pinned to the bottom. The nav is fixed and
     overlays the top, so the extra top padding keeps the optical centre
     correct rather than letting the copy ride up under the bar. */
  align-self: center;
  padding-block: clamp(5rem, 10vw, 8rem) clamp(3rem, 8vw, 5.5rem);
  color: #fff;
}
/* Hold the copy in a left-hand column so it never runs under the proof
   card. The headline sizes to this column, not to the viewport. */
.hero__content > * { max-width: 680px; }
/* Legibility comes from the type itself, not from dimming the picture.
   A wide, low-opacity shadow lifts the copy off whatever is behind it
   without reading as a drop shadow. */
.hero .eyebrow,
.hero h1,
.hero__sub,
.hero__aside { text-shadow: 0 1px 28px rgba(10,18,20,.55), 0 1px 3px rgba(10,18,20,.35); }

.hero .eyebrow { color: var(--champagne); }
.hero h1 {
  font-size: clamp(2.4rem, 1.4rem + 3.4vw, 4rem);
  max-width: 16ch; margin-top: .9rem;
}
.hero__sub { max-width: 46ch; color: rgba(255,255,255,.92); margin-top: 1.5rem; font-size: var(--step-1); line-height: 1.5; }
.hero__actions { margin-top: 2.25rem; }
.hero__aside { margin-top: 1.4rem; }
.hero__aside .link-arrow { color: #DCD8CE; }
.hero__aside .link-arrow:hover { color: #fff; }

/* ---------- proof card (floats on the hero) ---------- */
/* The rail shares .shell with the hero copy, so the card is aligned to
   the same 1280px column rather than to the raw viewport edge. Without
   this the two drift apart on wide displays. */
.hero__rail {
  grid-area: 1 / 1; z-index: 3;
  display: flex; justify-content: flex-end; align-items: flex-start;
  align-self: start;
  /* clears the fixed nav above it */
  padding-block-start: clamp(6.75rem, 13vh, 9.5rem);
  pointer-events: none;
}
.hero__rail > * { pointer-events: auto; }

.proof {
  width: min(280px, 78vw);
  padding: 1.4rem 1.5rem 1.3rem;
  border-radius: 22px;
  /* Glass: the fill is light enough to see the hero through, and the blur
     is deliberately shallow — enough to soften what is behind the card
     without turning it into frosted milk. */
  background: rgba(250,249,246,.56);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
          backdrop-filter: blur(14px) saturate(150%);
  border: 1px solid rgba(255,255,255,.5);
  box-shadow:
    0 1px 0 rgba(255,255,255,.9) inset,
    0 26px 54px -26px rgba(10,18,20,.62),
    0 4px 14px -6px rgba(10,18,20,.28);
}

@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .proof { background: rgba(250,249,246,.92); }
}

.proof__faces { display: flex; align-items: center; margin-bottom: 1.15rem; }
.proof__faces img {
  width: 44px; height: 44px; border-radius: 50%;
  object-fit: cover; flex: none;
  border: 2.5px solid var(--soft-white);
}
.proof__faces img + img { margin-left: -13px; }
.proof__more {
  display: grid; place-items: center;
  width: 44px; height: 44px; border-radius: 50%; flex: none;
  margin-left: -13px;
  background: var(--champagne); color: var(--deep-haus);
  border: 2.5px solid var(--soft-white);
  font-size: .74rem; font-weight: 700; letter-spacing: .01em;
}

.proof__num {
  font-size: 2.6rem; font-weight: 700;
  line-height: 1; letter-spacing: -.035em;
  color: var(--deep-haus); font-variant-numeric: tabular-nums;
}
.proof__label { margin-top: .5rem; font-size: 1rem; font-weight: 500; color: var(--deep-haus); }
.proof__rule { border: 0; border-top: 1px solid var(--border-gray); margin: 1.1rem 0 .5rem; }
.proof__stars {
  color: var(--champagne-ink);
  font-size: 1.15rem;
  /* the glyphs were inheriting the body's 1.62, which hung ten pixels of
     empty leading above and below a single row of stars */
  line-height: 1;
  letter-spacing: .16em;
  margin-bottom: .3rem;
}
/* ink, not the usual mid grey — over glass that grey fell under 2:1 */
.proof__note { font-size: .82rem; color: rgba(22,35,38,.88); }

/* Below the split the hero copy needs the full width, so the card drops
   into the flow under the buttons rather than overlapping the headline. */
@media (max-width: 1100px) {
  .hero__rail {
    justify-content: flex-start; align-self: end;
    padding-block: 0 clamp(2rem, 6vw, 3.5rem);
  }
  .proof { width: min(260px, 82vw); padding: 1.15rem 1.25rem 1.05rem; }
  .proof__num { font-size: 2.15rem; }
}
@media (max-width: 780px) { .hero__rail { display: none; } }


/* ---------- 02 what we stand on ----------
   Three still cards where a marquee used to run. Divided by hairlines
   rather than boxed, so the row reads as one band of related facts
   instead of three competing tiles — the hero above it is already doing
   the shouting, and this only has to be read.
   ------------------------------------------------------------------- */
.trio-band {
  /* a warm band, between the page white and the stone sections, so it
     separates from the hero without becoming a second colour event */
  background: color-mix(in srgb, var(--warm-stone) 42%, #fff);
  border-bottom: 1px solid rgba(22,35,38,.07);
}
/* tighter than a normal minor section: this band is a strip, not a stop */
.section--minor.trio-band { padding-block: clamp(.64rem, 1.19vw, .96rem); }
@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  .trio-band { background: #F1EEE8; }
}
.trio {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  list-style: none; padding: 0;
  /* Across the full shell each column was ~420px round text capped at
     34ch, so the space between columns was mostly empty. Pulled in so
     the gaps are small and the three read as one row. */
  max-width: 980px; margin: 0 auto;
}
.trio__item {
  display: grid; justify-items: center; align-content: start;
  gap: .29rem;
  text-align: center;
  padding: clamp(.13rem, .38vw, .32rem) clamp(.51rem, 1.15vw, 1.02rem);
  border-left: 1px solid rgba(22,35,38,.11);
}
.trio__item:first-child { border-left: 0; }

.trio__ico {
  display: grid; place-items: center;
  width: 52px; height: 52px;
  border-radius: 15px;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.5);
}
.trio__ico svg {
  width: 27px; height: 27px;
  fill: none; stroke-width: 1.45;
  stroke-linecap: round; stroke-linejoin: round;
}
/* One accent, three drawings. The row states three parallel facts, so
   three different hues implied a distinction that is not there — they
   now all sit in the Haus Blue family and the drawings do the
   differentiating instead.

   Duotone rather than line art: a soft blue mass carries the silhouette
   at a glance, ink strokes carry the detail. At this size a pure outline
   reads as a UI glyph, and a row sitting directly under the hero needs
   to read as illustration. */
.trio__ico {
  background:
    linear-gradient(155deg, rgba(107,160,222,.26) 0%, rgba(107,160,222,.10) 100%);
  border: 1px solid rgba(107,160,222,.34);
  transition: transform .32s cubic-bezier(.2,.7,.3,1), box-shadow .32s ease;
}
.trio__ico svg { stroke: var(--blue-ink); }

/* the filled masses */
.trio__ico .t-f, .svc__ico .t-f { fill: rgba(107,160,222,.30); stroke: none; }
.trio__ico .t-f2, .svc__ico .t-f2 { fill: rgba(107,160,222,.15); stroke: none; }
/* the heavier structural lines — spine, deck, fold */
.trio__ico .t-s, .svc__ico .t-s { stroke: #1C4577; stroke-width: 1.6; }
/* fine detail: ruled figures, water, lines of text. Thinner and lighter,
   so it reads as texture rather than as more structure. */
.trio__ico .t-a, .svc__ico .t-a { stroke: rgba(44,95,158,.55); stroke-width: 1.15; }
/* the stamp on the signed document */
.trio__ico .t-seal, .svc__ico .t-seal { fill: var(--haus-blue); stroke: var(--soft-white); stroke-width: 1.3; }
.trio__ico .t-tick, .svc__ico .t-tick { stroke: #fff; stroke-width: 1.9; }

/* A slow rise on hover. Nothing loops on its own — the marquee that used
   to live here was removed precisely because it moved unasked. */
.trio__item:hover .trio__ico {
  transform: translateY(-3px);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.6),
              0 10px 20px -12px rgba(44,95,158,.65);
}
@media (prefers-reduced-motion: reduce) {
  .trio__ico { transition: none; }
  .trio__item:hover .trio__ico { transform: none; }
}
.trio__title {
  font-size: clamp(1rem, .95rem + .2vw, 1.12rem);
  font-weight: 700; letter-spacing: -.02em;
  color: var(--deep-haus);
}
.trio__text {
  font-size: .88rem; line-height: 1.55;
  color: var(--muted); max-width: 34ch;
}

/* ---- arriving ----
   Each column rises as the band is reached, left to right, with its icon
   landing just behind it on a slight overshoot. Gated the same way as
   the work rows and the map: the finished state is what the CSS says by
   default, `is-live` winds it back, and `is-in` runs it — so with no JS
   the band is simply on screen. */
.trio.is-live .trio__item { opacity: 0; translate: 0 18px; }
.trio.is-live .trio__ico  { opacity: 0; scale: .8; }

.trio.is-live.is-in .trio__item {
  animation: trio-rise .62s cubic-bezier(.22,.75,.3,1) var(--d, 0s) both;
}
.trio.is-live.is-in .trio__ico {
  /* the overshoot is what makes the tile feel placed rather than faded */
  animation: trio-pop .54s cubic-bezier(.3,1.5,.45,1) calc(var(--d, 0s) + .06s) both;
}
@keyframes trio-rise { to { opacity: 1; translate: 0 0; } }
@keyframes trio-pop  { to { opacity: 1; scale: 1; } }

@media (prefers-reduced-motion: reduce) {
  .trio.is-live .trio__item,
  .trio.is-live .trio__ico { opacity: 1; translate: none; scale: 1; animation: none; }
}

@media (max-width: 860px) {
  /* stacked, so the divider moves to the top edge of each card */
  .trio { grid-template-columns: 1fr; }
  .trio__item {
    border-left: 0;
    border-top: 1px solid rgba(22,35,38,.11);
    padding-block: 1.4rem;
  }
  .trio__item:first-child { border-top: 0; padding-top: 0; }
}
/* ---------- 03 services (picture + accordion) ---------- */
.svc {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.12fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: start;
  margin-top: clamp(2.25rem, 4.5vw, 3.75rem);
}

/* All four pictures are stacked and cross-faded, so opening a service
   never waits on a download or flashes an empty frame. */
.svc__media {
  position: relative;
  aspect-ratio: 9 / 10;
  border-radius: 14px; overflow: hidden;
  background: var(--warm-stone);
}
.svc__shot {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity .5s ease;
}
.svc__shot.is-on { opacity: 1; }

.svc__list { display: grid; align-content: start; }
.svc__item { border-bottom: 1px solid var(--border-gray); }
.svc__item:last-child { border-bottom: 0; }
.svc__item h3 { font-size: inherit; font-weight: inherit; letter-spacing: normal; line-height: inherit; }

.svc__row {
  --svc-ground: #fff;
  width: 100%; display: flex; align-items: center; gap: 1rem;
  padding: 1.15rem .9rem;
  background: transparent; cursor: pointer; text-align: left;
  /* transparent by default so the open state's box adds no height */
  /* 2.25px so the travelling light has enough edge to show on */
  border: 2.25px solid transparent; border-radius: 10px;
  transition: border-color .3s ease, background-color .3s ease;
}
/* Open row: the border runs deep-haus for three quarters of its length
   and turns haus blue for the last quarter. A border cannot take a
   gradient directly, so the box paints twice — a flat fill clipped to
   padding-box over the gradient clipped to border-box — and the border
   itself goes transparent to let the lower layer through. The fill has
   to be the real ground behind the row, not `transparent`, or the
   gradient shows through the middle of the box as well as its edge. */
.svc__item.is-open .svc__row {
  border-color: transparent;
  background:
    linear-gradient(var(--svc-ground), var(--svc-ground)) padding-box,
    conic-gradient(from var(--gradient-angle),
      transparent 0%,
      transparent 62%,
      rgba(107,160,222,.55) 74%,
      var(--haus-blue) 82%,
      rgba(107,160,222,.55) 90%,
      transparent 100%) border-box,
    linear-gradient(105deg,
      var(--deep-haus) 0%,
      var(--deep-haus) 75%,
      var(--haus-blue) 100%) border-box;
  animation: shiny-angle 3.6s linear infinite;
}
/* A light that circles the open row's border. The conic layer sits
   between the fill and the base gradient and turns on --gradient-angle
   (registered with the shiny buttons, and the same keyframes), so only
   the edge ever shows it. It trails off behind into a solid core,
   so it reads as a moving light rather than a stripe. All Haus Blue. Where @property is
   unsupported it simply sits still. */
@media (prefers-reduced-motion: reduce) {
  .svc__item.is-open .svc__row { animation: none; }
}

/* The same duotone tile as the trio band, a size down to sit in a row.
   The fills and strokes come from the shared .t-* rules up there. */
.svc__ico {
  flex: none; display: grid; place-items: center;
  width: 42px; height: 42px; border-radius: 12px;
  background: linear-gradient(155deg, rgba(107,160,222,.26) 0%, rgba(107,160,222,.10) 100%);
  border: 1px solid rgba(107,160,222,.34);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.5);
}
.svc__ico svg {
  width: 25px; height: 25px;
  fill: none; stroke: var(--blue-ink); stroke-width: 1.45;
  stroke-linecap: round; stroke-linejoin: round;
}
.svc__name {
  flex: 1 1 auto;
  font-size: clamp(1.05rem, .95rem + .35vw, 1.3rem);
  font-weight: 600; letter-spacing: -.02em; color: var(--deep-haus);
}

/* a plus that turns into a cross */
.svc__mark { flex: none; position: relative; width: 16px; height: 16px; }
.svc__mark::before, .svc__mark::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  background: var(--deep-haus);
  translate: -50% -50%;
  transition: rotate .35s cubic-bezier(.2,.7,.3,1), opacity .3s ease;
}
.svc__mark::before { width: 15px; height: 1.5px; }
.svc__mark::after  { width: 1.5px; height: 15px; }
.svc__item.is-open .svc__mark::before { rotate: 45deg; }
.svc__item.is-open .svc__mark::after  { rotate: 45deg; }

/* 0fr to 1fr animates a height the browser works out itself, so the
   panel never needs a hard-coded max-height that clips longer copy */
.svc__panel {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows .42s cubic-bezier(.4,0,.2,1);
}
.svc__panel > div { overflow: hidden; }
.svc__item.is-open .svc__panel { grid-template-rows: 1fr; }
.svc__panel p {
  padding: .1rem .9rem 1.35rem;
  color: var(--muted); font-size: .96rem; line-height: 1.6;
  max-width: 54ch;
}

@media (max-width: 860px) {
  .svc { grid-template-columns: 1fr; }
  .svc__media { aspect-ratio: 4 / 3; }
}
@media (prefers-reduced-motion: reduce) {
  .svc__panel, .svc__shot, .svc__mark::before, .svc__mark::after { transition: none; }
}

/* ---------- 05 where we build (Texas + drive times) ----------
   The outline draws itself, the fill arrives behind it, the routes and
   cities follow, and the Port Aransas marker lands last and stays.
   pathLength="1" lets the draw run in 0-1 units, so the dash maths does
   not care how long the real path is. */
.where {
  display: grid;
  grid-template-columns: minmax(0, .92fr) minmax(0, 1.08fr);
  gap: clamp(1.75rem, 4vw, 4rem);
  /* stretch, not centre: the two columns then end on the same line, which
     is what keeps the cards from hanging below the map */
  align-items: stretch;
}
/* Copy takes what it needs, the cards take the rest of the column.

   The aspect-ratio is doing the real work. The map frame is square, so
   its height equals the right column's width; the two columns divide the
   same free space at .92 : 1.08, so this column's height must be its own
   width x 1.08/.92 for the two to finish level. Expressed as a ratio
   (.92/1.08 = .852) it holds at any viewport without measuring anything.
   min-height:0 lets the card rows below shrink into whatever is left
   instead of pushing the column taller than the map. */
.where__copy {
  display: grid; grid-template-rows: auto 1fr;
  gap: clamp(1.1rem, 2.2vw, 1.75rem);
}
.where .lede { max-width: 44ch; }

/* four figures, two by two */
.stats--quad { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(1.25rem, 2.5vw, 2rem); }
.where .stat__num { color: var(--blue-ink); }
.where .stat__label { color: var(--muted); }

@media (max-width: 900px) {
  .where { grid-template-columns: 1fr; }
}

/* The frame stretches to the height of the column beside it rather than
   holding a square. That is what keeps the stat cards from hanging below
   the map: the map grows to meet them, instead of the cards being
   squeezed to meet the map. A minimum keeps it usable when the copy
   beside it is short. */
.hhmap { display: grid; grid-template-rows: 1fr; height: 100%; }
.hhmap__frame {
  position: relative; margin: 0;
  min-height: clamp(380px, 34vw, 560px);
  border-radius: var(--radius);
  overflow: hidden;
  /* A haus-blue shine travelling the rim, at a fraction of the buttons'
     strength — they run solid brand colour, this runs it at a fifth.
     Three layers: the card's own fill clipped to padding-box on top, the
     travelling conic under it, and a flat border-gray beneath that, so
     the three quarters of the ring where the conic is transparent still
     read as a normal border instead of vanishing.
     The map SVG is inset:0, which resolves against the padding box, so
     it covers the fill and leaves the ring showing. */
  border: 1.5px solid transparent;
  background:
    linear-gradient(#E4EDF4, #E4EDF4) padding-box,
    conic-gradient(from var(--gradient-angle),
      transparent 0,
      rgba(107,160,222,.20)  6%,
      rgba(107,160,222,.50) 12%,
      rgba(107,160,222,.20) 18%,
      transparent 24%) border-box,
    linear-gradient(var(--border-gray), var(--border-gray)) border-box;
  animation: shiny-angle 10s linear infinite;
  /* the ledger's ramp at full size — this card is larger than the ledger,
     so the geometry carries over unscaled */
  box-shadow:
    2px 3px 0 0 rgba(22,35,38,.14),
    4px 6px 4px -1px rgba(22,35,38,.17),
    10px 14px 13px -5px rgba(22,35,38,.20),
    22px 30px 34px -14px rgba(22,35,38,.26),
    38px 54px 74px -24px rgba(22,35,38,.34);
}
.hhmap__svg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  display: block; cursor: grab;
  touch-action: pan-y;            /* the page keeps vertical scrolling */
}
.hhmap__frame.is-dragging .hhmap__svg { cursor: grabbing; }
.hhmap__svg:focus-visible { outline: 2px solid var(--haus-blue); outline-offset: -4px; }

/* level of detail: small streets are noise with the whole island in
   frame, and place names are in the way once it is not */
.hhmap__rd-sm, .hhmap__rd-md, .hhmap__place, .hhmap__pinlabel { transition: opacity .3s ease; }
.hhmap__svg.is-far .hhmap__rd-sm { opacity: 0; }
.hhmap__svg.is-far .hhmap__rd-md { opacity: .45; }
.hhmap__svg.is-far .hhmap__pinlabel { opacity: 0; }
.hhmap__svg.is-near .hhmap__place:not(.hhmap__place--town) { opacity: 0; }

.hhmap__place {
  font-family: var(--mono); font-size: 6.2px;
  letter-spacing: .16em; fill: #93A0A0;
}
.hhmap__place--town  { font-size: 8px; fill: var(--deep-haus); letter-spacing: .2em; }
.hhmap__place--water { fill: #7C9AB4; font-style: italic; }
.hhmap__officelabel {
  font-family: var(--mono); font-size: 7px;
  letter-spacing: .14em; fill: var(--blue-ink);
}
.hhmap__pinlabel {
  font-family: var(--mono); font-size: 6.4px;
  letter-spacing: .1em; fill: #7A6838;
  paint-order: stroke; stroke: rgba(252,251,248,.9); stroke-width: 2.6px;
}

.hhmap__pin { cursor: pointer; }
.hhmap__halo { opacity: 0; transition: opacity .25s ease; }
.hhmap__dot  { transition: fill .25s ease, stroke .25s ease; }
.hhmap__pin:hover .hhmap__halo,
.hhmap__pin:focus-visible .hhmap__halo,
.hhmap__pin.is-active .hhmap__halo { opacity: .28; }
.hhmap__pin:hover .hhmap__dot,
.hhmap__pin:focus-visible .hhmap__dot,
.hhmap__pin.is-active .hhmap__dot { fill: #B9A375; }
.hhmap__pin:focus { outline: none; }
.hhmap__pin:focus-visible .hhmap__dot { stroke: var(--blue-ink); stroke-width: 2.4; }

/* the city limits, traced on once when the section is first reached */
.hhmap__outline { transition: fill-opacity 1.1s ease .9s; }
.hhmap__outline.is-drawn { fill-opacity: .07; }

/* controls and captions sit over the frame as plain HTML, so they keep
   their size without fighting the viewBox */
.hhmap__ctl {
  position: absolute; top: .7rem; right: .7rem; z-index: 2;
  display: flex; flex-direction: column; gap: .35rem;
}
.hhmap__ctl button {
  width: 34px; height: 34px; display: grid; place-items: center;
  border-radius: 9px; cursor: pointer;
  background: rgba(255,255,255,.92);
  border: 1px solid var(--border-gray);
  color: var(--deep-haus); font-size: 1rem; line-height: 1;
  transition: background-color .2s ease, border-color .2s ease;
}
.hhmap__ctl button:hover { background: var(--haus-blue); border-color: var(--haus-blue); color: #fff; }

.hhmap__rockport {
  position: absolute; top: .8rem; left: .9rem; z-index: 2;
  display: flex; align-items: center; gap: .45rem;
  color: #8A7538;
  font-family: var(--mono); font-size: .6rem;
  letter-spacing: .1em; text-transform: uppercase; line-height: 1.35;
  pointer-events: none;
}
.hhmap__scale {
  position: absolute; left: .9rem; bottom: .8rem; z-index: 2;
  display: flex; align-items: center; gap: .5rem;
  color: var(--muted); font-family: var(--mono); font-size: .6rem;
  letter-spacing: .1em; pointer-events: none;
}
.hhmap__scale-bar {
  display: block; height: 6px; width: 60px;
  border: 1px solid currentColor; border-top: 0;
  transition: width .18s ease;
}
.hhmap__hint {
  position: absolute; inset: auto 0 0 0; z-index: 1;
  padding: .5rem .9rem 1.6rem;
  background: linear-gradient(to top, rgba(252,251,248,.92), transparent);
  color: var(--muted); text-align: center;
  font-family: var(--mono); font-size: .62rem;
  letter-spacing: .1em; text-transform: uppercase;
  pointer-events: none; transition: opacity .4s ease;
}
.hhmap__frame.is-live .hhmap__hint { opacity: 0; }
/* OpenStreetMap data carries an attribution condition, so the credit
   stays — but as a corner mark inside the map, the way every mapping
   library does it, rather than a line of grey text under the section. */
.hhmap__credit {
  position: absolute; right: .5rem; bottom: .4rem; z-index: 2;
  font-size: .58rem; letter-spacing: .01em; line-height: 1;
  color: rgba(22,35,38,.42);
  background: rgba(255,255,255,.62);
  padding: .16rem .34rem; border-radius: 4px;
}
.hhmap__credit a { color: rgba(22,35,38,.6); }

/* the three places, in a row under the whole block */
.hhmap__list {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
  gap: .75rem; margin-top: clamp(1.75rem, 3.5vw, 2.75rem);
}
.hhmap__item {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center; gap: .9rem;
  min-height: 44px;                  /* a touch target as much as a row */
  padding: .9rem 1.05rem;
  background: var(--soft-white);
  border: 1px solid var(--border-gray);
  border-radius: 12px;
  text-decoration: none; color: inherit;
  transition: background-color .25s ease, border-color .25s ease;
}
.hhmap__item:hover, .hhmap__item.is-active {
  background: rgba(107,160,222,.08);
  border-color: var(--haus-blue);
}
.hhmap__item-n {
  display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: 50%; flex: none;
  background: var(--champagne); color: var(--deep-haus);
  font-size: .95rem; font-weight: 700; font-variant-numeric: tabular-nums;
}
.hhmap__item-t { display: block; font-size: 1rem; font-weight: 600; color: var(--deep-haus); }
.hhmap__item-m { display: block; margin-top: .15rem; font-size: .82rem; color: var(--muted); }
.hhmap__item-d {
  font-family: var(--mono); font-size: .8rem; color: #8A7538;
  white-space: nowrap; font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
  .hhmap__item, .hhmap__halo, .hhmap__dot, .hhmap__outline,
  .hhmap__rd-sm, .hhmap__rd-md, .hhmap__place, .hhmap__pinlabel { transition: none; }
  .hhmap__frame { animation: none; }
}

/* ---------- the four figures, as cards ----------
   Corner mark, the number with a short qualifier beside it, the label
   underneath. The reference this follows puts a percentage change in the
   qualifier slot; these are standing facts rather than moving metrics,
   so that slot carries a true phrase instead of an invented delta. */
.statcards {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  /* two equal rows that divide whatever height is left, so the block
     finishes level with the map instead of running past it */
  grid-template-rows: repeat(2, minmax(0, 1fr));
  gap: clamp(.75rem, 1.4vw, 1.1rem);
}
.statcard {
  position: relative; isolation: isolate; overflow: hidden;
  display: grid; align-content: space-between;
  gap: clamp(.9rem, 2vw, 1.6rem);
  min-height: 132px;
  padding: clamp(.95rem, 1.6vw, 1.35rem);
  background: #fff;
  border: 1px solid var(--border-gray);
  border-radius: 12px;
  /* The ledger's hard-to-soft ramp, geometry scaled to about 0.45: these
     cards are a fifth of its area, and its 74px far stop on a 132px box
     is fog rather than shade. Same five steps, same opacities — first
     stop unblurred and unspread so the silhouette stays readable. */
  box-shadow:
    1px 2px 0 0 rgba(22,35,38,.09),
    2px 3px 2px -1px rgba(22,35,38,.11),
    5px 7px 6px -3px rgba(22,35,38,.13),
    10px 14px 15px -7px rgba(22,35,38,.17),
    17px 24px 33px -11px rgba(22,35,38,.22);
  transition: border-color .25s ease, box-shadow .25s ease;
}
/* Dot field, drifting. The layer is oversized and travels exactly one
   tile, so the loop is seamless and nothing ever shows an edge. Only
   `translate` animates — a background-position drift would repaint the
   whole card every frame, four cards over. */
.statcard::before {
  content: "";
  position: absolute; inset: -10px; z-index: -1;
  background: radial-gradient(circle at 2px 2px,
              rgba(44,95,158,.16) .6px, transparent 0);
  background-size: 6px 6px;
  animation: statcard-drift 9s linear infinite;
}
@keyframes statcard-drift { to { translate: 6px 6px; } }
.statcard:hover {
  border-color: var(--haus-blue);
  /* the same five stops pushed out a step, so the card lifts rather
     than trading its cast shadow for a different one */
  box-shadow:
    1px 2px 0 0 rgba(22,35,38,.10),
    3px 4px 3px -1px rgba(22,35,38,.12),
    7px 10px 9px -3px rgba(22,35,38,.14),
    14px 19px 21px -7px rgba(22,35,38,.18),
    24px 34px 46px -11px rgba(22,35,38,.23);
}
.statcard__mark { display: block; width: 19px; height: 19px; }
.statcard__mark svg {
  width: 100%; height: 100%;
  fill: none; stroke: var(--deep-haus); stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}
.statcard__fig { display: flex; align-items: baseline; gap: .55rem; flex-wrap: wrap; }
.statcard__num {
  font-size: clamp(1.75rem, 1.2rem + 1.6vw, 2.7rem);
  font-weight: 700; letter-spacing: -.035em; line-height: 1;
  font-variant-numeric: tabular-nums;
  /* solid first, so anything that cannot clip a gradient to text still
     gets a readable figure rather than a transparent one */
  color: var(--blue-ink);
  /* Blue into lighter blue, left to right, like the reference bar. It
     stops at the brand's light blue rather than running out to white:
     on a white card the pale tail would eat the last glyph. */
  background: linear-gradient(95deg,
      var(--blue-ink) 0%,
      #4176BE 52%,
      var(--haus-blue) 100%);
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
}
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .statcard__num { -webkit-text-fill-color: currentColor; background: none; }
}
.statcard__note { font-size: .82rem; color: var(--taupe); }
.statcard__label {
  font-size: .7rem; font-weight: 600;
  letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted); line-height: 1.35;
}
@media (max-width: 520px) { .statcards { grid-template-columns: 1fr; } }
@media (prefers-reduced-motion: reduce) { .statcard::before { animation: none; } }

/* ---------- 06 stats ---------- */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 165px), 1fr)); gap: clamp(1.5rem, 4vw, 3rem); }
.stat__num { font-size: var(--step-3); font-weight: 700; line-height: 1; color: var(--champagne); font-variant-numeric: tabular-nums; letter-spacing: -.03em; }
.stat__label { margin-top: .7rem; font-size: .78rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; color: #9AA3A2; }

/* ---------- 07 open book ----------
   The claim is that every line item is visible, so the section shows
   one. Built as a real table rather than a picture of a table: it stays
   sharp at any zoom, a screen reader reads it as data, and it costs
   almost nothing. Light ground on purpose, because an invoice reads as
   an invoice on paper white, and because the section below it is dark.
   ------------------------------------------------------------------- */
/* Copy beside the card, but the card takes the larger share — it is a
   dashboard and needs the room. Its own type and spacing are scaled down
   to suit (see .ledger--tight below) so the table still holds one line
   per row at this width. */
.openbook {
  display: grid;
  grid-template-columns: minmax(0, .74fr) minmax(0, 1.26fr);
  gap: clamp(1.5rem, 3vw, 2.75rem);
  /* stretch, so both columns end on the same line */
  align-items: stretch;
}
/* The copy spreads to the card's height rather than the card being cut
   down to the copy's — matching them by deleting table rows would be
   fixing the layout by throwing away the content. */
.openbook__copy { align-content: space-between; }
@media (max-width: 1000px) { .openbook { grid-template-columns: 1fr; } }
.openbook__copy { display: grid; gap: 1.15rem; align-content: center; }
.openbook__copy .prose { max-width: 40ch; }
.openbook__copy .prose { max-width: 46ch; }

/* Stacked, the ledger goes between the heading and the body — same
   reasoning as the splits. This has to sit after the .openbook__copy
   rules above rather than inside the media query beside them: those
   declare `display: grid` further down the file and would win. */
@media (max-width: 1000px) {
  .openbook { gap: 1.45rem; }
  .openbook > .openbook__copy { display: contents; }
  .openbook__copy > * { order: 4; }
  .openbook__copy > .eyebrow { order: 1; }
  .openbook__copy > h2 { order: 2; }
  .openbook > .ledger { order: 3; }
}

.ledger {
  margin: 0;
  background: #fff;
  border: 1px solid rgba(207,202,193,.5);
  border-radius: 20px;
  padding: clamp(1rem, 2vw, 1.5rem);
  display: grid; gap: clamp(.85rem, 1.6vw, 1.15rem);
  /* Hard to soft, in five steps. The first stop carries no blur and no
     negative spread, so it sits hard against the edge and the card's own
     silhouette stays readable in the shade; each stop after it blurs
     wider and reaches further, so the shade breaks up as it travels.
     Offsets and negative spreads on every stop is what left a band of
     clean ground at the edge and put the darkest point 12px out. */
  box-shadow:
    2px 3px 0 0 rgba(22,35,38,.14),
    4px 6px 4px -1px rgba(22,35,38,.17),
    10px 14px 13px -5px rgba(22,35,38,.20),
    22px 30px 34px -14px rgba(22,35,38,.26),
    38px 54px 74px -24px rgba(22,35,38,.34);
}

/* header: mark, name over subtitle, status pill */
/* the card is a control now, so it has to say so */
.ledger { position: relative; }
.ledger__hit {
  position: absolute; inset: 0; z-index: 3;
  width: 100%; height: 100%; padding: 0;
  background: none; border: 0; cursor: zoom-in;
  border-radius: inherit;
}
.ledger__hit:focus-visible { outline: 2px solid var(--haus-blue); outline-offset: 4px; }

/* A small push on hover: the card lifts toward the viewer and its shade
   stretches out to match, then gives a little under the click. Pointer
   devices only, so a tap on a phone does not leave it stuck raised. */
@media (hover: hover) and (pointer: fine) {
  .ledger {
    transition: translate .42s cubic-bezier(.2,.7,.3,1), scale .42s cubic-bezier(.2,.7,.3,1),
                box-shadow .42s cubic-bezier(.2,.7,.3,1);
  }
  .ledger:hover {
    translate: -3px -6px;
    scale: 1.012;
    box-shadow:
      3px 5px 0 0 rgba(22,35,38,.12),
      6px 10px 6px -1px rgba(22,35,38,.15),
      14px 22px 18px -5px rgba(22,35,38,.19),
      30px 44px 44px -14px rgba(22,35,38,.25),
      50px 74px 92px -24px rgba(22,35,38,.33);
  }
  .ledger:active {
    translate: -1px -2px;
    scale: .995;
    transition-duration: .12s;
  }
}

/* ---- the open book, full screen ----
   The card opens the whole window. The dialog is the ground; .dash is
   the screen laid on it, and only the body scrolls, so the header stays
   put over a long invoice list.

   Everything animates in from `is-live`, which lands one frame after the
   dialog opens. Each element carries its own --d in the markup, so the
   order of the sequence is readable where the elements are rather than
   being a table of magic numbers in here. */
.bigview {
  width: 100vw; max-width: 100vw;
  height: 100vh; max-height: 100vh;
  padding: 0; border: 0;
  background:
    radial-gradient(120% 90% at 8% 0%, rgba(107,160,222,.14), transparent 58%),
    radial-gradient(100% 80% at 100% 100%, rgba(107,160,222,.12), transparent 60%),
    #F4F7FC;
  overflow: hidden;
}
.bigview::backdrop { background: rgba(10,18,20,.8); }

.dash {
  display: grid; grid-template-rows: auto minmax(0, 1fr);
  height: 100%;
  font-size: .95rem;
}

/* ---- header ---- */
.dash__bar {
  display: flex; align-items: center; gap: .9rem;
  padding: clamp(.8rem, 1.5vw, 1.15rem) clamp(1rem, 3vw, 2.2rem);
  background: rgba(255,255,255,.72);
  -webkit-backdrop-filter: blur(14px);
          backdrop-filter: blur(14px);
  border-bottom: 1px solid rgba(44,95,158,.10);
}
.dash__mark {
  flex: none; width: 44px; height: 44px;
  display: grid; place-items: center; border-radius: 14px;
  background: #fff;
  box-shadow: 0 6px 16px -10px rgba(44,95,158,.5), 0 0 0 1px rgba(44,95,158,.10);
}
.dash__mark svg {
  width: 21px; height: 21px;
  fill: none; stroke: var(--blue-ink); stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}
.dash__id { display: grid; gap: .12rem; margin-right: auto; min-width: 0; }
.dash__id strong { font-size: 1.15rem; letter-spacing: -.025em; color: var(--deep-haus); }
.dash__id span { font-size: .84rem; color: var(--muted); }
.dash__id i { font-style: normal; opacity: .5; margin-inline: .25rem; }

.dash__tag {
  display: inline-flex; align-items: center; gap: .45rem;
  padding: .5rem .9rem; border-radius: var(--pill);
  background: #fff; color: var(--deep-haus);
  font-size: .82rem; font-weight: 600;
  box-shadow: 0 6px 18px -12px rgba(44,95,158,.6), 0 0 0 1px rgba(44,95,158,.10);
  white-space: nowrap;
}
.dash__tag svg { width: 13px; height: 13px; fill: none; stroke: var(--blue-ink); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

.dash__close {
  flex: none; width: 42px; height: 42px;
  display: grid; place-items: center; border-radius: 50%; cursor: pointer;
  background: #fff; color: var(--deep-haus); border: 0;
  box-shadow: 0 6px 18px -12px rgba(44,95,158,.6), 0 0 0 1px rgba(44,95,158,.10);
  transition: background-color .2s ease, color .2s ease;
}
.dash__close svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }
.dash__close:hover { background: var(--blue-ink); color: #fff; }

/* ---- body ---- */
.dash__body {
  overflow-y: auto;
  padding: clamp(1rem, 2.4vw, 1.75rem) clamp(1rem, 3vw, 2.2rem) clamp(2rem, 4vw, 3rem);
  display: grid; gap: clamp(.8rem, 1.6vw, 1.15rem);
  align-content: start;
  max-width: 1460px; width: 100%; margin-inline: auto;
}

/* every animated block starts here and is released by .is-live */
.dash__fig, .dash__bar-row, .dash__panel, .dash__foot,
.dash__cat, .dash__stages li, .dash__table tbody tr {
  opacity: 0; translate: 0 14px;
}
.dash.is-live .dash__fig,
.dash.is-live .dash__bar-row,
.dash.is-live .dash__panel,
.dash.is-live .dash__foot,
.dash.is-live .dash__cat,
.dash.is-live .dash__stages li,
.dash.is-live .dash__table tbody tr {
  animation: dash-rise .52s cubic-bezier(.22,.75,.3,1) var(--d, 0s) both;
}
@keyframes dash-rise { to { opacity: 1; translate: 0 0; } }

/* ---- the four figures ---- */
.dash__figs {
  margin: 0; display: grid; gap: clamp(.6rem, 1.2vw, .95rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr));
}
.dash__fig {
  position: relative; isolation: isolate; overflow: hidden;
  display: grid; gap: .1rem;
  padding: clamp(.85rem, 1.5vw, 1.15rem);
  border-radius: 18px; background: #fff;
  box-shadow: 0 10px 26px -20px rgba(22,42,74,.55), 0 0 0 1px rgba(44,95,158,.09);
}
.dash__fig--lead {
  background:
    linear-gradient(140deg, rgba(107,160,222,.22) 0%, rgba(107,160,222,.08) 55%, rgba(255,255,255,.9) 100%),
    #fff;
  box-shadow: 0 14px 32px -20px rgba(44,95,158,.6), 0 0 0 1px rgba(44,95,158,.20);
}
/* the swell in the corner of the lead card, as in the reference */
.dash__fig-wave {
  position: absolute; inset: auto 0 0 0; z-index: -1;
  width: 100%; height: 62%;
  fill: rgba(107,160,222,.20);
}
.dash__fig-ico {
  width: 34px; height: 34px; margin-bottom: .45rem;
  display: grid; place-items: center; border-radius: 11px;
  background: rgba(44,95,158,.09);
}
.dash__fig--lead .dash__fig-ico { background: rgba(255,255,255,.75); }
.dash__fig-ico svg {
  width: 17px; height: 17px;
  fill: none; stroke: var(--blue-ink); stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}
.dash__fig dt {
  font-family: var(--mono); font-size: .58rem; font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase; color: var(--taupe);
}
.dash__fig dd {
  margin: .25rem 0 0; font-size: clamp(1.35rem, 2vw, 1.75rem);
  font-weight: 700; letter-spacing: -.035em;
  color: var(--deep-haus); font-variant-numeric: tabular-nums;
}
.dash__fig-sub {
  display: flex; align-items: center; gap: .3rem;
  margin-top: .35rem; font-size: .78rem; color: var(--muted);
}
.dash__fig-sub b { color: var(--blue-ink); font-variant-numeric: tabular-nums; }
.dash__fig-sub svg {
  width: 13px; height: 13px; flex: none;
  fill: none; stroke: #2E8B75; stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round;
}

/* ---- the draw bar ---- */
.dash__bar-row {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center; gap: 1rem;
  padding: clamp(.75rem, 1.3vw, 1rem) clamp(.9rem, 1.5vw, 1.2rem);
  border-radius: 16px; background: #fff;
  box-shadow: 0 10px 26px -20px rgba(22,42,74,.55), 0 0 0 1px rgba(44,95,158,.09);
}
.dash__bar-label { font-size: .86rem; color: var(--muted); }
.dash__bar-label b { color: var(--deep-haus); font-variant-numeric: tabular-nums; }
.dash__bar-note {
  font-family: var(--mono); font-size: .72rem; color: var(--taupe);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* one track, two sizes. The fill is held at zero until is-live, then
   runs out to the --w the markup carries. */
.dash__track {
  height: 9px; border-radius: 999px;
  background: rgba(44,95,158,.10); overflow: hidden;
}
.dash__track--sm { height: 6px; }
.dash__track > span {
  display: block; height: 100%; width: 0; border-radius: inherit;
  background: linear-gradient(90deg, var(--blue-ink), var(--haus-blue));
}
.dash__track--sm > span { background: linear-gradient(90deg, var(--c, var(--blue-ink)), color-mix(in srgb, var(--c, var(--haus-blue)) 55%, #fff)); }
.dash.is-live .dash__track > span {
  animation: dash-fill .95s cubic-bezier(.22,.75,.3,1) var(--d, .3s) both;
}
@keyframes dash-fill { from { width: 0; } to { width: var(--w, 0%); } }

/* ---- panels ---- */
.dash__split {
  display: grid; gap: clamp(.8rem, 1.6vw, 1.15rem);
  grid-template-columns: minmax(0, 1.62fr) minmax(0, 1fr);
}
@media (max-width: 900px) { .dash__split { grid-template-columns: 1fr; } }

.dash__panel {
  padding: clamp(.9rem, 1.6vw, 1.3rem);
  border-radius: 20px; background: #fff;
  box-shadow: 0 12px 30px -22px rgba(22,42,74,.55), 0 0 0 1px rgba(44,95,158,.09);
}
.dash__h {
  display: flex; align-items: center; gap: .6rem;
  font-size: 1rem; font-weight: 700; letter-spacing: -.02em;
  color: var(--deep-haus); margin-bottom: 1rem;
}
.dash__h-ico {
  flex: none; width: 30px; height: 30px;
  display: grid; place-items: center; border-radius: 10px;
  background: rgba(44,95,158,.09);
}
.dash__h-ico--soft { width: 22px; height: 22px; border-radius: 7px; background: rgba(44,95,158,.07); }
.dash__h-ico svg {
  width: 15px; height: 15px;
  fill: none; stroke: var(--blue-ink); stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}
.dash__h-ico--soft svg { width: 12px; height: 12px; }
.dash__h--gap { margin-top: 1.5rem; font-size: .8rem; }

.dash__viewall {
  margin-left: auto;
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .35rem .75rem; border-radius: var(--pill);
  font-size: .78rem; font-weight: 600; text-decoration: none;
  color: var(--blue-ink); background: rgba(44,95,158,.07);
  transition: background-color .2s ease;
}
.dash__viewall:hover { background: rgba(44,95,158,.14); }
.dash__viewall svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* ---- the colour key, shared by the categories and the invoices ---- */
.dash__dot {
  flex: none; width: 9px; height: 9px; border-radius: 50%;
  background: var(--c, var(--blue-ink));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--c, var(--blue-ink)) 16%, transparent);
}

.dash__cats { display: grid; gap: .55rem; }
.dash__cat {
  display: grid; align-items: center; gap: .7rem;
  grid-template-columns: 9px minmax(0, 1fr) minmax(70px, 1.15fr) auto auto;
}
.dash__cat-name { font-size: .88rem; color: var(--charcoal); }
.dash__cat-pct, .dash__cat-amt {
  font-family: var(--mono); font-size: .76rem;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.dash__cat-pct { color: var(--taupe); }
.dash__cat-amt { color: var(--deep-haus); font-weight: 500; min-width: 7ch; text-align: right; }

.dash__sum, .dash__foot {
  display: flex; align-items: flex-start; gap: .5rem;
  margin-top: 1rem; padding: .6rem .8rem;
  border-radius: 12px; background: rgba(44,95,158,.05);
  font-size: .78rem; color: var(--muted);
}
.dash__foot { margin-top: 0; }
.dash__sum svg, .dash__foot svg {
  width: 14px; height: 14px; flex: none; margin-top: .1rem;
  fill: none; stroke: var(--blue-ink); stroke-width: 1.7; stroke-linecap: round;
}

@media (max-width: 620px) {
  /* the share column is the first thing to go when the row runs out */
  .dash__cat { grid-template-columns: 9px minmax(0, 1fr) auto; }
  .dash__track--sm, .dash__cat-pct { display: none; }
}

/* ---- the stage rail ----
   A line behind the dots with a second line over it that grows to the
   stage the job is actually at, so the connection between the steps is
   drawn rather than implied by their spacing. */
.dash__stages { position: relative; display: grid; gap: .1rem; }
.dash__stages::before,
.dash__stages::after {
  content: ""; position: absolute; left: 14px; z-index: 0;
  top: 18px; bottom: 18px; width: 2px; border-radius: 2px;
}
.dash__stages::before { background: rgba(44,95,158,.13); }
.dash__stages::after {
  height: 0; bottom: auto;
  background: linear-gradient(180deg, var(--blue-ink), var(--haus-blue));
}
.dash.is-live .dash__stages::after {
  animation: dash-rail 1s cubic-bezier(.22,.75,.3,1) .6s both;
}
@keyframes dash-rail { from { height: 0; } to { height: calc((100% - 36px) * var(--reach, 0) / 100%); } }

.dash__stages li {
  position: relative; z-index: 1;
  display: flex; align-items: center; gap: .75rem;
  padding: .45rem .6rem; border-radius: 10px;
  font-size: .88rem; color: var(--taupe);
}
.dash__stage-dot {
  flex: none; width: 10px; height: 10px; margin-left: 4px; border-radius: 50%;
  border: 2px solid rgba(44,95,158,.22); background: #fff;
}
.dash__stages li.done { color: var(--muted); }
.dash__stages li.done .dash__stage-dot { background: var(--haus-blue); border-color: var(--haus-blue); }
.dash__stages li.now { color: var(--deep-haus); font-weight: 600; background: rgba(44,95,158,.08); }
.dash__stages li.now .dash__stage-dot {
  background: var(--blue-ink); border-color: var(--blue-ink);
  box-shadow: 0 0 0 4px rgba(44,95,158,.16);
}
.dash__next { font-size: .85rem; line-height: 1.55; color: var(--muted); }

/* ---- invoices ---- */
.dash__scroll { overflow-x: auto; }
.dash__table { width: 100%; border-collapse: collapse; font-size: .85rem; }
.dash__table th {
  text-align: left; padding: .5rem .7rem;
  font-family: var(--mono); font-size: .56rem; font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase; color: var(--taupe);
  border-bottom: 1px solid rgba(44,95,158,.10);
  white-space: nowrap;
}
.dash__table td {
  padding: .6rem .7rem;
  border-bottom: 1px solid rgba(44,95,158,.07);
  color: var(--charcoal);
}
.dash__table tbody tr:last-child td { border-bottom: 0; }
.dash__table tbody tr { transition: background-color .2s ease; }
.dash__table tbody tr:hover { background: rgba(44,95,158,.04); }
.dash__date { font-family: var(--mono); font-size: .76rem; color: var(--taupe); white-space: nowrap; }
.dash__vendor { display: inline-flex; align-items: center; gap: .55rem; }
.dash__cat-col { color: var(--muted); }
.dash__amt {
  text-align: right; font-family: var(--mono);
  font-variant-numeric: tabular-nums; color: var(--deep-haus); font-weight: 500;
  white-space: nowrap;
}
.dash__go { width: 24px; text-align: right; }
.dash__go svg {
  width: 13px; height: 13px;
  fill: none; stroke: rgba(44,95,158,.4); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
.dash__table tbody tr:hover .dash__go svg { stroke: var(--blue-ink); }

@media (max-width: 700px) {
  .dash__bar-row { grid-template-columns: 1fr; gap: .5rem; }
  .dash__bar-note { text-align: right; }
  .dash__cat-col, .dash__go { display: none; }
}

/* No JS, or motion turned down: everything is simply already there. */
@media (prefers-reduced-motion: reduce) {
  .dash__fig, .dash__bar-row, .dash__panel, .dash__foot,
  .dash__cat, .dash__stages li, .dash__table tbody tr {
    opacity: 1; translate: none; animation: none;
  }
  .dash.is-live .dash__track > span { animation: none; width: var(--w, 0%); }
  .dash.is-live .dash__stages::after { animation: none; height: calc((100% - 36px) * var(--reach, 0) / 100%); }
}

.ledger__head {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center; gap: .85rem;
  padding-bottom: clamp(.85rem, 1.6vw, 1.15rem);
  border-bottom: 1px solid rgba(207,202,193,.5);
}
.ledger__id { display: grid; gap: .1rem; }
.ledger__proj { font-size: 1.05rem; font-weight: 700; letter-spacing: -.02em; color: var(--deep-haus); }
.ledger__sub { font-size: .84rem; color: var(--muted); }

/* the rounded tile every icon sits in */
.ledger__badge {
  display: grid; place-items: center; flex: none;
  width: 44px; height: 44px; border-radius: 13px;
  background: rgba(44,95,158,.10);
}
.ledger__badge--sm { width: 36px; height: 36px; border-radius: 11px; }
.ledger__badge svg {
  width: 21px; height: 21px;
  fill: none; stroke: var(--blue-ink); stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
}
.ledger__badge--sm svg { width: 18px; height: 18px; }

.ledger__tag {
  display: inline-flex; align-items: center; gap: .4rem;
  font-family: var(--mono); font-size: .6rem; font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase;
  color: #7A6838; background: rgba(200,181,138,.2);
  border: 1px solid rgba(200,181,138,.55);
  border-radius: 999px; padding: .3rem .7rem; white-space: nowrap;
}
.ledger__tag svg {
  width: 13px; height: 13px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}

/* the two figures, each panelled */
.ledger__cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .75rem; }
.ledger__card {
  display: grid; gap: .3rem; align-content: start;
  padding: clamp(.85rem, 1.5vw, 1.15rem);
  border: 1px solid rgba(207,202,193,.5);
  border-radius: 14px;
  background: var(--soft-white);
}
.ledger__card--lead {
  background: linear-gradient(140deg, rgba(107,160,222,.16), rgba(107,160,222,.05));
  border-color: rgba(107,160,222,.34);
}
.ledger__card .ledger__badge--sm { margin-bottom: .35rem; }
.ledger__k {
  font-family: var(--mono); font-size: .6rem; font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase; color: var(--taupe);
}
.ledger__v {
  font-size: clamp(1.35rem, 2.4vw, 1.75rem);
  font-weight: 700; letter-spacing: -.035em; color: var(--deep-haus);
  font-variant-numeric: tabular-nums;
}

/* progress, in its own band */
.ledger__progress {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  align-items: center; gap: .4rem .75rem;
  padding: clamp(.75rem, 1.4vw, 1rem);
  border: 1px solid rgba(207,202,193,.5);
  border-radius: 14px;
  background: var(--soft-white);
}
.ledger__pct { font-size: .84rem; color: var(--muted); }
.ledger__pct span { font-family: var(--mono); font-weight: 600; color: var(--deep-haus); }
.ledger__pctnum {
  font-family: var(--mono); font-size: .84rem; font-weight: 600;
  color: var(--deep-haus); font-variant-numeric: tabular-nums;
}
.ledger__bar {
  grid-column: 1 / -1;
  height: 8px; border-radius: 999px;
  background: var(--warm-stone); overflow: hidden;
}
.ledger__bar span {
  display: block; height: 100%; border-radius: inherit;
  background: linear-gradient(90deg, var(--blue-ink), var(--haus-blue));
}

/* the table, panelled */
.ledger__panel {
  border: 1px solid rgba(207,202,193,.5);
  border-radius: 14px; overflow: hidden;
  background: var(--soft-white);
}
.ledger__scroll { overflow-x: auto; }
.ledger__table { width: 100%; border-collapse: collapse; font-size: .86rem; }
.ledger__table th {
  text-align: left; padding: .7rem .8rem;
  font-family: var(--mono); font-size: .58rem; font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase; color: var(--taupe);
  background: rgba(22,35,38,.025);
  border-bottom: 1px solid rgba(207,202,193,.5);
  white-space: nowrap;
}
.ledger__table td {
  padding: .6rem .7rem;
  border-bottom: 1px solid rgba(207,202,193,.35);
  color: var(--charcoal);
  /* transparent rather than #fff: it was a no-op on this white card and
     would be a hole punched through any fill the card is ever given */
  background: transparent;
  /* the name columns wrap; only the date and the figure hold one line.
     With nowrap on every cell the table's intrinsic width ran past the
     panel and the amounts were being cut off. */
  white-space: normal;
}
.ledger__table td:first-child, .ledger__table .ledger__amt { white-space: nowrap; }
.ledger__table .ledger__cell { align-items: flex-start; }
.ledger__table tr:last-child td { border-bottom: 0; }
.ledger__cell { display: inline-flex; align-items: center; gap: .55rem; }

.ledger__date {
  display: inline-block;
  font-family: var(--mono); font-size: .72rem;
  color: var(--muted); background: rgba(22,35,38,.05);
  border-radius: 7px; padding: .2rem .45rem;
}

/* small tinted tiles beside each name, in brand hues rather than a rainbow */
.ledger__ico {
  display: grid; place-items: center; flex: none;
  width: 28px; height: 28px; border-radius: 8px;
}
.ledger__ico svg {
  width: 15px; height: 15px;
  fill: none; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}
.ledger__ico--a     { background: rgba(44,95,158,.12); }
.ledger__ico--a svg { stroke: var(--blue-ink); }
.ledger__ico--b     { background: rgba(200,181,138,.26); }
.ledger__ico--b svg { stroke: #7A6838; }
.ledger__ico--c     { background: rgba(107,160,222,.18); }
.ledger__ico--c svg { stroke: var(--blue-ink); }
.ledger__ico--d     { background: rgba(166,159,148,.22); }
.ledger__ico--d svg { stroke: #6B6459; }
.ledger__ico--soft     { background: rgba(22,35,38,.055); }
.ledger__ico--soft svg { stroke: var(--muted); }

.ledger__amt {
  text-align: right;
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  color: var(--deep-haus); font-weight: 500;
}

/* footnote, on its own tinted strip */
.ledger__foot {
  display: flex; align-items: center; gap: .55rem;
  font-size: .8rem; color: var(--muted);
  padding: .65rem .8rem;
  background: rgba(44,95,158,.05);
  border-radius: 12px;
}


/* ---- the card at column width: same design, smaller gauge ---- */
.ledger { padding: clamp(.8rem, 1.3vw, 1rem); gap: .55rem; }
.ledger__head { gap: .6rem; padding-bottom: .6rem; }
.ledger__badge { width: 34px; height: 34px; border-radius: 10px; }
.ledger__badge svg { width: 18px; height: 18px; }
.ledger__badge--sm { width: 30px; height: 30px; border-radius: 9px; }
.ledger__badge--sm svg { width: 15px; height: 15px; }
.ledger__proj { font-size: .98rem; }
.ledger__sub { font-size: .78rem; }
.ledger__cards { gap: .6rem; }
.ledger__card { padding: .65rem .8rem; gap: .2rem; }
.ledger__v { font-size: clamp(1.2rem, 1.9vw, 1.5rem); }
.ledger__progress { padding: .55rem .75rem; gap: .3rem .75rem; }
.ledger__table { font-size: .76rem; }
.ledger__table th { padding: .42rem .55rem; font-size: .54rem; }
.ledger__table td { padding: .34rem .55rem; }
.ledger__cell { gap: .45rem; }
.ledger__ico { width: 21px; height: 21px; border-radius: 6px; }
.ledger__ico svg { width: 13px; height: 13px; }
.ledger__date { font-size: .66rem; padding: .16rem .38rem; }
.ledger__foot { font-size: .72rem; padding: .45rem .7rem; }

@media (max-width: 560px) {
  .ledger__cards { grid-template-columns: 1fr; }
}

/* ---------- 08 featured work (expanding gallery) ----------
   One project open, the others held as slivers of their own photograph.
   Width is animated through `flex-basis` rather than `width`, which is
   what keeps the row from reflowing its neighbours every frame.

   Three panels, so the arithmetic is: one at 66% plus two at 17% makes
   100%, and each panel gives back its share of the two gaps (2/3 of one
   gap each). Add a fourth and those three numbers change together.
   ------------------------------------------------------------------- */
.gal {
  --g: clamp(.35rem, .7vw, .6rem);
  display: flex; gap: var(--g);
  align-items: stretch;
  min-height: clamp(340px, 38vw, 540px);
}

.gal__item {
  position: relative; isolation: isolate; overflow: hidden;
  flex: 0 0 calc(17% - (var(--g) * 0.667));
  border-radius: 14px;
  background: rgba(255,255,255,.06);
  transition: flex-basis .6s cubic-bezier(.4,0,.2,1);
}
.gal__item.is-open { flex-basis: calc(66% - (var(--g) * 0.667)); }

/* The button is the control and it covers the whole panel; the copy
   sits above it but lets clicks through, so only the real link inside
   catches them. Without that the panel would swallow every click that
   was not exactly on "Learn More". */
.gal__hit {
  position: absolute; inset: 0; z-index: 2;
  width: 100%; height: 100%; padding: 0;
  background: none; border: 0; cursor: pointer;
}
.gal__hit:focus-visible { outline: 2px solid var(--haus-blue); outline-offset: -4px; }

.gal__shot {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
  /* collapsed, the panel is a narrow window onto the middle of the
     picture rather than its left edge */
  object-position: 50% 50%;
  /* These are phone and drone shots straight off the job, and they sit
     on a near-black section that flattens them further. A light lift,
     not a filter look — past about 1.12 saturation the timber and the
     sky both start to go synthetic. */
  filter: saturate(1.1) contrast(1.06);
  transition: transform .6s cubic-bezier(.4,0,.2,1);
}
.gal__item.is-open .gal__shot { transform: scale(1.02); }

/* Legible copy without flattening the picture. The scrim is clear for
   the top 44% and only gathers under the words — the old one opened at
   .42 across the very top, which greyed the sky in every panel, and ran
   at .55 on the closed ones, which carry no copy to protect at all.
   Closed panels now get none of it and show the photograph straight. */
.gal__item::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg,
    rgba(10,18,20,0)   0%,
    rgba(10,18,20,0)  34%,
    rgba(10,18,20,.66) 60%,
    rgba(10,18,20,.88) 80%,
    rgba(10,18,20,.95) 100%);
  opacity: 0; transition: opacity .5s ease;
}
.gal__item.is-open::after { opacity: 1; }

.gal__badge {
  position: absolute; top: 14px; left: 14px; z-index: 3;
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border-radius: 10px;
  background: rgba(16,25,28,.55);
  -webkit-backdrop-filter: blur(10px);
          backdrop-filter: blur(10px);
  border: 1px solid rgba(255,255,255,.2);
  pointer-events: none;
}
.gal__badge svg {
  width: 17px; height: 17px;
  fill: none; stroke: var(--champagne); stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
}

/* Fixed width, set from the open panel, so the copy cannot re-wrap
   while the panel is still growing — the same reason the process cards
   size theirs in JS. */
.gal__panel {
  position: absolute; z-index: 3;
  left: clamp(1rem, 2vw, 1.75rem);
  bottom: clamp(1rem, 2vw, 1.6rem);
  width: var(--gal-panel-w, 380px);
  display: grid; gap: .3rem;
  opacity: 0; visibility: hidden;
  transition: opacity .34s ease .14s, visibility .34s ease .14s;
  /* clicks fall through to the button behind, except on the link */
  pointer-events: none;
}
.gal__item.is-open .gal__panel { opacity: 1; visibility: visible; }
.gal__panel a { pointer-events: auto; }

.gal__kind {
  font-size: .72rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase;
  color: rgba(255,255,255,.62);
}
.gal__name {
  margin-top: .25rem;
  font-size: clamp(1.4rem, 1.1rem + 1.1vw, 2.1rem);
  letter-spacing: -.03em; color: #fff;
}
.gal__name a { text-decoration: none; transition: color .22s ease; }
.gal__name a:hover { color: var(--haus-blue); }
.gal__meta { margin-top: .5rem; font-size: .92rem; color: rgba(255,255,255,.78); }
.gal__meta--soft { margin-top: .1rem; color: rgba(255,255,255,.5); }
.gal__cta { margin-top: 1rem; }

@media (max-width: 820px) {
  /* Stacked, every panel is open — a sliver of a photograph is a
     desktop affordance, and on a phone it would just be a stripe. */
  .gal { flex-direction: column; min-height: 0; gap: clamp(.9rem, 3vw, 1.25rem); }
  .gal__item,
  .gal__item.is-open { flex: 0 0 auto; min-height: clamp(280px, 62vw, 400px); }
  .gal__item::after { opacity: 1; }
  .gal__panel {
    position: static; width: auto;
    opacity: 1; visibility: visible;
    padding: clamp(1rem, 4vw, 1.5rem);
    align-self: end;
  }
  .gal__item { display: grid; }
  .gal__hit { display: none; }        /* nothing to expand, so no control */
  .gal__panel { pointer-events: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .gal__item, .gal__shot { transition: none; }
}

/* ---------- 08 process (expanding cards) ----------
   Collapsed cards show only their number. Clicking one grows it and
   reveals the detail. Width is animated through `flex`, which stays on
   the compositor better than animating width directly. */
.proc {
  --g:    clamp(.5rem, 1vw, .85rem);
  --pad:  clamp(1rem, 1.8vw, 1.75rem);
  --numw: clamp(2.6rem, 5vw, 4.6rem);
  --lead: clamp(.75rem, 2vw, 2rem);
  display: flex; gap: var(--g);
  align-items: stretch;
  min-height: clamp(360px, 42vw, 540px);
}

.proc__item {
  /* Both states are real lengths so the browser can interpolate between
     them. Animating to `flex-basis: auto` is what made this snap —
     `auto` has no numeric value to ease toward.
     Five cards: 4 x 12% + 52% = 100%, minus each card's share of the
     four gaps (4/5 of one gap each). */
  flex: 0 0 calc(12% - (var(--g) * 0.8));
  display: flex; align-items: stretch; gap: 0;
  position: relative;
  padding: var(--pad);
  /* A closed card is barely a gradient at all — just enough fall-off to
     stop five flat rectangles reading as a table. */
  background: linear-gradient(180deg, var(--soft-white) 0%, #F1EEE7 100%);
  border: 1px solid var(--border-gray);
  border-radius: 16px;
  /* The ramp at about half size. The track's gap is only 8-14px, so most
     of a card's sideways cast lands where its neighbour is — and since
     the cards are positioned siblings, the neighbour paints over it and
     only the part inside the gap survives. The downward cast is the half
     that actually reads, which is why the vertical offsets carry more of
     the ramp than the horizontal ones. */
  box-shadow:
    1px 2px 0 0 rgba(22,35,38,.14),
    2px 3px 2px -1px rgba(22,35,38,.17),
    5px 8px 7px -3px rgba(22,35,38,.20),
    11px 16px 18px -7px rgba(22,35,38,.26),
    19px 29px 40px -12px rgba(22,35,38,.34);
  overflow: hidden;
  cursor: pointer;
  text-align: left;
  /* The whole move is 20% slower than it was: .5s -> .6s on the width,
     and everything travelling with it scaled by the same 1.2 so the copy
     still lands as the card finishes rather than ahead of it. */
  transition: flex-basis .6s cubic-bezier(.4,0,.2,1),
              background-color .48s ease, border-color .48s ease, color .48s ease,
              box-shadow .48s ease;
}
/* No tilt on hover — only the edge and a little lift. */
.proc__item:hover:not(.is-open) {
  border-color: var(--taupe);
  /* the same five stops pushed out a step, so the card lifts instead of
     swapping its cast shadow for a flat one */
  box-shadow:
    1px 2px 0 0 rgba(22,35,38,.16),
    3px 4px 3px -1px rgba(22,35,38,.19),
    7px 11px 10px -3px rgba(22,35,38,.22),
    15px 22px 25px -7px rgba(22,35,38,.28),
    26px 40px 55px -12px rgba(22,35,38,.36);
}

/* ---- the open card: gradient fill + a glow that travels the border ----
   The glow is one oversized square carrying a conic gradient, spun with
   `transform` so it stays on the compositor, clipped to the card's
   rounded box by the overflow above. A second layer sits 2px inside it
   and carries the actual fill, so all that shows of the spinner is a 2px
   rim running round the edge. No masks, no per-frame gradient repaint. */
.proc__item.is-open {
  flex-basis: calc(52% - (var(--g) * 0.8));
  /* The card keeps the light gradient as its base and lets the fill layer
     fade in over it — overriding `background` here would blank the
     closed card's gradient the instant the class lands, and the page
     would flash through the edge before the layer arrived.
     The 1px border stays the deeper company blue, so the glow rim reads
     as sitting inside a frame rather than floating on the ivory. */
  border-color: var(--blue-ink);
  color: #fff;
  cursor: default;
  /* the same ramp, three quarters size and in the card's own blue, so
     the open card casts a shadow rather than wearing a flat halo */
  box-shadow:
    2px 3px 0 0 rgba(44,95,158,.16),
    4px 6px 4px -1px rgba(44,95,158,.19),
    9px 13px 12px -4px rgba(44,95,158,.22),
    18px 25px 28px -11px rgba(44,95,158,.28),
    30px 43px 60px -20px rgba(44,95,158,.34);
}

/* Both layers exist on every card and are simply faded in, so opening
   one cross-fades into the blue instead of snapping — a gradient cannot
   be interpolated the way the old flat background-color was. */
.proc__item::before, .proc__item::after {
  content: "";
  position: absolute;
  pointer-events: none;
  opacity: 0; visibility: hidden;
  transition: opacity .4s ease, visibility .4s ease;
}
.proc__item.is-open::before,
.proc__item.is-open::after { opacity: 1; visibility: visible; }

.proc__item::before {
  top: -50%; left: -50%;
  width: 200%; height: 200%;
  /* A light-to-gold comet with a near-white core. Pure Haus Blue was
     invisible here — the rim sits on a blue card, so the travelling part
     has to be brighter than the fill, not the same hue as it. */
  background: conic-gradient(from 0deg,
      transparent 0deg 150deg,
      rgba(107,160,222,.35) 196deg,
      rgba(215,233,255,.95) 246deg,
      #FFFFFF 268deg,
      var(--champagne) 300deg,
      rgba(200,181,138,.35) 330deg,
      transparent 352deg);
}
/* nothing spins on the four closed cards */
.proc__item.is-open::before {
  animation: proc-glow 7s linear infinite;
  will-change: transform;
}
/* parked by the observer in site.js while the section is off screen */
.proc.is-idle .proc__item.is-open::before { animation-play-state: paused; }

.proc__item::after {
  inset: 2.5px;
  border-radius: 14px;
  /* keeps a hairline edge on the card even where the comet is not */
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.06);
  /* The same ramp the stat figures run: blue-ink into haus-blue, at the
     same 95deg. The radial that used to sit over it is gone — it was
     there to lighten the bottom right corner, which this ramp now does
     on its own, and both together washed the right-hand side out. */
  background: linear-gradient(137deg,
    var(--blue-ink)  0%,
    #4176BE         66%,
    var(--haus-blue) 100%);
}

/* both pseudo-layers are positioned, so the content has to be lifted
   above them or the fill paints straight over the words */
.proc__num, .proc__panel, .proc__label { z-index: 1; }
.proc__num { position: relative; }

@keyframes proc-glow { to { transform: rotate(1turn); } }

@media (prefers-reduced-motion: reduce) {
  .proc__item.is-open::before { animation: none; transform: rotate(140deg); }
}

.proc__num {
  flex: none;
  font-size: clamp(1.9rem, 3.4vw, 3.2rem);
  font-weight: 700; letter-spacing: -.04em; line-height: 1;
  color: var(--champagne);
  /* keeps the number in the same spot whether open or collapsed */
  align-self: flex-start;
  width: var(--numw);
}

/* ---- the closed card's title ----
   A collapsed card is only ~12% wide, so the title runs vertically down
   the number column rather than across it. Absolutely positioned for the
   same reason .proc__panel is: a static element here would re-wrap on
   every frame while the card resizes. It leaves the moment the card
   opens so it never doubles up with .proc__title, and it uses visibility
   as well as opacity so a screen reader reads one or the other. */
.proc__label {
  position: absolute;
  /* centred under the numeral: in vertical-rl the line box sits at the
     block-start edge, so a full-width box pushed the text off to one side */
  left: calc(var(--pad) + var(--numw) / 2);
  translate: -50% 0;
  top: calc(var(--pad) + clamp(1.9rem, 3.4vw, 3.2rem) + .9rem);
  bottom: var(--pad);
  writing-mode: vertical-rl;
  /* the numeral's weight and tracking, so the two read as one unit */
  font-size: clamp(.88rem, 1.12vw, 1.05rem);
  font-weight: 700;
  letter-spacing: -.01em;
  line-height: 1;
  color: var(--deep-haus);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
  opacity: 1; visibility: visible;
  transition: opacity .36s ease .19s, visibility .36s ease .19s, color .48s ease;
}
.proc__item.is-open .proc__label { opacity: 0; visibility: hidden; transition-delay: 0s; }

/* the detail only exists once the card is open */
/* Taken out of the flex flow and given a fixed width, so the copy never
   re-wraps while the card is resizing — that reflow, frame by frame, is
   what made the open card judder. The card clips it instead.
   --panel-w is measured in JS; the fallback covers the no-JS case. */
.proc__panel {
  position: absolute;
  top: var(--pad); bottom: var(--pad);
  left: calc(var(--pad) + var(--numw) + var(--lead));
  width: var(--panel-w, 460px);
  display: flex; flex-direction: column; gap: .6rem;
  opacity: 0; visibility: hidden;
  transition: opacity .34s ease .14s, visibility .34s ease .14s;
}
.proc__item.is-open .proc__panel { opacity: 1; visibility: visible; }

.proc__title {
  font-size: clamp(1.15rem, 1.4vw, 1.5rem);
  font-weight: 700; letter-spacing: -.02em;
}
/* the marker beside the closing line, and the two small concessions the
   extra line needs from the block above and the picture below it */
.proc__out::before {
  content: ""; flex: none;
  width: 2px; margin-top: .35em; height: 1.05em;
  border-radius: 2px;
  background: var(--champagne);
}
.proc__body { max-width: 46ch; }
.proc__shot { max-height: 36%; }

.proc__out {
  display: flex; gap: .5rem;
  font-size: .9rem; line-height: 1.45; font-weight: 600;
  color: #fff;
}
.proc__body {
  font-size: .96rem; line-height: 1.5;
  color: rgba(255,255,255,.78); max-width: 62ch;
}
.proc__shot {
  margin-top: auto; display: block;
  border-radius: 12px; overflow: hidden;
  max-height: 58%;
}
.proc__shot img { width: 100%; height: 100%; object-fit: cover; display: block; }

@media (max-width: 820px) {
  /* stacks vertically — the same interaction, rotated */
  .proc { flex-direction: column; min-height: 0; }
  .proc__item { flex: 0 0 auto; transition: background-color .48s ease, border-color .48s ease, color .48s ease; }
  .proc__item.is-open { flex-basis: auto; }
  /* relative, not static — back in normal flow, but still able to take a
     z-index and sit above the card's fill layer. The desktop insets have
     to be cleared by hand: static ignored them, relative would not. */
  .proc__panel {
    position: relative;
    inset: auto;
    width: auto;
    padding-left: clamp(.75rem, 3vw, 1.25rem);
  }
  .proc__shot { max-height: 220px; margin-top: .75rem; }
  /* wide, short rows here, so the title reads straight across instead */
  .proc__label {
    position: static;
    translate: none;
    writing-mode: horizontal-tb;
    align-self: center;
    margin-left: clamp(.75rem, 3vw, 1.25rem);
    font-size: 1.02rem;
    line-height: 1.2;
    white-space: normal;
    transition: opacity .26s ease, visibility .26s ease;
  }
  /* display, not visibility: a hidden static label would leave a gap */
  .proc__item.is-open .proc__label { display: none; }
  /* Same reason, the other way round. In the column layout the panel is
     back in normal flow, so a closed card's panel — hidden, but still a
     flex item — was taking 230px of a 335px row and leaving the title
     17px to live in, one ellipsed word per line. */
  .proc__item:not(.is-open) .proc__panel { display: none; }
  .proc__label { flex: 1 1 auto; overflow: visible; text-overflow: clip; }
}

/* ---------- phone tuning ----------
   Measured at a 375px viewport: the ledger ran 330px inside a 305px
   panel and chopped the amounts mid-glyph, and several controls were
   under the 44px that a finger actually needs.
   ---------------------------------------------------------------- */
@media (max-width: 560px) {
  /* The table is the one thing on the page allowed to scroll sideways,
     but it only has 25px to lose — tighter cells buy that back so the
     amounts land on screen instead of behind the card edge. */
  .ledger__table { font-size: .72rem; }
  .ledger__table th { padding: .5rem .3rem; }
  .ledger__table td { padding: .3rem .3rem; }
  .ledger__cell { gap: .3rem; }
  .ledger__ico { width: 18px; height: 18px; border-radius: 5px; }

}

/* Touch targets key off the input device as well as the width — a 768px
   tablet is every bit as much a thumb as a 375px phone. The width arm is
   kept alongside it because a phone that misreports its pointer still
   needs the bigger targets, and because it is the arm a desktop browser
   can actually be made to exercise. */
@media (pointer: coarse), (max-width: 620px) {
  /* 34px is a comfortable mouse target and a poor thumb one */
  .hhmap__ctl button { width: 42px; height: 42px; }
  .hhmap__ctl { gap: .4rem; }
  .nav-toggle { padding-block: .72rem; }
}

/* ---------- 09 quality (glass tiles over the coast) ----------
   The section runs on its own photograph. The four checks sit over it as
   glass tiles, alternating light and dark, each with its number ghosted
   into the top corner and its copy on the floor of the tile. */
.section--photo {
  position: relative; isolation: isolate;
  background-image: url("../img/section-bg-2.webp");
  background-size: cover;
  background-position: center;
  /* not `fixed` — that repaints the whole section on every scroll frame */
  background-attachment: scroll;
  color: #fff;
}
.section--photo::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(180deg, rgba(10,16,20,.70) 0%, rgba(10,16,20,.80) 100%);
}
.section--photo > .shell { position: relative; }

/* ---- photo section, copy on one side ----
   The flat wash above is right when tiles cover the picture. Where the
   copy sits in a column and the photograph has to stay legible beside
   it, the weight goes across instead, and the angle keeps the scrim edge
   off the vertical. */
.section--photo--edge::before {
  background:
    /* Deep Haus rather than neutral black: a grey wash over a gold sunset
       turns it to ash, where the brand ink keeps the warmth underneath. */
    linear-gradient(100deg,
      rgba(22,35,38,.93)  0%,
      rgba(22,35,38,.88) 26%,
      rgba(22,35,38,.62) 46%,
      rgba(22,35,38,.26) 64%,
      rgba(22,35,38,.04) 82%),
    /* a light overall seat, so the roofline and the grass still settle */
    linear-gradient(180deg, rgba(22,35,38,.22) 0%, rgba(22,35,38,.10) 45%, rgba(22,35,38,.30) 100%);
}
@media (max-width: 860px) {
  /* the copy spans the full width here, so weight the whole plate */
  .section--photo--edge::before {
    background: linear-gradient(180deg, rgba(10,16,20,.72) 0%, rgba(10,16,20,.82) 100%);
  }
}
.positioning-photo {
  background-image: url("../img/section-bg-2.webp");
  /* 240 Nautilus from the drone (DJI_0084). On wide screens the photo is
     pinned to the right at full height and the copy sits on Deep Haus
     to its left, so the house is never under the paragraph. */
  background-color: #162326;
  background-image:
    linear-gradient(90deg, #162326 0%, #162326 30%, rgba(22,35,38,.55) 48%, rgba(22,35,38,0) 62%),
    url("../img/section-bg-2.webp");
  background-size: 100% 100%, auto 100%;
  background-position: 0 0, right center;
  background-repeat: no-repeat;
}
.positioning__copy { max-width: 680px; }
/* legibility from the type itself, not from dimming the picture further */
.positioning__copy .eyebrow,
.positioning__copy h2,
.positioning__copy .prose,
.positioning__copy .link-arrow {
  text-shadow: 0 1px 26px rgba(10,18,20,.55), 0 1px 3px rgba(10,18,20,.35);
}
.positioning__copy .prose { color: rgba(255,255,255,.92); }
.positioning__copy .prose { max-width: 56ch; font-size: clamp(1.0625rem, .98rem + .35vw, 1.1875rem); line-height: 1.62; }
.positioning__copy .link-arrow { color: #fff; font-size: 1rem; }
.section--photo .eyebrow { color: var(--champagne); }
.section--photo .muted { color: rgba(255,255,255,.76); }

.qrow {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(.9rem, 1.6vw, 1.35rem);
}

.qtile {
  position: relative;
  display: flex; flex-direction: column;
  min-height: clamp(300px, 25vw, 420px);
  padding: clamp(1.2rem, 1.8vw, 1.7rem);
  /* The cut corner, as in the reference. Percentages, so the chamfer is
     14% of the card's own width across and 14% of its height down — on a
     tall card that reads as one straight diagonal rather than the slight
     dog-leg a matched pixel value would give. */
  clip-path: polygon(14% 0, 100% 0, 100% 100%, 0 100%, 0 14%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
          backdrop-filter: blur(16px) saturate(140%);
}
/* A tilt on approach, not a flip — the tiles sit on a photograph and
   anything more than a couple of degrees reads as the picture moving
   rather than the tile. Only transform and box-shadow animate, so this
   stays on the compositor over a backdrop-filtered element. */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .qtile {
    transition: transform .45s cubic-bezier(.2,.7,.3,1), box-shadow .45s ease;
    will-change: transform;
  }
  .qtile:hover {
    transform: perspective(900px) rotateX(2.2deg) rotateY(-3.4deg) translateY(-7px);
    box-shadow:
      2px 3px 0 0 rgba(10,18,20,.16),
      5px 7px 6px -2px rgba(10,18,20,.20),
      12px 17px 18px -7px rgba(10,18,20,.26),
      26px 36px 48px -18px rgba(10,18,20,.36);
  }
  /* the row alternates, so the tilt alternates with it */
  .qtile:nth-child(even):hover {
    transform: perspective(900px) rotateX(2.2deg) rotateY(3.4deg) translateY(-7px);
  }
}

.qtile--light { background: rgba(250,249,246,.90); color: var(--deep-haus); }
.qtile--dark  { background: rgba(16,27,30,.84);   color: #fff; }
/* no blur available means no glass — go near-solid so the copy still reads */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .qtile--light { background: rgba(250,249,246,.93); }
  .qtile--dark  { background: rgba(12,18,22,.88); }
}

.qtile__num {
  position: absolute; top: .3rem; right: clamp(.9rem, 1.6vw, 1.4rem);
  font-size: clamp(3rem, 5vw, 5.2rem);
  font-weight: 700; letter-spacing: -.04em; line-height: 1;
  pointer-events: none;
}
.qtile--light .qtile__num { color: rgba(22,35,38,.22); }
.qtile--dark  .qtile__num { color: rgba(255,255,255,.24); }

/* copy sits on the floor of the tile */
.qtile__foot { margin-top: auto; }
.qtile__title { font-size: clamp(1.05rem, .95rem + .4vw, 1.3rem); letter-spacing: -.02em; }
.qtile__text { margin-top: .6rem; font-size: clamp(.95rem, .9rem + .15vw, 1.02rem); line-height: 1.58; }
.qtile--light .qtile__text { color: rgba(22,35,38,.86); }
.qtile--dark  .qtile__text { color: rgba(255,255,255,.88); }

@media (max-width: 1000px) { .qrow { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px)  { .qrow { grid-template-columns: 1fr; } .qtile { min-height: 0; } }
/* ---------- media blocks ---------- */
/* The section photographs are cards in their own right: a white mat, a
   hairline edge drawn as a zero-blur spread so it follows the radius,
   then the five-step ramp. */
.media, .svc__media {
  border: 2px solid #fff;
  box-shadow:
    0 0 0 1px rgba(207,202,193,.9),
    2px 3px 0 0 rgba(22,35,38,.14),
    4px 6px 4px -1px rgba(22,35,38,.17),
    10px 14px 13px -5px rgba(22,35,38,.20),
    22px 30px 34px -14px rgba(22,35,38,.26),
    38px 54px 74px -24px rgba(22,35,38,.32);
}
.media { border-radius: var(--radius); overflow: hidden; }
.media img { width: 100%; height: 100%; object-fit: cover; }

/* ---------- 12 where the clients are ----------
   The dotted lower 48 behind this section, with a line from each city
   clients call from back to Port Aransas. It sits under .shell as a
   plain background layer, so nothing in the copy column has to know it
   is there.
   ------------------------------------------------------------------- */
/* The map is the section's subject now that the phone has gone, so it
   takes the larger half rather than splitting the row down the middle. */
@media (min-width: 861px) {
  .section--reach .split { grid-template-columns: minmax(0, .88fr) minmax(0, 1.12fr); }
}
.reach {
  margin: 0; display: grid; justify-items: center;
  /* dropped to sit on the copy's baseline rather than floating level
     with the middle of it, then lifted clear of it by 15px */
  align-self: end;
  margin-bottom: 15px;
}
.reach__svg {
  display: block; width: 100%; height: auto;
  /* the land is an irregular dot field, so it needs no mask to stop it
     ending on a rectangle — it has no rectangle to end on */
  pointer-events: none;
}
.reach__land { fill: url(#reach-dots); }
/* a warm grey: the dot screen should read as ground, not as a second
   subject competing with the lines. #C6BFB2 taken down 25%. */
.reach pattern rect { fill: #948F86; }

.reach__arc {
  fill: none; stroke: url(#reach-line);
  stroke-width: 1.5; stroke-linecap: round;
  opacity: .85;
}
.reach__node { fill: var(--haus-blue); }
.reach__hubdot { fill: var(--blue-ink); }
.reach__hubname {
  fill: var(--blue-ink);
  font-size: 12.5px; font-weight: 700; letter-spacing: .12em;
}

/* Drawn state is the resting state: with no JS the whole map is simply
   finished. .is-live is only added once the section has been reached,
   and that is what winds the lines back to nothing so they can run. */
.reach.is-live .reach__arc { stroke-dasharray: 1; stroke-dashoffset: 1; }
.reach.is-live .reach__node,
.reach.is-live .reach__halo { opacity: 0; }

/* One line at a time. Each takes 1.7s to travel and the next leaves at
   the halfway mark, so the delays in the markup step by 0.85s — about
   twelve seconds for all thirteen. Linear, not eased: an eased line
   arrives at a crawl, and with the next one already in flight the
   sequence reads as a queue rather than a relay. */
.reach.is-live.is-in .reach__arc {
  animation: reach-draw 1.7s linear var(--d, 0s) forwards;
}
.reach.is-live.is-in .reach__node,
.reach.is-live.is-in .reach__halo {
  animation: reach-pop .45s ease var(--d, 0s) forwards;
}
/* the hub lights when the first line lands on it */
.reach.is-live.is-in .reach__hub .reach__halo { --d: 1.7s; }
@keyframes reach-draw { to { stroke-dashoffset: 0; } }
@keyframes reach-pop  { to { opacity: 1; } }

@media (max-width: 860px) {
  /* stacked, the map gets the full column width, so the drawn detail
     scales up and the hairlines need to come back down */
  .reach__hubname { font-size: 13px; }
  .reach__arc { stroke-width: 1.2; }
}
@media (max-width: 560px) {
  /* Twelve seconds is a fair run for a half-metre of screen. On a phone
     the map is about 180px tall and most people have scrolled past it
     well before the last line leaves, so the whole sequence is halved —
     the delays are scaled rather than rewritten, so each line still
     departs exactly as the one ahead passes its halfway mark. */
  .reach.is-live.is-in .reach__arc {
    animation-duration: .9s;
    animation-delay: calc(var(--d, 0s) * .5);
  }
  .reach.is-live.is-in .reach__node,
  .reach.is-live.is-in .reach__halo { animation-delay: calc(var(--d, 0s) * .5); }
}
@media (prefers-reduced-motion: reduce) {
  .reach.is-live .reach__arc,
  .reach.is-live .reach__node,
  .reach.is-live .reach__halo { animation: none; stroke-dashoffset: 0; opacity: 1; }
  .reach.is-live .reach__arc { stroke-dasharray: none; }
}


/* ---------- 12 founder ---------- */
/* An arch, because that is what the trade draws. The top corners take a
   horizontal radius of half the width against a vertical radius of 40% of
   the height, which on a 4:5 box is a true semicircle rather than a
   guessed curve. The apex sits at full height, so the arch only ever cuts
   sky from the top corners, never the top of his head. */
/* Carl's portrait is a card like the other photographs on the page —
   white mat, hairline edge, the same five-step ramp. It used to be an
   arch with a champagne outline offset behind it; that was a nice device
   but it made this the only picture on the site that was not a card. */
.founder__frame {
  position: relative; isolation: isolate;
  max-width: 400px; margin: 0;
}
.founder__portrait {
  position: relative; z-index: 1;
  aspect-ratio: 4 / 5; overflow: hidden;
  border-radius: var(--radius);
  border: 2px solid #fff;
  background: var(--warm-stone);
  box-shadow:
    0 0 0 1px rgba(207,202,193,.9),
    2px 3px 0 0 rgba(22,35,38,.14),
    4px 6px 4px -1px rgba(22,35,38,.17),
    10px 14px 13px -5px rgba(22,35,38,.20),
    22px 30px 34px -14px rgba(22,35,38,.26),
    38px 54px 74px -24px rgba(22,35,38,.32);
}
.founder__portrait img { width: 100%; height: 100%; object-fit: cover; }

/* ---- the name plate ----
   Glass, hung across the bottom corner so it overlaps the picture rather
   than sitting under it. Pulled in from the left edge and down past the
   bottom one, which is what makes it read as laid on top. */
.founder__plate {
  position: absolute; z-index: 2;
  /* hung off the left edge and sat inside the bottom one, so it reads as
     laid across the picture rather than hanging under it */
  left: clamp(-1.5rem, -2.4vw, -1rem);
  right: auto; width: max-content;
  max-width: calc(100% + 1.5rem);
  bottom: clamp(1.5rem, 4vw, 2.5rem);
  display: grid; gap: .3rem;
  padding: .7rem .95rem .75rem;
  border-radius: 14px;
  background: rgba(255,255,255,.82);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
          backdrop-filter: blur(14px) saturate(150%);
  border: 1px solid rgba(255,255,255,.75);
  /* The same hard-to-soft ramp as the cards, at about a third of their
     size and well under half their weight. It has a horizontal offset
     now — the old one fell straight down, which on a plate this small
     read as a glow rather than as something sitting on the picture. */
  box-shadow:
    0 1px 0 rgba(255,255,255,.9) inset,
    0 0 0 1px rgba(22,35,38,.055),
    1px 1px 0 0 rgba(22,35,38,.07),
    2px 3px 2px -1px rgba(22,35,38,.09),
    4px 6px 5px -2px rgba(22,35,38,.11),
    8px 11px 11px -6px rgba(22,35,38,.13),
    13px 18px 24px -10px rgba(22,35,38,.17);
}
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .founder__plate { background: rgba(250,249,246,.96); }
}
.founder__who { display: flex; align-items: baseline; flex-wrap: wrap; gap: .5rem; }
.founder__who strong {
  font-size: 1.02rem; font-weight: 700; letter-spacing: -.02em;
  color: var(--deep-haus);
}
/* the designation, as a pill rather than a line of small print */
.founder__who > span {
  padding: .16rem .55rem; border-radius: var(--pill);
  background: rgba(44,95,158,.10); color: var(--blue-ink);
  font-size: .68rem; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  white-space: nowrap;
}
.founder__since {
  display: flex; align-items: center; gap: .35rem;
  font-size: .76rem; color: var(--muted);
}
.founder__since svg {
  width: 12px; height: 12px; flex: none;
  fill: none; stroke: var(--haus-blue); stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}

@media (max-width: 620px) {
  .founder__frame { margin-inline: auto; }
  .founder__plate {
    left: .75rem; right: .75rem; width: auto; max-width: none;
  }
}

/* ---------- 13 testimonials ----------
   One featured story: project shots, portrait, then the words. */
.testimonials {
  padding-top: calc(clamp(4rem, 9vw, 8rem) - 80px);
  /* the cards travel out past the shell, so the clip lives here rather
     than around the card — a box tight to the card cuts its own shade */
  overflow: hidden;
  /* `clip`, not just `hidden`: a hidden box can still be scrolled by the
     browser, and focusing or clicking the wide hit areas beside the card
     would slide the whole section sideways to bring them into view. */
  overflow: clip;
}

/* ---- card + controls ---- */
/* The card runs the full width of the section, flush with the heading
   above it, and the arrows sit in the same grid cell — straddling its
   left and right edges rather than taking tracks of their own. Half an
   arrow (21px) is all that hangs outside, which the section's gutter can
   always absorb; giving them their own tracks would have pushed the card
   inwards, and hanging them fully outside overflows the page once the
   window drops near 1000px. */
.tmnl-wrap {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  row-gap: 1.1rem;
}

/* A bare stage. Everything that made this look like a card has moved on
   to the slides — the point of the hand-off is that the cards travel,
   and a card cannot travel if it is the box the travelling happens
   inside. Nothing is clipped here either: a clip tight to the card would
   slice off that card's own shadow. The section does the clipping. */
.tmnl-card {
  grid-area: 1 / 1;
  display: grid; grid-template-columns: minmax(0, 1fr);
  /* the peeks share this cell and carry a z-index, so without one of its
     own the stage would be painted over by the things behind it */
  position: relative; z-index: 2;
}

/* Each testimonial is its own card, and it is the card that moves. */
.tmnl {
  background: var(--card-wash);
  border: 1px solid var(--border-gray);
  border-radius: 18px;
  padding: clamp(1rem, 2vw, 1.6rem);
  box-shadow:
    2px 3px 0 0 rgba(22,35,38,.14),
    4px 6px 4px -1px rgba(22,35,38,.17),
    10px 14px 13px -5px rgba(22,35,38,.20),
    22px 30px 34px -14px rgba(22,35,38,.26),
    38px 54px 74px -24px rgba(22,35,38,.34);
}

/* ---- the track ----
   All the cards sit in one row at full card width, and the row slides so
   the active one lines up with the heading. What shows past either edge
   is therefore the real neighbouring card in its real position: the
   previous card's right-hand side (its words) on the left, the next
   card's left-hand side (its photographs) on the right. The section
   clips the rest.

   Applied only once the script has run (`is-track`). Without it the
   slides stay stacked and toggled with `hidden`, as before. */
.tmnl-wrap { --tmnl-gap: clamp(1rem, 1.8vw, 1.6rem); }
.tmnl-wrap.is-track .tmnl-card {
  display: flex; align-items: stretch;
  gap: var(--tmnl-gap);
  /* 100% is the track's own width, which is one card */
  translate: calc(var(--tmnl-at, 0) * -1 * (100% + var(--tmnl-gap))) 0;
  transition: translate .7s cubic-bezier(.33,.72,.26,1);
}
.tmnl-wrap.is-track .tmnl {
  flex: 0 0 100%;
  transition: opacity .5s ease, scale .7s cubic-bezier(.33,.72,.26,1);
}
/* The neighbours are there to be seen, not read, so they sit back —
   fainter and smaller, and they grow to full size on the same curve as
   the slide. Each shrinks toward the edge facing the active card, so the
   gap between them stays even. */
.tmnl-wrap.is-track .tmnl:not([data-active]) { opacity: .51; scale: .86; }
.tmnl-wrap.is-track .tmnl[data-side="before"] { transform-origin: 100% 50%; }
.tmnl-wrap.is-track .tmnl[data-side="after"]  { transform-origin: 0 50%; }

/* ---- the hit areas ----
   The neighbouring cards are inert (not focusable, not announced), so
   each side gets a transparent button laid over the gutter it shows in.
   It sits entirely outside the active card and runs out past the window
   edge, which the section's overflow clips. */
.tmnl-peek {
  grid-area: 1 / 1;
  align-self: stretch;
  z-index: 3;
  width: 50vw; padding: 0; border: 0;
  background: transparent;
  cursor: pointer;
}
.tmnl-peek--prev { justify-self: start; margin-left: calc(-50vw - var(--tmnl-gap)); }
.tmnl-peek--next { justify-self: end;   margin-right: calc(-50vw - var(--tmnl-gap)); }
.tmnl-peek:focus-visible { outline: none; }
/* no neighbour in that direction: nothing to press */
.tmnl-peek[disabled] { pointer-events: none; }

/* Pointing at (or tabbing to) a side brings that card forward, so the
   hit area answers before it commits. The focus ring goes on the card,
   since the button itself is invisible. */
.tmnl-wrap:has(.tmnl-peek--prev:hover) .tmnl[data-pos="prev"],
.tmnl-wrap:has(.tmnl-peek--next:hover) .tmnl[data-pos="next"] { opacity: .88; }
.tmnl-wrap:has(.tmnl-peek--prev:focus-visible) .tmnl[data-pos="prev"],
.tmnl-wrap:has(.tmnl-peek--next:focus-visible) .tmnl[data-pos="next"] {
  opacity: .88;
  outline: 2px solid var(--haus-blue); outline-offset: 3px;
}


.tmnl-dots {
  grid-area: 2 / 1;
  display: flex; align-items: center; justify-content: center; gap: .45rem;
}
.tmnl-dot {
  width: 8px; height: 8px; padding: 0;
  border: 0; border-radius: 999px;
  background: var(--taupe); opacity: .5;
  cursor: pointer;
  transition: width .28s cubic-bezier(.2,.7,.3,1), opacity .22s ease, background-color .22s ease;
}
.tmnl-dot:hover { opacity: .8; }
.tmnl-dot.is-on {
  width: 26px;                 /* the active one stretches into a pill */
  opacity: 1; background: var(--haus-blue);
}

@media (pointer: coarse), (max-width: 620px) {
  /* The dot stays 8px — it is the indicator. The button around it grows
     into a real thumb target, so the padding goes on rather than the dot
     getting bigger. This has to sit after the base rules: an identical
     selector earlier in the file loses on source order, which is exactly
     what happened the first time. */
  .tmnl-dots { gap: 0; }
  .tmnl-dot {
    width: 8px; height: 8px;
    padding: 16px 9px;
    background-clip: content-box;
    border-radius: 999px;
  }
  .tmnl-dot.is-on { width: 26px; }

  /* primary inline links are CTAs here, so give them a real target */
  .link-arrow { padding-block: .55rem; }
}

/* `display: grid` below outranks the browser's own [hidden] rule, so the
   inactive slides would all stay on screen without this. */
.tmnl[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
  .tmnl-wrap.is-track .tmnl-card,
  .tmnl-wrap.is-track .tmnl { transition: none; }
}

.tmnl {
  margin: 0;
  grid-area: 1 / 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr) minmax(0, 2.1fr);
  gap: clamp(.75rem, 1.4vw, 1.15rem);
  align-items: stretch;
  /* Height lives here, not on the portrait. With align-items: stretch the
     portrait's height is already forced, so an aspect-ratio on it derived
     a width from that height and overflowed into the quote column. */
  min-height: clamp(360px, 32vw, 500px);
}

/* two project shots, stacked, filling the same height as the portrait */
.tmnl__shots { display: grid; grid-template-rows: 1fr 1fr; gap: clamp(.75rem, 1.4vw, 1.15rem); }
/* Each picture is a card in its own right: a 2px white mat, a hairline
   edge drawn as a zero-blur spread so it follows the radius exactly, and
   then the five-step ramp. The earlier version carried the ramp alone at
   a third strength and simply did not read against the card behind it. */
.tmnl__shots img,
.tmnl__portrait--filled {
  border: 2px solid #fff;
  box-shadow:
    0 0 0 1px rgba(207,202,193,.9),
    1px 2px 0 0 rgba(22,35,38,.16),
    3px 4px 3px -1px rgba(22,35,38,.19),
    6px 8px 8px -3px rgba(22,35,38,.22),
    12px 17px 18px -7px rgba(22,35,38,.26),
    20px 29px 38px -12px rgba(22,35,38,.32);
}
.tmnl__shots img {
  width: 100%; height: 100%; object-fit: cover;
  border-radius: 14px; display: block;
}

/* the portrait sets the height of the row */
.tmnl__portrait {
  border-radius: 14px;
  display: grid; place-items: center; text-align: center;
  padding: 1rem;
  background: var(--warm-stone);
  border: 1px dashed var(--taupe);
}
/* PLACEHOLDER PHOTOGRAPHY. These two portraits are generated images, not
   clients. They sit beside a name and a quote, so shipping them would be
   presenting invented people as customers of a real business. Replace
   with real client photographs used with permission, or remove the
   portrait column. Slide 2 is deliberately still empty. */
.tmnl__portrait--filled {
  padding: 0; border: 0;
  overflow: hidden; background: var(--warm-stone);
}
.tmnl__portrait--filled img { width: 100%; height: 100%; object-fit: cover; display: block; }
.tmnl__portrait span {
  font-size: .78rem; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); line-height: 1.7;
}

/* the words */
.tmnl__body {
  margin: 0; padding: clamp(.5rem, 1.5vw, 1rem) clamp(.5rem, 1.5vw, 1rem) .5rem clamp(.75rem, 2vw, 2rem);
  display: flex; flex-direction: column; justify-content: space-between;
  gap: 2rem;
}
.tmnl__quote {
  font-size: clamp(1.05rem, .85rem + .75vw, 1.55rem);
  line-height: 1.45; letter-spacing: -.015em;
  color: var(--deep-haus); text-wrap: pretty;
}
.tmnl__who { display: flex; flex-direction: column; gap: .3rem; }
.tmnl__name { font-size: 1.05rem; font-weight: 600; color: var(--deep-haus); }
/* ---- the outcome pair under the attribution ----
   PLACEHOLDER. The labels are the two things these quotes actually claim
   — that the final number matched the estimate, and that the job ran to
   its schedule — but the figures themselves are Carl's to give. They are
   deliberately em-dashes rather than plausible numbers: invented results
   under a named client read as that client's results. */
.tmnl__stats {
  display: flex; flex-wrap: wrap; align-items: flex-start;
  gap: clamp(.9rem, 2.2vw, 1.75rem);
  margin-top: .95rem; padding-top: .95rem;
  border-top: 1px solid rgba(207,202,193,.55);
}
.tmnl__stat { margin: 0; }
/* the rule between them, carried by the second so it never leads */
.tmnl__stat + .tmnl__stat {
  padding-left: clamp(.9rem, 2.2vw, 1.75rem);
  border-left: 1px solid rgba(207,202,193,.75);
}
.tmnl__stat-v {
  font-size: clamp(1.45rem, 2.4vw, 2rem);
  font-weight: 700; letter-spacing: -.03em; line-height: 1;
  color: var(--deep-haus); font-variant-numeric: tabular-nums;
}
.tmnl__stat-l {
  margin: .4rem 0 0;
  font-size: .78rem; line-height: 1.35;
  color: var(--blue-ink); max-width: 18ch;
}
.tmnl__stats .todo { align-self: center; margin-left: 0; }

.tmnl__place {
  display: flex; align-items: center; gap: .45rem;
  font-size: .88rem; color: var(--muted);
}
.tmnl__place svg {
  width: 15px; height: 15px; flex: none;
  fill: none; stroke: var(--taupe); stroke-width: 1.4;
  stroke-linecap: round; stroke-linejoin: round;
}

@media (max-width: 1000px) {
  .tmnl { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .tmnl__body { grid-column: 1 / -1; padding-left: 0; gap: 1.5rem; }
}
@media (max-width: 1280px) {
  /* Below this the section gutter is narrower than an arrow plus its gap,
     so hanging them outside would push them off the page. They drop under
     the card instead: dots left, arrows right. Raised from 1220px when
     the gap went 3px -> 6px: at 1221px the gutter is 48.8px and the arm
     is now 48px, which left the button touching the window edge. */
  .tmnl-wrap { grid-template-columns: minmax(0, 1fr) auto auto; }
  .tmnl-card { grid-area: 1 / 1 / 2 / 4; }
  .tmnl-dots { grid-area: 2 / 1; justify-content: flex-start; }
  /* The gutter is only a sliver here, too narrow to aim at, so the hit
     areas step aside and the dots carry it. The neighbours still show. */
  .tmnl-peek { display: none; }
}
@media (max-width: 620px) {
  .tmnl { grid-template-columns: 1fr; }
  .tmnl__shots { grid-template-rows: auto; grid-auto-flow: column; }
}

/* ---------- 14 faq ----------
   Framing left, questions right. Both scroll normally. */
.faq-layout {
  display: grid;
  grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr);
  gap: clamp(2rem, 5vw, 5.5rem);
  align-items: start;
}
.faq-intro h2 { margin-top: .85rem; }
.faq-intro__sub { margin-top: 1.1rem; max-width: 32ch; color: var(--muted); }
@media (max-width: 900px) {
  .faq-layout { grid-template-columns: 1fr; gap: 2rem; }
}


.faq { border-top: 1px solid var(--border-gray); }
.faq__item { border-bottom: 1px solid var(--border-gray); }
.faq__q {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.5rem; width: 100%;
  background: transparent; border: 0; cursor: pointer;
  text-align: left; padding: 1.45rem 0;
  font-size: 1.06rem; font-weight: 600; letter-spacing: -.015em;
  color: var(--deep-haus);
}
.faq__q:hover { color: var(--blue-ink); }
.faq__icon {
  flex: none; width: 18px; height: 18px; position: relative;
}
.faq__icon::before, .faq__icon::after {
  content: ""; position: absolute; background: var(--deep-haus);
  left: 50%; top: 50%; translate: -50% -50%;
  transition: background-color .22s ease;
}
.faq__icon::before { width: 16px; height: 1.5px; }
.faq__icon::after  { width: 1.5px; height: 16px; transition: scale .22s ease, background-color .22s ease; }
.faq__q:hover .faq__icon::before,
.faq__q:hover .faq__icon::after { background: var(--blue-ink); }
.faq__q[aria-expanded="true"] .faq__icon::after { scale: 1 0; }
.faq__a { padding: 0 0 1.5rem; color: var(--muted); max-width: 62ch; }

/* ---------- placeholders ---------- */
.ph {
  display: grid; place-items: center;
  padding: 1.75rem; text-align: center;
  background: var(--warm-stone);
  border: 1px dashed var(--border-gray);
  border-radius: var(--radius);
  min-height: 190px;
}
.section--dark .ph { background: rgba(239,236,228,.05); border-color: rgba(239,236,228,.2); }
.ph span { font-size: .8rem; font-weight: 600; letter-spacing: .04em; color: var(--muted); max-width: 34ch; line-height: 1.7; }
.section--dark .ph span { color: #9AA3A2; }
.ph--wide { aspect-ratio: 16 / 9; }

/* build-time flag — delete this rule before launch */
.todo {
  display: inline-block;
  font-size: .62rem; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase;
  color: #8A3A1E; background: #F6E5DC;
  border: 1px solid #D9A992; border-radius: var(--pill);
  padding: .18rem .55rem; margin-left: .5rem;
  vertical-align: middle;
}

/* ---------- 17 contact ----------
   One image plate. Details overlay the top-left, the form floats right,
   and the throwable tags settle in the lower left. Square corners. */
/* Wider container than the rest of the page, and a 30px gap before
   the footer closes the site out. */
.contact { padding-bottom: 30px; }
.contact > .shell { max-width: 1600px; }   /* --shell 1280 + 25% */

.contact__stage {
  position: relative;
  overflow: hidden;
  height: 620px;            /* cut to the line; the card is trimmed to fit */
}
.contact__bg {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
}
.contact__stage::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(100deg,
    rgba(12,20,22,.86) 0%, rgba(12,20,22,.62) 42%, rgba(12,20,22,.34) 72%);
}

.contact__overlay {
  position: relative; z-index: 3;
  /* The overlay covers the whole stage, so without this it swallows every
     pointer event and the tags underneath cannot be grabbed. */
  pointer-events: none;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 550px);   /* 440 + 25% */
  align-items: start;
  gap: clamp(1.5rem, 4vw, 3.5rem);
  padding: clamp(1.75rem, 3.5vw, 3rem);
}

/* ---- details, top-left over the image ---- */
.contact__info {
  color: #fff; max-width: 34ch; pointer-events: none;
  /* Same value as the stage padding, so the copy sits exactly one inset
     in from the edge of the picture rather than hard against it.
     margin, not padding — padding would eat into the 34ch measure. */
  margin-left: clamp(1.75rem, 3.5vw, 3rem);
}
.contact__info a { pointer-events: auto; }
.contact__info .eyebrow { color: var(--champagne); }
.contact__heading {
  margin-top: 1rem;
  font-size: clamp(1.45rem, 1rem + 1.4vw, 2.1rem);
  letter-spacing: -.03em; white-space: nowrap;
}
.contact__list { margin-top: clamp(1.1rem, 2vw, 1.4rem); display: grid; gap: .45rem; }
.contact__list li { display: flex; align-items: center; gap: .85rem; }
.contact__ico {
  flex: none; display: grid; place-items: center;
  width: 40px; height: 40px; border-radius: 50%;
  background: rgba(255,255,255,.12);
  border: 1px solid rgba(255,255,255,.22);
}
.contact__ico svg {
  width: 18px; height: 18px;
  fill: none; stroke: var(--champagne); stroke-width: 1.4;
  stroke-linecap: round; stroke-linejoin: round;
}
.contact__pair { display: flex; flex-direction: column; gap: .15rem; }
.contact__key {
  font-size: .72rem; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase;
  color: rgba(255,255,255,.62);
}
.contact__val {
  font-size: 1rem; font-weight: 600; line-height: 1.25;
  color: #fff; text-decoration: none;
}
a.contact__val:hover { color: var(--haus-blue); }

/* ---- tags: lower-left only, so they never pile under the form ---- */
.tags {
  position: absolute; left: 0; top: 0; z-index: 2;
  /* Invisible play area: the whole left half. Bodies are clamped to it,
     so they can travel up behind the details but never under the form
     card — and its bottom edge IS the floor they land on, which is why
     it stops short of the stage rather than running to it. Shorten this
     and the blocks come to rest higher; the walls are rebuilt from this
     box on every resize. */
  bottom: var(--tag-floor, 20px);
  width: min(50%, 800px); height: auto;
  border: 0; margin: 0; padding: clamp(1rem, 2.5vw, 1.75rem);
  display: flex; flex-wrap: wrap; align-content: flex-end; gap: .55rem;
}
.tag { cursor: grab; }
.tag:active { cursor: grabbing; }
.tags.is-live { display: block; padding: 0; }
.tags.is-live .tag {
  position: absolute; top: 0; left: 0;
  will-change: transform; transform-origin: 50% 50%;
}
.tags.is-live .tag span { user-select: none; -webkit-user-select: none; }
.tag input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.tag span {
  display: inline-block;
  padding: .55rem 1rem;
  font-size: .88rem; font-weight: 600; color: #fff;
  /* Deliberately NOT backdrop-filter. There are eight of these, each its
     own composited layer, and re-sampling the backdrop behind all of them
     every frame made scrolling to the footer stutter badly. A flat tint
     is indistinguishable over a photograph. */
  background: rgba(16,25,28,.78);
  border: 1px solid rgba(255,255,255,.22);
  box-shadow: 0 1px 0 rgba(255,255,255,.20) inset;
  transition: background-color .22s ease, border-color .22s ease, color .22s ease;
}
.tag:hover span { border-color: rgba(255,255,255,.5); }
.tag input:checked + span {
  background: var(--haus-blue); color: var(--deep-haus); border-color: var(--haus-blue);
}
.tag input:focus-visible + span { outline: 2px solid #fff; outline-offset: 2px; }

/* ---- form card, floating right ---- */
.contact__card {
  pointer-events: auto;
  color: var(--charcoal);          /* never inherit the overlay's white */
  /* Frosted panel — light enough that the dark form text still reads
     comfortably over the image behind it. */
  background: rgba(250,249,246,.82);
  -webkit-backdrop-filter: blur(22px) saturate(150%);
          backdrop-filter: blur(22px) saturate(150%);
  border: 1px solid rgba(255,255,255,.55);
  border-radius: 22px;
  padding: clamp(1.35rem, 2.4vw, 1.75rem);
  box-shadow:
    0 1px 0 rgba(255,255,255,.65) inset,
    0 30px 60px -30px rgba(8,14,16,.7);
}

/* No backdrop-filter (older Firefox): go near-solid, never see-through. */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .contact__card { background: rgba(250,249,246,.97); }
}
.contact__title { font-size: clamp(1.5rem, 1.1rem + 1.2vw, 2rem); }
.contact__sub { margin-top: .4rem; color: var(--muted); font-size: .92rem; }
.contact__form { margin-top: 1.15rem; display: flex; flex-direction: column; gap: .85rem; }

.field { display: flex; flex-direction: column; gap: .45rem; }
.field label { font-size: .85rem; font-weight: 600; color: var(--deep-haus); }
/* email + phone, side by side */
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: .85rem; }
@media (max-width: 560px) { .field-row { grid-template-columns: 1fr; } }
.field input, .field textarea {
  font: inherit; font-size: .95rem; color: var(--charcoal);
  background: rgba(255,255,255,.88); border: 1px solid var(--border-gray);
  border-radius: 10px;
  padding: .8rem .95rem; width: 100%;
}
.field textarea { resize: vertical; min-height: 90px; }
.field input::placeholder, .field textarea::placeholder { color: var(--taupe); }
.field input:focus, .field textarea:focus {
  outline: none; border-color: var(--haus-blue);
  box-shadow: 0 0 0 3px rgba(107,160,222,.22);
}

.contact__submit {
  font: inherit; font-size: .95rem; font-weight: 600;
  padding: .85rem 1.5rem; width: 100%;
  border: 1px solid transparent; border-radius: 12px;
  background: var(--deep-haus); color: #fff; cursor: pointer;
  transition: background-color .22s ease, color .22s ease;
}
.contact__submit:hover:not(:disabled) { background: var(--haus-blue); color: var(--deep-haus); }
.contact__submit:disabled { background: var(--taupe); color: var(--soft-white); cursor: not-allowed; }

@media (max-width: 940px) {
  .contact__overlay { grid-template-columns: 1fr; }
  /* Stacked, the stage reads top to bottom: what we are and how to reach
     us, then the form, then the tags. The tags come last because they
     are the one thing on the page that is played with rather than read —
     first in the flow they were a wall of blocks before the heading.
     Flex, because the background image is absolute and the only two
     elements actually in the flow are the overlay and the tags. */
  .contact__stage { height: auto; display: flex; flex-direction: column; }
  .contact__overlay { order: 1; }
  .tags {
    order: 2;
    position: relative; width: 100%; height: auto; min-height: 180px;
    margin-top: clamp(1.5rem, 5vw, 2.5rem);
  }
  .contact__stage::after { background: rgba(12,20,22,.72); }
}

/* ---------- closing CTA (image plate) ----------
   The section itself is ivory and only holds padding, so the page
   breathes above and below the plate instead of the video running
   edge to edge into the sections either side. */
.closer {
  background: var(--hearth-ivory);
  padding-block: clamp(.7rem, 1.4vw, 1.1rem) clamp(1.25rem, 2.9vw, 2.5rem);
}
.closer__plate {
  position: relative; display: grid; overflow: hidden;
  height: 75vh;
  /* The row must be exactly the plate height. Left auto, the poster's
     intrinsic height grows it past 75vh and anything aligned to the end
     lands below the visible area. */
  grid-template-rows: 100%;
}
.closer__media, .closer__video, .closer__content { grid-area: 1 / 1; }
.closer__media {
  width: 100%; height: 100%; min-height: 0; object-fit: cover;
}
/* Video fades in over the still once it can genuinely play. */
.closer__video {
  width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity .9s ease;
  pointer-events: none;
}
.closer__plate.has-video .closer__video { opacity: 1; }
/* Heavy, even scrim — the copy is centred, so unlike the hero it needs
   the whole plate darkened rather than just the lower half. */
.closer__plate::after {
  content: ""; grid-area: 1 / 1; z-index: 1;
  background: linear-gradient(180deg,
    rgba(14,22,24,.94) 0%, rgba(14,22,24,.81) 40%,
    rgba(14,22,24,.85) 70%, rgba(14,22,24,.95) 100%);
}
.closer__content {
  position: relative; z-index: 3;
  align-self: center; justify-self: center;
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
  /* Centred on the plate, but not on its geometric middle: the ghosted
     wordmark occupies the bottom of the frame and carries real visual
     weight, so the extra bottom padding lifts the copy to sit centred in
     the space actually left above it. */
  padding-block: clamp(2rem, 4vw, 3rem) clamp(4rem, 10vw, 8rem);
  color: #fff;
}
@media (max-width: 700px) {
  /* the plate is short here and the wordmark is small, so the copy needs
     the room back rather than the optical lift */
  .closer__content { padding-block: clamp(1.5rem, 5vw, 2.5rem); }
}
.closer__title {
  font-size: clamp(2rem, 1.3rem + 2.4vw, 3rem);
  max-width: 18ch;
}
.closer__sub {
  margin-top: 1rem; max-width: 54ch;
  font-size: 1.02rem; line-height: 1.5;
  color: rgba(255,255,255,.8);
}
.closer__actions {
  display: flex; flex-wrap: wrap; gap: .8rem;
  justify-content: center; margin-top: 1.75rem;
}
.closer__aside { margin-top: 1.1rem; font-size: .9rem; }
.closer__aside .link-arrow { color: rgba(255,255,255,.78); }
.closer__aside .link-arrow:hover { color: #fff; }

/* ghosted wordmark, cropped slightly by the bottom edge */
/* On a phone the plate was a flat 75vh — about 630px — while the copy
   inside it needs barely half that, so the photograph opened into two
   dead bands above and below the words. Sized to its content instead,
   with a floor so it still reads as a plate rather than a caption. The
   row has to go with it: `100%` of an auto height resolves to nothing
   useful, and the picture then drives the height from its own 16:9. */
@media (max-width: 780px) {
  .closer__plate {
    height: auto; min-height: 44vh;
    grid-template-rows: auto;
  }
  .closer__media, .closer__video { height: 100%; min-height: 0; }
  /* the ghosted wordmark still sits on the floor of the plate, so the
     bottom padding is what keeps it off the back of the last link —
     with the plate no longer 630px there is nothing else holding it
     clear */
  .closer__content { padding-block: clamp(2rem, 7vw, 2.75rem) clamp(4rem, 18vw, 5.5rem); }
}

.closer__wordmark {
  grid-area: 1 / 1; z-index: 2;
  align-self: end; justify-self: center;
  /* Back to the earlier resting position (.05em), nudged 5px lower.
     The mask below still takes it to zero opacity at the baseline. */
  margin-bottom: calc(.05em - 5px);
  -webkit-mask-image: linear-gradient(to bottom,
    #000 0%, #000 26%, rgba(0,0,0,.40) 58%, transparent 82%);
          mask-image: linear-gradient(to bottom,
    #000 0%, #000 26%, rgba(0,0,0,.40) 58%, transparent 82%);
  /* 13vw put the lockup at 378px inside a 375px phone and clipped both
     ends; 12vw keeps a gutter and still fills the width on a desktop. */
  font-size: clamp(2.4rem, 12vw, 14rem);
  font-weight: 700; letter-spacing: -.035em; line-height: .8;
  text-transform: uppercase; white-space: nowrap;
  color: rgba(255,255,255,.20);
  user-select: none; pointer-events: none;
}

/* light pill + its dark counterpart, per the reference pairing */
.btn--light { background: var(--soft-white); color: var(--deep-haus); border-color: var(--soft-white); }
.btn--light::after { background: var(--deep-haus); color: var(--soft-white); }
.btn--light:hover { background: #fff; border-color: #fff; }

/* ---------- footer ---------- */
.footer { background: var(--deep-haus); color: #FFFFFF; padding-block: clamp(3.5rem, 7vw, 5.5rem) 2rem; }
/* The brand column carries the logo, the line and the social row, so it
   takes more width than the three link columns beside it. */
.footer__grid {
  display: grid;
  grid-template-columns: 1.5fr 1fr 1.25fr 1fr;
  gap: clamp(2rem, 4vw, 3.5rem);
}
/* the white filter is declared once, with .brand__logo--onDark above */
.footer__logo { height: 34px; width: auto; }
.footer__brandline { margin-top: 1.1rem; max-width: 30ch; font-size: .95rem; }
.footer__head { font-size: .76rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: #fff; margin-bottom: 1rem; }
.footer__list { display: flex; flex-direction: column; gap: .55rem; font-size: .95rem; }
.footer__list a { text-decoration: none; }
.footer__list a:hover { color: var(--haus-blue); }

/* ---- social row ---- */
.footer__sociallabel {
  margin-top: 1.9rem;
  display: flex; align-items: center; gap: .6rem;
  font-size: .76rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; color: #fff;
}
.footer__social { display: flex; gap: .6rem; margin-top: .85rem; }
.footer__social a {
  width: 40px; height: 40px;
  display: grid; place-items: center;
  border: 1px solid rgba(239,236,228,.2);
  border-radius: 50%;
  color: #fff;
  transition: background-color .22s ease, border-color .22s ease, color .22s ease;
}
.footer__social a:hover {
  background: var(--haus-blue); border-color: var(--haus-blue); color: var(--deep-haus);
}
.footer__social svg {
  width: 18px; height: 18px;
  fill: none; stroke: currentColor; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}
/* glyphs that only read as solid shapes — the f and the play triangle */
.footer__social svg .fill { fill: currentColor; stroke: none; }
.footer__social svg .dot  { fill: currentColor; stroke: none; }

/* ---- contact column: each line gets its own badge ---- */
.footer__contact { display: flex; flex-direction: column; gap: .85rem; font-size: .95rem; }
.footer__contact li { display: flex; align-items: center; gap: .7rem; }
.footer__contact a { text-decoration: none; }
.footer__contact a:hover { color: var(--haus-blue); }
.footer__ico {
  flex: none; width: 34px; height: 34px;
  display: grid; place-items: center;
  border: 1px solid rgba(239,236,228,.2);
  border-radius: 50%;
}
.footer__ico svg {
  width: 16px; height: 16px;
  fill: none; stroke: var(--champagne); stroke-width: 1.3;
  stroke-linecap: round; stroke-linejoin: round;
}

/* ---- bottom bar: copyright / credit / policy ---- */
.footer__legal {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center; gap: .5rem 1.5rem;
  margin-top: clamp(2.5rem, 5vw, 4rem); padding-top: 1.6rem;
  border-top: 1px solid rgba(239,236,228,.14);
  font-size: .8rem;
}
.footer__legal a { text-decoration: none; }
.footer__legal > :last-child { justify-self: end; }
.footer__craft strong { font-weight: 600; }
.footer__craft a { color: inherit; }
.footer__craft a:hover { color: var(--haus-blue); }

@media (max-width: 900px) {
  .footer__grid { grid-template-columns: 1fr 1fr; }
  .footer__brand { grid-column: 1 / -1; }
}
@media (max-width: 560px) {
  .footer__grid { grid-template-columns: 1fr; }
  .footer__legal { grid-template-columns: 1fr; justify-items: start; }
  .footer__legal > :last-child { justify-self: start; }
}

/* ---------- contact page (/contact) ----------
   Laid out after fintruction.com/contact: one centred column — pill
   eyebrow, headline with a gradient phrase, tabs for the kind of
   enquiry — then a single white questionnaire card with an accent
   stripe, and office cards under it. Their teal is swapped for the
   Haus Blue family; the ink stays Deep Haus.

   The active tab and the submit sit on the darker end of the blue
   (#3F77BD to Blue Ink) because white type on Haus Blue itself only
   reaches about 2.6:1. */
.cf-page {
  --cf-grad: linear-gradient(135deg, #4A82C8 0%, #3A70B4 50%, var(--blue-ink) 100%);
  --cf-line: #D9E3EE;
  --cf-line-soft: #E8EEF5;
  --cf-soft: #F5F8FC;
  --cf-ring: 0 0 0 4px rgba(107,160,222,.24);
  --cf-shadow-sm: 0 2px 8px rgba(22,35,38,.06);
  --cf-shadow-md: 0 10px 28px rgba(22,35,38,.08);
  --cf-shadow-lg: 0 20px 50px rgba(22,35,38,.12);
  background:
    radial-gradient(90% 60% at 50% 0%, rgba(107,160,222,.14), transparent 70%),
    var(--soft-white);
}
.cf { padding-inline: var(--gutter); }

/* ---- hero ---- */
.cf-hero {
  max-width: 920px; margin: 0 auto;
  /* the nav is fixed over the top of the page, so the column starts
     below it rather than under it */
  padding: clamp(6.5rem, 11vw, 8.5rem) 0 1.75rem;
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
}
.cf-eyebrow {
  display: inline-flex; align-items: center; gap: .5rem;
  margin: 0 0 1rem;
  padding: .44rem .9rem .44rem .7rem;
  background: rgba(107,160,222,.12);
  border: 1px solid rgba(107,160,222,.30);
  border-radius: var(--pill);
  font-size: .72rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: var(--blue-ink);
  box-shadow: 0 4px 14px rgba(107,160,222,.12);
}
.cf-eyebrow svg {
  width: 13px; height: 13px; fill: var(--blue-ink); stroke: none;
  animation: cf-pulse 2.2s ease-in-out infinite;
}
.cf-eyebrow svg circle { fill: #fff; }
@keyframes cf-pulse {
  0%, 100% { scale: 1; opacity: 1; }
  50%      { scale: 1.15; opacity: .75; }
}
.cf-title {
  max-width: 760px; margin: 0 0 .9rem;
  font-size: clamp(1.9rem, 1.2rem + 2.4vw, 2.6rem);
  font-weight: 800; line-height: 1.12; letter-spacing: -.03em;
  color: var(--deep-haus);
}
.cf-hl {
  background: var(--cf-grad);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
.cf-sub {
  max-width: 620px; margin: 0 0 1.4rem;
  font-size: 1rem; font-weight: 500; line-height: 1.55;
  color: var(--muted);
}

/* ---- tabs ---- */
.cf-tabs { display: flex; flex-wrap: wrap; justify-content: center; gap: .625rem; }
.cf-tab {
  display: inline-flex; align-items: center; gap: .45rem;
  padding: .7rem 1.4rem;
  border-radius: var(--pill);
  background: #fff; color: var(--deep-haus);
  border: 1.5px solid var(--cf-line);
  font: inherit; font-size: .875rem; font-weight: 700; line-height: 1;
  white-space: nowrap; cursor: pointer;
  box-shadow: var(--cf-shadow-sm);
  transition: translate .18s ease, box-shadow .18s ease, border-color .18s ease, background .18s ease, color .18s ease;
}
.cf-tab svg {
  width: 15px; height: 15px; flex: none;
  fill: none; stroke: var(--blue-ink); stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
  transition: stroke .18s ease, translate .18s ease;
}
.cf-tab:hover { border-color: var(--haus-blue); translate: 0 -2px; box-shadow: var(--cf-shadow-md); }
.cf-tab:hover svg { translate: 1px 0; }
.cf-tab.is-on {
  background: var(--cf-grad); color: #fff; border-color: transparent;
  box-shadow: 0 12px 26px rgba(44,95,158,.30), 0 0 0 4px rgba(107,160,222,.14);
  translate: 0 -1px;
}
.cf-tab.is-on svg { stroke: #fff; }
.cf-tab:focus-visible { outline: none; box-shadow: var(--cf-shadow-md), var(--cf-ring); }

/* ---- the card ---- */
.cf-wrap { max-width: 980px; margin: 0 auto; padding-block: 1.75rem 3rem; }
.cf-card {
  position: relative; overflow: hidden;
  background: #fff;
  border: 1px solid var(--cf-line-soft);
  border-radius: 22px;
  box-shadow: var(--cf-shadow-lg);
}
.cf-card::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 4px; z-index: 1;
  background: var(--cf-grad);
}
/* A slow diagonal light that crosses the card, then rests before the
   next pass. It sits over the fields but is pale and pointer-less, so it
   reads as a sheen on the card rather than something in the way. The
   card's own overflow clips it to the rounded corners. */
.cf-card::after {
  content: ""; position: absolute; inset: 0; z-index: 2;
  pointer-events: none;
  background: linear-gradient(105deg,
    transparent 35%,
    rgba(107,160,222,.064) 43%,
    rgba(255,255,255,.44) 50%,
    rgba(107,160,222,.064) 57%,
    transparent 65%);
  background-size: 250% 100%;
  background-repeat: no-repeat;
  background-position: 110% 0;
  animation: cf-sweep 6s cubic-bezier(.45,.05,.35,1) infinite;
}
@keyframes cf-sweep {
  0%        { background-position: 110% 0; }
  45%, 100% { background-position: -10% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .cf-card::after { animation: none; display: none; }
}
.cf-form { padding: clamp(1.4rem, 3.4vw, 2.5rem); }

.cf-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.1rem 1rem; }
.cf-field { display: flex; flex-direction: column; }
.cf-field label, .cf-q__label {
  margin-bottom: .44rem;
  font-size: .84rem; font-weight: 700; letter-spacing: -.005em;
  color: var(--deep-haus);
}
.cf label b, .cf legend b { color: var(--blue-ink); font-weight: 700; margin-left: 2px; }

.cf-field input, .cf-q textarea {
  width: 100%; height: 46px;
  padding: .625rem .875rem;
  border-radius: 12px;
  border: 1.5px solid var(--cf-line);
  background: #fff; color: var(--deep-haus);
  font: inherit; font-size: .9rem; font-weight: 600;
  transition: border-color .18s ease, box-shadow .18s ease;
}
.cf-q textarea { height: auto; min-height: 110px; padding: .75rem .875rem; resize: vertical; line-height: 1.5; }
.cf-field input::placeholder, .cf-q textarea::placeholder { color: #9AA6B2; font-weight: 500; }
.cf-field input:hover, .cf-q textarea:hover { border-color: #AFC6E0; }
.cf-field input:focus, .cf-q textarea:focus {
  outline: none; border-color: var(--haus-blue); box-shadow: var(--cf-ring);
}

/* ---- question groups ---- */
.cf-q { margin: .9rem 0 0; padding: 0; border: 0; min-width: 0; }
/* a little more air between the typed fields and the first question */
.cf-grid + .cf-q { margin-top: 1.1rem; }
.cf-q legend, .cf-q__label {
  display: block; padding: 0; margin-bottom: .45rem;
  font-size: .94rem; font-weight: 800; letter-spacing: -.01em;
  color: var(--deep-haus);
}
.cf-opts { display: flex; flex-wrap: wrap; gap: .4rem .6rem; margin-left: -.75rem; }

.cf-check {
  display: inline-flex; align-items: center; gap: .56rem;
  min-height: 38px; padding: .4rem .75rem;
  border-radius: 10px; border: 1.5px solid transparent;
  font-size: .875rem; font-weight: 600; line-height: 1.3; color: var(--deep-haus);
  cursor: pointer;
  transition: border-color .18s ease, background .18s ease;
}
.cf-check:hover { border-color: var(--cf-line); background: var(--cf-soft); }
.cf-check:has(input:checked) { border-color: var(--haus-blue); background: rgba(107,160,222,.08); }

.cf-check input {
  appearance: none; -webkit-appearance: none;
  flex: none; width: 19px; height: 19px; margin: 0;
  display: grid; place-items: center;
  border: 1.8px solid var(--blue-ink); border-radius: 5px;
  background: #fff; cursor: pointer;
  transition: scale .15s ease, background-color .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.cf-check input[type="radio"] { border-radius: 50%; }
.cf-check input:hover { scale: 1.05; border-color: var(--deep-haus); }
.cf-check input:focus-visible { outline: none; box-shadow: var(--cf-ring); }
.cf-check input:checked {
  background-color: var(--blue-ink); border-color: var(--blue-ink);
  animation: cf-pop .28s ease;
}
/* the tick and the dot, drawn in CSS so they need no image */
.cf-check input[type="checkbox"]:checked::after {
  content: ""; width: 5px; height: 9px; margin-top: -2px;
  border: solid #fff; border-width: 0 2px 2px 0; rotate: 45deg;
}
.cf-check input[type="radio"]:checked::after {
  content: ""; width: 7px; height: 7px; border-radius: 50%; background: #fff;
}
@keyframes cf-pop {
  0%   { scale: .85; }
  60%  { scale: 1.12; }
  100% { scale: 1; }
}

/* Desktop: a question gets a soft panel while you are in it, and keeps
   a calmer one once it is answered. Negative margin with matching
   padding, so the panel grows outward and nothing inside moves. The
   stripe down the left edge only marks the question in hand. */
@media (min-width: 768px) {
  .cf-q {
    position: relative;
    padding: .5rem 1.125rem; margin-inline: -1.125rem;
    border: 1px solid transparent; border-radius: 14px;
    transition: background .24s ease, border-color .24s ease, box-shadow .28s ease;
  }
  .cf-q + .cf-q { margin-top: .25rem; }
  .cf-q legend { float: left; width: 100%; }
  .cf-q legend + .cf-opts { clear: both; }
  .cf-q:focus-within,
  .cf-q:has(input:checked),
  .cf-q:has(textarea:not(:placeholder-shown)) {
    background: rgba(107,160,222,.05);
    border-color: rgba(107,160,222,.24);
    box-shadow: 0 8px 24px rgba(22,35,38,.07);
  }
  .cf-q::before {
    content: ""; position: absolute; top: 50%; left: -2px;
    width: 3px; height: 0; translate: 0 -50%;
    background: var(--cf-grad); border-radius: 3px; opacity: 0;
    transition: height .28s ease, opacity .24s ease;
  }
  .cf-q:focus-within::before { height: calc(100% - 24px); opacity: 1; }
}

/* ---- submit + proof ---- */
.cf-actions { display: flex; flex-direction: column; align-items: center; gap: 1.1rem; margin-top: 1.5rem; }
.cf-submit {
  display: inline-flex; align-items: center; justify-content: center; gap: .75rem;
  min-width: 250px; padding: 1.05rem 2.6rem;
  border: 0; border-radius: var(--pill);
  background: var(--cf-grad); color: #fff;
  font: inherit; font-size: 1.05rem; font-weight: 800; letter-spacing: .01em; line-height: 1;
  white-space: nowrap; cursor: pointer;
  box-shadow: 0 14px 30px rgba(44,95,158,.30);
  transition: translate .18s ease, box-shadow .18s ease, filter .18s ease, opacity .18s ease;
}
.cf-submit svg {
  width: 20px; height: 20px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
  transition: translate .22s ease;
}
.cf-submit:not(:disabled):hover { translate: 0 -2px; box-shadow: 0 20px 40px rgba(44,95,158,.38); filter: brightness(1.05); }
.cf-submit:not(:disabled):hover svg { translate: 4px 0; }
.cf-submit:not(:disabled):active { translate: 0 0; }
.cf-submit:focus-visible { outline: none; box-shadow: 0 14px 30px rgba(44,95,158,.30), var(--cf-ring); }
/* waits for the required fields, as the home page form does */
.cf-submit:disabled { opacity: .55; cursor: not-allowed; box-shadow: none; }

.cf-proof {
  display: inline-flex; align-items: center; flex-wrap: wrap; justify-content: center; gap: .875rem;
  padding: .625rem 1.125rem;
  background: rgba(107,160,222,.06);
  border: 1px solid rgba(107,160,222,.20);
  border-radius: 14px;
  box-shadow: var(--cf-shadow-sm);
}
.cf-proof__faces { display: inline-flex; }
.cf-proof__faces img {
  width: 44px; height: 44px; object-fit: cover;
  border-radius: 50%; border: 2px solid #fff;
  box-shadow: 0 0 0 2px rgba(107,160,222,.55), 0 4px 12px rgba(22,35,38,.12);
  margin-left: -12px;
  transition: translate .2s ease;
}
.cf-proof__faces img:first-child { margin-left: 0; }
.cf-proof__faces img:hover { translate: 0 -2px; }
.cf-proof__rule { width: 1px; height: 24px; background: rgba(22,35,38,.16); }
.cf-proof__copy { font-size: .84rem; font-weight: 600; color: var(--muted); }
.cf-proof__copy b { font-weight: 800; color: var(--deep-haus); }

/* ---- contact questions ----
   The home page FAQ as it is. <main> here already carries the page
   gutter, so the section steps back out of it and lets .shell set the
   edges, the same as on the home page. The home #faq rule trims its
   padding for the dark blocks either side of it there; this one sits on
   open ground and gets the normal rhythm back. */
.cf-faq { margin-inline: calc(var(--gutter) * -1); }
#faq.cf-faq { padding-block: clamp(2.5rem, 5vw, 4rem) clamp(1rem, 2vw, 1.5rem); }
.cf-faq__cta { margin-top: 1.5rem; }

/* ---- office cards ---- */
.cf-locations {
  max-width: 980px; margin: 0 auto;
  padding-block: 2.5rem 5rem;
  display: flex; flex-direction: column; align-items: center; text-align: center;
}
.cf-loc-title {
  margin: 0 0 2.25rem;
  font-size: clamp(1.9rem, 1.3rem + 2vw, 2.75rem);
  font-weight: 800; letter-spacing: -.03em; color: var(--deep-haus);
}
.cf-loc-grid { width: 100%; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.25rem; text-align: left; }
.cf-loc {
  background: #fff;
  border: 1px solid var(--cf-line-soft);
  border-radius: 22px; padding: .875rem;
  box-shadow: var(--cf-shadow-md);
  transition: translate .22s ease, box-shadow .22s ease;
}
.cf-loc:hover { translate: 0 -4px; box-shadow: var(--cf-shadow-lg); }
/* Close to square: the map in the office card is drawn for a square frame,
   and a wide short one crops the office off its top edge. */
.cf-loc__img { width: 100%; height: clamp(300px, 31vw, 420px); object-fit: cover; border-radius: 14px; }
.cf-loc__img--face { object-position: 50% 22%; }
/* The map in the office card. On the home page the frame is a card of its
   own with a tall minimum and a heavy shadow; here the card is already the
   card, so the frame takes the photo's place instead — same height and
   corners as the picture beside it, no second shadow. */
.cf-loc .hhmap { height: auto; }
.cf-loc .hhmap__frame {
  min-height: 0;
  height: clamp(300px, 31vw, 420px);
  border-radius: 14px;
  box-shadow: none;
}
.cf-loc__body { padding: 1.1rem .5rem .6rem; }
.cf-loc__body h3 { margin: 0 0 .35rem; font-size: 1.3rem; font-weight: 800; letter-spacing: -.02em; color: var(--deep-haus); }
.cf-loc__body p { margin: 0; font-size: .9rem; font-weight: 600; line-height: 1.5; color: var(--muted); }
.cf-loc__body a { color: var(--blue-ink); text-decoration: none; }
.cf-loc__body a:hover { text-decoration: underline; }

/* ---- call button on the right edge ---- */
.cf-stick {
  position: fixed; right: 18px; top: 50%; translate: 0 -50%; z-index: 55;
  display: flex; align-items: center;
  border-radius: var(--pill);
  background: var(--cf-grad); color: #fff; text-decoration: none;
  box-shadow: 0 10px 24px rgba(44,95,158,.35);
  transition: opacity .3s ease, scale .3s cubic-bezier(.2,.7,.3,1), visibility 0s linear 0s;
}
/* Out of the way while the page is moving; it comes back once scrolling
   settles. `visibility` waits for the fade, so the button cannot be hit
   or tabbed to while it is gone. */
.cf-stick.is-away {
  opacity: 0; scale: .7; pointer-events: none; visibility: hidden;
  transition: opacity .2s ease, scale .2s ease, visibility 0s linear .2s;
}
.cf-stick__ico { width: 48px; height: 48px; flex: none; display: grid; place-items: center; }
.cf-stick__ico svg { width: 20px; height: 20px; fill: none; stroke: #fff; stroke-width: 1.6; stroke-linejoin: round; }
.cf-stick__body {
  display: grid; overflow: hidden;
  max-width: 0; opacity: 0; padding-left: 0;
  white-space: nowrap; line-height: 1.2;
  transition: max-width .3s cubic-bezier(.2,.7,.3,1), opacity .2s ease, padding .3s ease;
}
.cf-stick__body b { font-size: .8rem; font-weight: 800; }
.cf-stick__body span { font-size: .75rem; opacity: .85; }
@media (hover: hover) {
  .cf-stick:hover .cf-stick__body,
  .cf-stick:focus-visible .cf-stick__body { max-width: 180px; opacity: 1; padding-left: 1.1rem; }
}
.cf-stick:focus-visible { outline: 2px solid var(--haus-blue); outline-offset: 3px; }

@media (max-width: 720px) {
  .cf-grid, .cf-loc-grid { grid-template-columns: 1fr; }
  .cf-submit { width: 100%; min-width: 0; }
  .cf-stick { top: auto; bottom: 18px; translate: none; }
  .cf-proof__rule { display: none; }
}

/* ---------- projects page (/projects) ----------
   Same light page and centred hero as /contact. The work is grouped by
   street, since that is how the houses actually sit on the island, and
   the pills filter by kind. Cards borrow the office cards' shape: a
   white frame, the photograph inset with its own corners. */
.pj { padding-block: .5rem clamp(2.5rem, 5vw, 4rem); }
.pj-filter { display: flex; flex-wrap: wrap; justify-content: center; gap: .625rem; margin-bottom: clamp(1.75rem, 3.5vw, 2.75rem); }

.pj-group + .pj-group { margin-top: clamp(2.25rem, 4.5vw, 3.5rem); }
.pj-group[hidden] { display: none; }
.pj-group__head {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: .35rem 1.5rem;
  padding-bottom: .9rem; margin-bottom: 1.25rem;
  border-bottom: 1px solid var(--cf-line);
}
.pj-group__title { margin: 0; font-size: clamp(1.4rem, 1.1rem + 1vw, 1.9rem); font-weight: 800; letter-spacing: -.03em; color: var(--deep-haus); }
.pj-group__meta { margin: 0; font-size: .88rem; font-weight: 600; color: var(--muted); }
.pj-group__meta b { color: var(--blue-ink); font-weight: 800; }

.pj-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.25rem; }
.pj-card {
  background: #fff;
  border: 1px solid var(--cf-line-soft);
  border-radius: 22px; padding: .75rem;
  box-shadow: var(--cf-shadow-md);
  transition: translate .22s ease, box-shadow .22s ease;
}
.pj-card:hover { translate: 0 -4px; box-shadow: var(--cf-shadow-lg); }
.pj-card__media { position: relative; overflow: hidden; border-radius: 14px; }
.pj-card__media img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover;
  transition: scale .6s cubic-bezier(.2,.7,.3,1);
}
.pj-card:hover .pj-card__media img { scale: 1.04; }
.pj-card__kind {
  position: absolute; left: .7rem; top: .7rem;
  padding: .32rem .7rem; border-radius: var(--pill);
  background: rgba(255,255,255,.9);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  font-size: .7rem; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  color: var(--blue-ink);
}
.pj-card__body { padding: 1rem .4rem .45rem; }
.pj-card__name { margin: 0 0 .3rem; font-size: 1.2rem; font-weight: 800; letter-spacing: -.02em; color: var(--deep-haus); }
.pj-card__meta { margin: 0; font-size: .88rem; font-weight: 600; color: var(--muted); }
.pj-card__spec { margin: .35rem 0 0; font-size: .8rem; color: #8C9594; }

@media (max-width: 960px) { .pj-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 600px) { .pj-grid { grid-template-columns: 1fr; } }

/* The inner pages open on a light hero rather than a photograph, so the
   first home-page section after it needs no extra gap of its own. */
.cf-page .cf-hero + * { margin-top: 0; }

/* ---------- about page: hero ----------
   Copy on the left, a photograph panel on the right. Brighter blue than
   the rest of the site on purpose — this block follows the approved
   mockup: a royal-blue accent for the last word, the primary button and
   the chip icons, with the ink kept to Deep Haus. */
.ab-hero {
  --ab-blue: #2F6BF0;
  --ab-blue-2: #5B8DEF;
  --ab-grad: linear-gradient(135deg, #4A86F5 0%, #2F6BF0 50%, #2356D6 100%);
  --ab-soft: rgba(47,107,240,.10);
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.18fr) minmax(0, 1fr);
  gap: clamp(2rem, 3.6vw, 3.5rem);
  align-items: center;
  padding-top: clamp(6.25rem, 10vw, 8rem);
  padding-bottom: clamp(2.5rem, 5vw, 4rem);
}
/* the faint sweeping arc in the top-right corner of the mockup */
.ab-hero::before {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  right: -12%; top: -6%; width: 55%; aspect-ratio: 1;
  border-radius: 50%;
  border: 1px solid rgba(47,107,240,.07);
}

/* the site's own section label, as on every other section */
.ab-hero__copy > .eyebrow { margin-bottom: 1.25rem; }

/* Heading and copy in the site's own type — the same .h-lg, .hl and
   muted prose as "Who you'll be working with" further down — so the page
   opens in the voice it continues in. */
.ab-title { margin-bottom: 1.6rem; }
.ab-prose { max-width: 60ch; }

/* one line on desktop; they only wrap once the column is too narrow */
.ab-chips { display: flex; flex-wrap: nowrap; gap: .4rem; margin: 1.6rem 0 1.9rem; padding: 0; list-style: none; }
@media (max-width: 1240px) { .ab-chips { flex-wrap: wrap; } }
.ab-chip {
  display: inline-flex; align-items: center; gap: .45rem;
  padding: .22rem .6rem .22rem .22rem;
  white-space: nowrap; flex: none;
  border-radius: var(--pill);
  background: rgba(255,255,255,.72);
  border: 1px solid rgba(47,107,240,.08);
  box-shadow: 0 6px 18px -8px rgba(47,107,240,.22);
  font-size: .74rem; font-weight: 500; color: #24407A;
  text-decoration: none;
  transition: translate .2s ease, box-shadow .2s ease, background .2s ease;
}
/* the one that opens the ledger view is a button, not a link */
button.ab-chip { font-family: inherit; line-height: inherit; cursor: pointer; }
.ab-chip:hover { translate: 0 -2px; background: #fff; box-shadow: 0 12px 24px -10px rgba(47,107,240,.32); }
.ab-chip__ico {
  flex: none; width: 24px; height: 24px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--ab-soft);
}
.ab-chip__ico svg { width: 12px; height: 12px; fill: none; stroke: var(--ab-blue); stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.ab-chip .ab-arrow { flex: none; width: 11px; height: 11px; margin-left: .05rem; stroke: #7EA3EE; }
.ab-arrow { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; transition: translate .2s ease; }
.ab-chip:hover .ab-arrow, .ab-btn:hover .ab-arrow { translate: 3px 0; }

.ab-actions { display: flex; flex-wrap: wrap; gap: .9rem; }
.ab-btn {
  display: inline-flex; align-items: center; gap: .9rem;
  padding: 1.05rem 1.9rem;
  border-radius: var(--pill);
  font-size: 1rem; font-weight: 600; text-decoration: none; white-space: nowrap;
  transition: translate .2s ease, box-shadow .2s ease, filter .2s ease, border-color .2s ease;
}
.ab-btn .ab-arrow { width: 18px; height: 18px; }
.ab-btn--primary {
  background: var(--ab-grad); color: #fff;
  box-shadow: 0 16px 32px -12px rgba(47,107,240,.6);
}
.ab-btn--primary:hover { translate: 0 -2px; filter: brightness(1.06); box-shadow: 0 20px 38px -12px rgba(47,107,240,.7); }
.ab-btn--ghost {
  background: rgba(255,255,255,.8); color: var(--deep-haus);
  border: 1px solid #DCE3EE;
}
.ab-btn--ghost:hover { translate: 0 -2px; border-color: #B9C8E4; box-shadow: 0 12px 26px -14px rgba(22,35,38,.3); }
.ab-btn:focus-visible, .ab-chip:focus-visible { outline: 2px solid var(--ab-blue); outline-offset: 3px; }

/* ---- the photograph panel ---- */
.ab-photo {
  position: relative; margin: 0;
  border-radius: 30px; overflow: hidden;
  /* The figure cards' hard-to-soft ramp, at the full size the ledger and
     the founder portrait use since this panel is that large: a crisp first
     step against the edge, then each stop wider and further out. */
  box-shadow:
    0 0 0 1px rgba(207,202,193,.6),
    2px 3px 0 0 rgba(22,35,38,.14),
    4px 6px 4px -1px rgba(22,35,38,.17),
    10px 14px 13px -5px rgba(22,35,38,.20),
    22px 30px 34px -14px rgba(22,35,38,.26),
    38px 54px 74px -24px rgba(22,35,38,.34);
  isolation: isolate;
}
.ab-photo__frame { position: relative; aspect-ratio: 1.42; background: #1B2A3A; }
.ab-slide {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 55%;
  opacity: 0; scale: 1.04;
  transition: opacity .7s ease, scale 1.2s cubic-bezier(.2,.7,.3,1);
}
.ab-slide.is-on { opacity: 1; scale: 1; }
/* a light foot so the caption and arrows read on any photograph */
.ab-photo__frame::after {
  content: ""; position: absolute; inset: auto 0 0 0; height: 38%; z-index: 1;
  background: linear-gradient(180deg, transparent, rgba(8,14,22,.55));
  pointer-events: none;
}

.ab-photo__cap, .ab-photo__nav button {
  background: rgba(20,28,36,.38);
  -webkit-backdrop-filter: blur(12px) saturate(140%);
          backdrop-filter: blur(12px) saturate(140%);
  border: 1px solid rgba(255,255,255,.22);
  color: #fff;
}
.ab-photo__cap {
  position: absolute; left: clamp(.9rem, 2vw, 1.4rem); bottom: clamp(.9rem, 2vw, 1.4rem); z-index: 2;
  display: inline-flex; align-items: center; gap: .55rem;
  max-width: calc(100% - 8.2rem);
  padding: .6rem 1rem .6rem .85rem;
  border-radius: var(--pill);
  font-size: .68rem; font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
  white-space: nowrap; overflow: hidden;
}
.ab-photo__cap svg { flex: none; width: 14px; height: 14px; fill: none; stroke: #fff; stroke-width: 1.6; }
.ab-photo__cap span { overflow: hidden; text-overflow: ellipsis; }
.ab-photo__rule { flex: none; width: 18px; height: 1px; background: rgba(255,255,255,.7); }

.ab-photo__nav {
  position: absolute; right: clamp(.9rem, 2vw, 1.4rem); bottom: clamp(.9rem, 2vw, 1.4rem); z-index: 2;
  display: flex;
}
.ab-photo__nav button {
  width: 44px; height: 44px; margin: 0; padding: 0;
  display: grid; place-items: center;
  border-radius: 50%; cursor: pointer;
  transition: background .2s ease;
}
.ab-photo__nav button + button { margin-left: -1px; }
.ab-photo__nav button:hover { background: rgba(20,28,36,.6); }
.ab-photo__nav button:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.ab-photo__nav svg { width: 18px; height: 18px; fill: none; stroke: #fff; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

@media (max-width: 960px) {
  .ab-hero { grid-template-columns: 1fr; }
  .ab-hero::before { display: none; }
}
@media (max-width: 520px) {
  .ab-photo__frame { aspect-ratio: 1.1; }
  .ab-photo__cap { font-size: .66rem; letter-spacing: .1em; max-width: calc(100% - 1.8rem); bottom: 4.6rem; }
  .ab-btn { padding: .95rem 1.5rem; }
}

/* ---------- about page: meet the team ----------
   A deep navy band with soft light sweeping in from the corners, after
   the approved mockup. The portraits drift sideways on one row that
   holds its set of tiles twice and travels exactly half its width, so the
   loop never shows a seam; the edges fade through a mask. Figures sit on
   a glass panel, each with an icon tile, and the call to action is a pale
   blue pill. */
.team {
  --team-tile: clamp(112px, 12.5vw, 188px);
  --team-gap: clamp(.75rem, 1.2vw, 1.1rem);
  position: relative; isolation: isolate;
  color: #fff;
  padding-block: clamp(2.75rem, 5vw, 4rem);
  overflow: hidden;
  background:
    radial-gradient(60% 55% at 100% 100%, rgba(107,160,222,.22), transparent 70%),
    radial-gradient(45% 50% at 0% 0%, rgba(107,160,222,.14), transparent 70%),
    linear-gradient(180deg, #1B2634 0%, #1E2B3B 55%, #1A2432 100%);
}
/* the light sweeps: two wide soft rings clipped by the band's edges */
.team::before, .team::after {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  border-radius: 50%;
  border: 1px solid rgba(160,195,240,.12);
  box-shadow: 0 0 80px rgba(107,160,222,.10), inset 0 0 60px rgba(107,160,222,.06);
}
.team::before { width: 60vw; aspect-ratio: 1; left: -38vw; top: -30vw; }
.team::after  { width: 52vw; aspect-ratio: 1; right: -30vw; bottom: -34vw; }

.team__head { display: flex; flex-direction: column; align-items: center; text-align: center; }
.team .eyebrow {
  background: rgba(255,255,255,.06);
  border: 1px solid rgba(160,195,240,.18);
  color: #E6EEF9;
}
.team .eyebrow::before { background: #6BA0DE; }
.team__head h2 { margin-top: .9rem; color: #fff; }
.team__head .hl { color: var(--haus-blue); }
.team__sub { margin-top: .8rem; max-width: 50ch; font-size: 1.05rem; line-height: 1.7; color: rgba(230,238,249,.78); }

.team__rows {
  display: grid; gap: var(--team-gap);
  margin-top: clamp(1.75rem, 3.2vw, 2.5rem);
  padding-block: .6rem 1.2rem;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 8%, #000 92%, transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 8%, #000 92%, transparent 100%);
}
.team__row { display: flex; width: max-content; animation: team-left 70s linear infinite; }
.team__row--rev { animation-name: team-right; animation-duration: 80s; }
.team__rows:hover .team__row { animation-play-state: paused; }
@keyframes team-left  { from { translate: 0 0; }    to { translate: -50% 0; } }
@keyframes team-right { from { translate: -50% 0; } to { translate: 0 0; } }

.team__set { display: flex; gap: var(--team-gap); padding-right: var(--team-gap); }
.team__tile {
  flex: none; width: var(--team-tile); aspect-ratio: 1.08;
  border-radius: 22px; overflow: hidden;
  border: 1px solid rgba(190,215,250,.22);
  box-shadow: 0 14px 30px -14px rgba(0,0,0,.6), 0 0 22px -6px rgba(107,160,222,.35);
  transition: translate .3s cubic-bezier(.2,.7,.3,1), box-shadow .3s ease;
}
.team__tile:hover { translate: 0 -4px; box-shadow: 0 18px 34px -14px rgba(0,0,0,.6), 0 0 30px -4px rgba(107,160,222,.5); }
.team__tile--photo img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 30%; }

/* ---- figures on a glass panel ---- */
.team__nums {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  max-width: 780px; margin: 0 auto;
  padding: clamp(1.1rem, 2vw, 1.5rem) clamp(1rem, 2.5vw, 2rem);
  border-radius: 20px;
  background: linear-gradient(180deg, rgba(255,255,255,.07), rgba(255,255,255,.03));
  border: 1px solid rgba(160,195,240,.18);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.06), 0 20px 40px -24px rgba(0,0,0,.6);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
}
.team__num {
  display: flex; align-items: center; justify-content: center; gap: .9rem;
  padding-inline: .75rem;
}
.team__num + .team__num { border-left: 1px solid rgba(160,195,240,.14); }
.team__num-ico {
  flex: none; width: 44px; height: 44px; border-radius: 13px;
  display: grid; place-items: center;
  background: rgba(107,160,222,.16);
  border: 1px solid rgba(160,195,240,.16);
}
.team__num-ico svg { width: 20px; height: 20px; fill: none; stroke: #A9C7F2; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.team__num dt { font-size: clamp(1.7rem, 1.2rem + 1.4vw, 2.3rem); font-weight: 500; line-height: 1; letter-spacing: -.03em; color: #fff; }
.team__num dd { margin: .45rem 0 0; font-size: .68rem; font-weight: 500; letter-spacing: .14em; text-transform: uppercase; color: rgba(185,210,245,.75); }
@media (max-width: 720px) {
  .team__nums { grid-template-columns: 1fr; gap: .9rem; }
  .team__num { justify-content: flex-start; }
  .team__num + .team__num { border-left: 0; border-top: 1px solid rgba(160,195,240,.14); padding-top: .9rem; }
}

.team__foot { display: flex; justify-content: center; margin-top: clamp(1.5rem, 3vw, 2.25rem); }
.team__cta {
  display: inline-flex; align-items: center; gap: .75rem;
  padding: .95rem 2.1rem;
  border-radius: var(--pill);
  background: linear-gradient(180deg, #DCE8FF 0%, #A9C4F5 100%);
  color: #1B3A6B; font-weight: 600; text-decoration: none;
  box-shadow: 0 0 0 1px rgba(255,255,255,.35) inset, 0 12px 30px -10px rgba(107,160,222,.6);
  transition: translate .2s ease, box-shadow .2s ease, filter .2s ease;
}
.team__cta svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; transition: translate .2s ease; }
.team__cta:hover { translate: 0 -2px; filter: brightness(1.04); box-shadow: 0 0 0 1px rgba(255,255,255,.4) inset, 0 16px 36px -10px rgba(107,160,222,.75); }
.team__cta:hover svg { translate: 3px 0; }
.team__cta:focus-visible { outline: 2px solid #A9C4F5; outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  .team__row { animation: none; }
}

/* ---------- about page: who you'll be working with ----------
   The founder section's split, once per person, Carl first. Reversed rows need the
   400px portrait track on the right, or the copy is squeezed into it. */
.members__head { display: flex; flex-direction: column; align-items: center; text-align: center; margin-bottom: clamp(2rem, 4vw, 3rem); }
.members__head h2 { margin-top: .85rem; }
/* tight enough that the rows read as one list, with room for the name
   plates that hang below each picture */
.members__list { display: grid; gap: clamp(2.25rem, 4vw, 3.25rem); }
.member .stack--md { gap: 1.1rem; }
/* name as the headline, the line about them under it */
.member__names { display: grid; gap: .45rem; }
.member__sub {
  font-size: var(--step-2); font-weight: 600; line-height: 1.2; letter-spacing: -.02em;
  color: var(--deep-haus); opacity: .78;
}
@media (min-width: 810px) {
  .member.split--bio.split--reverse { grid-template-columns: minmax(0, 1fr) minmax(0, 400px); }
  .member.split--reverse .founder__frame { justify-self: end; }
}

/* ---------- about page: key figures ----------
   The home page's figure cards, in a single row of four under a centred
   heading. The home grid sets two rows to fill the map's height; here the
   row takes its natural height instead. */
.figures__head { display: flex; flex-direction: column; align-items: center; text-align: center; margin-bottom: clamp(1.75rem, 3.5vw, 2.5rem); }
.figures__head h2 { margin-top: .85rem; }
.figures .statcards { grid-template-columns: repeat(4, minmax(0, 1fr)); grid-template-rows: auto; }
@media (max-width: 900px) { .figures .statcards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ---------- about page: client stories ----------
   Heading left, a short line in the middle, the way on to the projects on
   the right. Under it two rows of cards drift in opposite directions and
   pause under the pointer: photographs with a place and a way through,
   quotes on white. The rows are offset a step so photo sits over quote
   and quote over photo. Edges fade through a mask. */
.reviews__head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 1.25rem clamp(1.5rem, 4vw, 4rem);
  align-items: end;
}
.reviews__title h2 { margin-top: 1rem; font-size: clamp(2rem, 1.3rem + 2vw, 2.9rem); text-wrap: nowrap; }
@media (max-width: 720px) { .reviews__title h2 { text-wrap: balance; } }
.reviews__title .hl { display: block; }
.rv-eyebrow {
  display: inline-flex; align-items: center; gap: .55rem;
  padding: .42rem .9rem .42rem .75rem;
  border-radius: var(--pill);
  background: #fff; border: 1px solid rgba(107,160,222,.18);
  box-shadow: 0 4px 14px -6px rgba(44,95,158,.25);
  font-size: .72rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--blue-ink);
}
.rv-eyebrow::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--blue-ink); }
.reviews__sub { max-width: 40ch; font-size: .9rem; line-height: 1.6; color: var(--muted); padding-bottom: .3rem; }
.rv-all {
  align-self: start;
  margin-top: 120px;
  display: inline-flex; align-items: center; gap: .6rem;
  padding: .8rem 1.25rem;
  border-radius: var(--pill);
  background: #fff; border: 1px solid rgba(22,35,38,.06);
  box-shadow: 0 8px 22px -12px rgba(22,35,38,.3);
  font-size: .88rem; font-weight: 600; color: var(--deep-haus); text-decoration: none; white-space: nowrap;
  transition: translate .2s ease, box-shadow .2s ease;
}
.rv-all svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; transition: translate .2s ease; }
.rv-all:hover { translate: 0 -2px; box-shadow: 0 12px 26px -12px rgba(22,35,38,.38); }
.rv-all:hover svg { translate: 3px 0; }
@media (max-width: 960px) {
  .reviews__head { grid-template-columns: 1fr; align-items: start; }
  .rv-all { justify-self: start; margin-top: 0; }
}

.rv-rows {
  --rv-h: 15.5rem;
  --rv-gap: clamp(.9rem, 1.4vw, 1.25rem);
  display: grid; gap: var(--rv-gap);
  margin-top: clamp(2rem, 4vw, 2.75rem);
  padding-block: .75rem 1.5rem;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
          mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
}
.rv-row { display: flex; width: max-content; animation: rv-left 90s linear infinite; }
.rv-row--rev { animation-name: rv-right; animation-duration: 100s; }
.rv-rows:hover .rv-row { animation-play-state: paused; }
@keyframes rv-left  { from { translate: 0 0; }    to { translate: -50% 0; } }
@keyframes rv-right { from { translate: -50% 0; } to { translate: 0 0; } }
.rv-set { display: flex; gap: var(--rv-gap); padding-right: var(--rv-gap); }

/* ---- photograph card ---- */
.rv-photo {
  position: relative; flex: none; margin: 0;
  width: clamp(18rem, 30vw, 26rem); height: var(--rv-h);
  border-radius: 18px; overflow: hidden;
  box-shadow: 0 18px 40px -22px rgba(22,35,38,.5);
}
.rv-photo img { width: 100%; height: 100%; object-fit: cover; transition: scale .6s cubic-bezier(.2,.7,.3,1); }
.rv-photo:hover img { scale: 1.04; }
.rv-photo::after {
  content: ""; position: absolute; inset: auto 0 0 0; height: 45%;
  background: linear-gradient(180deg, transparent, rgba(8,14,22,.55));
  pointer-events: none;
}
.rv-photo__tag {
  position: absolute; left: .9rem; top: .9rem; z-index: 1;
  padding: .3rem .65rem; border-radius: 7px;
  background: rgba(15,24,28,.72); color: #fff;
  font-size: .72rem; font-weight: 600;
}
.rv-photo__place {
  position: absolute; left: 1rem; bottom: 1rem; z-index: 1;
  display: inline-flex; align-items: center; gap: .45rem;
  font-size: .86rem; font-weight: 600; color: #fff;
  text-shadow: 0 1px 8px rgba(0,0,0,.4);
}
.rv-photo__place svg { width: 15px; height: 15px; fill: none; stroke: #fff; stroke-width: 1.8; }
.rv-photo__go {
  position: absolute; right: 1rem; bottom: .85rem; z-index: 1;
  width: 38px; height: 38px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(255,255,255,.22);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  border: 1px solid rgba(255,255,255,.4);
  transition: background .2s ease;
}
.rv-photo__go:hover { background: rgba(255,255,255,.4); }
.rv-photo__go svg { width: 16px; height: 16px; fill: none; stroke: #fff; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

/* ---- quote card ---- */
.rv-q {
  flex: none; margin: 0;
  width: clamp(18rem, 30vw, 26rem); height: var(--rv-h);
  display: flex; flex-direction: column;
  padding: 1.5rem 1.6rem;
  border-radius: 18px;
  background: rgba(255,255,255,.92);
  border: 1px solid rgba(22,35,38,.05);
  box-shadow: 0 18px 40px -26px rgba(44,95,158,.4);
}
.rv-q__head { display: flex; align-items: center; gap: .8rem; }
.rv-q__avatar {
  flex: none; width: 42px; height: 42px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(107,160,222,.16);
}
.rv-q__avatar svg { width: 18px; height: 18px; fill: none; stroke: var(--blue-ink); stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.rv-q__who { display: grid; gap: .12rem; min-width: 0; }
.rv-q__name {
  display: flex; align-items: center; gap: .45rem;
  font-size: .76rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--deep-haus);
}
.rv-q__needed {
  padding: .1rem .45rem; border-radius: var(--pill);
  background: rgba(107,160,222,.16); color: var(--blue-ink);
  font-size: .62rem; letter-spacing: .08em;
}
.rv-q__role { font-size: .8rem; color: var(--muted); }
.rv-q__mark { margin-left: auto; flex: none; align-self: flex-start; width: 20px; height: 20px; fill: var(--haus-blue); opacity: .7; }
.rv-q__text { margin: 1rem 0 0; font-size: .98rem; line-height: 1.6; color: var(--charcoal); }

@media (max-width: 620px) {
  .rv-rows { --rv-h: 17rem; }
  .rv-q__text { font-size: .9rem; }
}
@media (prefers-reduced-motion: reduce) {
  .rv-row { animation: none; }
  .rv-rows { overflow-x: auto; }
}

/* ---------- client portal page (/portal) ----------
   Copy on the left, the example ledger on the right, then what the portal
   holds as four cards. The buttons are the about page's. */
.pt-hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.02fr);
  gap: clamp(2rem, 4.5vw, 4.5rem);
  align-items: center;
  padding-top: clamp(6.25rem, 10vw, 8rem);
  padding-bottom: clamp(2.5rem, 5vw, 4rem);
}
.pt-hero__copy h1 { margin: 1rem 0 1.4rem; }
.pt-hero__copy .prose { max-width: 52ch; }
.pt-hero__actions { display: flex; flex-wrap: wrap; gap: .9rem; margin-top: 2rem; }
.pt-hero .ab-btn { --ab-grad: linear-gradient(135deg, #4A86F5 0%, #2F6BF0 50%, #2356D6 100%); }
.pt-hero .ab-btn--primary { background: var(--ab-grad); color: #fff; box-shadow: 0 16px 32px -12px rgba(47,107,240,.6); }
.pt-hero .ab-btn > svg:first-child, .pt-cta .ab-btn > svg:first-child {
  width: 17px; height: 17px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}
.pt-hero__note { margin-top: 1.1rem; font-size: .9rem; color: var(--muted); }
.pt-hero__note a { color: var(--blue-ink); }
@media (max-width: 960px) { .pt-hero { grid-template-columns: 1fr; } }

.pt-cards {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(1rem, 2vw, 1.5rem);
  margin-top: clamp(2rem, 4vw, 3rem);
  list-style: none; padding: 0;
}
.pt-card {
  display: grid; align-content: start; gap: .7rem;
  padding: clamp(1.1rem, 2vw, 1.5rem);
  background: var(--soft-white);
  border: 1px solid var(--border-gray); border-radius: 14px;
  box-shadow: 0 10px 26px -20px rgba(22,35,38,.5);
}
/* the trio band's duotone tile, as the card mark */
.pt-card__ico {
  width: 46px; height: 46px; border-radius: 13px;
  display: grid; place-items: center;
  background: linear-gradient(155deg, rgba(107,160,222,.26) 0%, rgba(107,160,222,.10) 100%);
  border: 1px solid rgba(107,160,222,.34);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.5);
}
.pt-card__ico svg {
  width: 25px; height: 25px;
  fill: none; stroke: var(--blue-ink); stroke-width: 1.45;
  stroke-linecap: round; stroke-linejoin: round;
}
.pt-card__ico .t-f { fill: rgba(107,160,222,.30); stroke: none; }
.pt-card__ico .t-s { stroke: #1C4577; stroke-width: 1.6; }
.pt-card__ico .t-a { stroke: rgba(44,95,158,.55); stroke-width: 1.15; }
.pt-card__ico .t-seal { fill: var(--haus-blue); stroke: var(--soft-white); stroke-width: 1.3; }
.pt-card__ico .t-tick { stroke: #fff; stroke-width: 1.9; }
.pt-card__title { font-size: 1.05rem; font-weight: 700; letter-spacing: -.02em; color: var(--deep-haus); }
.pt-card__text { font-size: .9rem; line-height: 1.55; color: var(--muted); }
@media (max-width: 900px) { .pt-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .pt-cards { grid-template-columns: 1fr; } }

.pt-cta {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1.5rem;
  padding: clamp(1.5rem, 3vw, 2.25rem);
  background: var(--soft-white);
  border: 1px solid var(--border-gray); border-radius: 18px;
}
.pt-cta p { margin-top: .6rem; max-width: 60ch; }
.pt-cta a[href^="tel"] { color: var(--blue-ink); }
.pt-cta .ab-btn--primary {
  background: linear-gradient(135deg, #4A86F5 0%, #2F6BF0 50%, #2356D6 100%); color: #fff;
  box-shadow: 0 16px 32px -12px rgba(47,107,240,.5);
}

/* ---- the sign-in dialog ---- */
.login { width: min(32rem, calc(100vw - 2rem)); padding: 0; border: 0; background: none; }
.login::backdrop { background: rgba(10,18,20,.72); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px); }
.login__card {
  position: relative;
  padding: clamp(1.75rem, 4vw, 2.5rem);
  background: #fff; border-radius: 20px;
  box-shadow: 0 40px 80px -30px rgba(10,18,20,.55);
  text-align: center;
}
.login__close {
  position: absolute; right: 1rem; top: 1rem;
  width: 36px; height: 36px; display: grid; place-items: center;
  border: 0; border-radius: 50%; background: rgba(22,35,38,.06); cursor: pointer;
}
.login__close:hover { background: rgba(22,35,38,.12); }
.login__close svg { width: 16px; height: 16px; fill: none; stroke: var(--deep-haus); stroke-width: 1.8; stroke-linecap: round; }
.login__ico {
  display: grid; place-items: center; width: 54px; height: 54px; margin: 0 auto 1rem;
  border-radius: 16px;
  background: linear-gradient(155deg, rgba(107,160,222,.26), rgba(107,160,222,.10));
  border: 1px solid rgba(107,160,222,.34);
}
.login__ico svg { width: 24px; height: 24px; fill: none; stroke: var(--blue-ink); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.login__title { font-size: 1.5rem; font-weight: 700; letter-spacing: -.02em; color: var(--deep-haus); }
.login__text { margin: .8rem auto 1.5rem; max-width: 42ch; font-size: .95rem; line-height: 1.6; color: var(--muted); }
.login__go {
  background: linear-gradient(135deg, #4A86F5 0%, #2F6BF0 50%, #2356D6 100%); color: #fff;
  box-shadow: 0 16px 32px -12px rgba(47,107,240,.5);
}
.login__aside { margin-top: 1.1rem; font-size: .88rem; color: var(--muted); }
.login__aside a { color: var(--blue-ink); }

/* ---------- motion ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
  }
}

/* =================================================================
   SERVICES PAGES (/services/, /services/*, /how-we-work/)
   Everything below is new for the service set and is built from the
   homepage vocabulary only: .eyebrow, .h-lg + one .hl, hairlines,
   mono for data, the floating-picture ramp. None of the cf- or ab-
   dialects. Components are listed in page order.
   ================================================================= */

/* the reset only clears lists and paragraphs; quotes and figures on these
   pages sit in their own layout */
.pg blockquote, .pg figure { margin: 0; }

/* service pages anchor to their own section ids */
.sv-anchor { scroll-margin-top: clamp(80px, 9vw, 104px); }

/* ---------- breadcrumb ----------
   Sits in the eyebrow's slot: both are "where am I" labels, and a pill
   stacked on a pill would be heavy. Mono, because it is a path. */
.crumbs ol { display: flex; flex-wrap: wrap; align-items: center; gap: .2rem .55rem; }
.crumbs li {
  display: inline-flex; align-items: center; gap: .55rem;
  font-family: var(--sans); font-weight: 600; font-size: .72rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--taupe);
}
.crumbs li + li::before { content: "\00B7"; color: var(--border-gray); }
.crumbs a { text-decoration: none; color: inherit; transition: color .2s ease; }
.crumbs a:hover { color: var(--blue-ink); text-decoration: underline; text-underline-offset: 3px; }
.crumbs [aria-current] { color: var(--deep-haus); }

/* ---------- service hero ----------
   The about hero's shape (copy left, one photograph right) painted in
   core tokens. Light page, so the header ships solid. */
.svh { padding-top: clamp(6.6rem, 11vw, 8.6rem); padding-bottom: clamp(2.2rem, 4.7vw, 4rem); }
.svh__grid {
  display: grid; gap: clamp(2rem, 5vw, 4.5rem);
  grid-template-columns: minmax(0, 1.12fr) minmax(0, 1fr);
  align-items: center;
}
.svh__copy { display: grid; gap: 1.15rem; align-content: center; }
.svh__title { font-size: clamp(2.3rem, 1.5rem + 3vw, 4.1rem); color: var(--deep-haus); }
.svh__copy .prose { max-width: 54ch; }
.svh__copy .btn-row { margin-top: .5rem; }
.svh__facts { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .35rem; }
.svh__fact {
  display: inline-flex; align-items: center; gap: .55rem;
  padding: .38rem .8rem .38rem .45rem; border-radius: var(--pill);
  background: var(--soft-white); border: 1px solid var(--border-gray);
  font-size: .82rem; font-weight: 600; color: var(--deep-haus);
}
.svh__fact .svc__ico { width: 26px; height: 26px; border-radius: 50%; }
.svh__fact .svc__ico svg { width: 15px; height: 15px; }
.svh__fact b { font-family: var(--sans); font-weight: 600; font-weight: 500; font-size: .76rem; color: #8A7538; }
.svh__media { position: relative; aspect-ratio: 4 / 4.4; }
.svh__cap {
  position: absolute; left: .85rem; bottom: .85rem; right: .85rem;
  display: flex; align-items: center; gap: .55rem; width: fit-content; max-width: calc(100% - 1.7rem);
  padding: .45rem .85rem; border-radius: var(--pill);
  background: rgba(16,25,28,.46);
  -webkit-backdrop-filter: blur(10px) saturate(140%); backdrop-filter: blur(10px) saturate(140%);
  border: 1px solid rgba(255,255,255,.18);
  font-family: var(--sans); font-weight: 600; font-size: .7rem; letter-spacing: .08em; text-transform: uppercase; color: #fff;
}
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .svh__cap { background: rgba(16,25,28,.78); }
}
.svh--text .svh__grid { grid-template-columns: minmax(0, 1fr); }
.svh--text .svh__copy { max-width: 60rem; }
@media (max-width: 860px) {
  .svh__grid { grid-template-columns: 1fr; }
  .svh__media { aspect-ratio: 4 / 3; }
}

/* ---------- page body with the side index ----------
   From 1100px the index is a sticky left column. Below that it is a
   row of pills under the hero that scrolls sideways. The links are
   plain anchors, so it works with no script at all; the script only
   marks the section in view. */
.pg { display: block; }
.pgnav { margin-bottom: clamp(1rem, 2.4vw, 1.8rem); }
.pgnav__label { display: none; }
.pgnav ol {
  display: flex; gap: .5rem; overflow-x: auto; padding: .15rem .1rem .6rem;
  scrollbar-width: thin; -webkit-overflow-scrolling: touch;
}
.pgnav a {
  display: inline-flex; align-items: center; gap: .5rem; white-space: nowrap;
  min-height: 40px; padding: .4rem .95rem; border-radius: var(--pill);
  background: #fff; border: 1px solid var(--border-gray);
  font-size: .84rem; font-weight: 600; color: var(--deep-haus); text-decoration: none;
  transition: border-color .22s ease, background-color .22s ease, color .22s ease;
}
.pgnav a span { font-family: var(--sans); font-weight: 600; font-size: .68rem; font-weight: 500; color: var(--taupe); }
.pgnav a:hover, .pgnav a[aria-current] { border-color: var(--haus-blue); background: rgba(107,160,222,.08); }
@media (min-width: 1100px) {
  .pg { display: grid; grid-template-columns: 196px minmax(0, 1fr); gap: clamp(2rem, 4vw, 4rem); align-items: start; }
  .pgnav { position: sticky; top: clamp(96px, 9vw, 116px); margin: clamp(2.2rem, 4.7vw, 4rem) 0 0; }
  .pgnav__label {
    display: block; margin-bottom: .9rem;
    font-family: var(--sans); font-weight: 600; font-size: .66rem; letter-spacing: .13em; text-transform: uppercase; color: var(--taupe);
  }
  .pgnav ol { display: grid; gap: 0; overflow: visible; padding: 0; border-left: 1px solid var(--border-gray); }
  .pgnav a {
    position: relative; min-height: 0; border: 0; border-radius: 0; background: none;
    padding: .42rem 0 .42rem 1rem; font-size: .84rem; font-weight: 500; color: var(--muted); white-space: normal;
  }
  .pgnav a::before {
    content: ""; position: absolute; left: -1px; top: .35rem; bottom: .35rem; width: 2px;
    background: var(--haus-blue); scale: 1 0; transition: scale .3s cubic-bezier(.2,.7,.3,1);
  }
  .pgnav a:hover { background: none; color: var(--deep-haus); }
  .pgnav a[aria-current] { background: none; color: var(--deep-haus); font-weight: 600; }
  .pgnav a[aria-current]::before { scale: 1 1; }
}
.pg__body > .section:first-child { padding-top: clamp(1rem, 2vw, 1.6rem); }
@media (min-width: 1100px) { .pg__body > .section:first-child { padding-top: clamp(2.2rem, 4.7vw, 4rem); } }

/* ---------- who it's for ----------
   Named readers, hairlines between, no cards. */
.who { display: grid; border-top: 1px solid var(--border-gray); }
.who__row {
  display: grid; grid-template-columns: auto minmax(0, 15rem) minmax(0, 1fr);
  gap: .6rem 1.4rem; align-items: start;
  padding: 1.15rem 0; border-bottom: 1px solid var(--border-gray);
}
.who__row .svc__ico { width: 38px; height: 38px; border-radius: 11px; }
.who__row .svc__ico svg { width: 22px; height: 22px; }
.who__name { font-weight: 700; color: var(--deep-haus); letter-spacing: -.015em; padding-top: .45rem; }
.who__text { color: var(--muted); padding-top: .45rem; max-width: 58ch; }
@media (max-width: 700px) {
  .who__row { grid-template-columns: auto minmax(0, 1fr); }
  .who__text { grid-column: 2; padding-top: 0; }
}

/* ---------- what's included ----------
   A two-column definition list with hairlines, seven rows at most. */
.incl { display: grid; border-top: 1px solid var(--border-gray); }
.incl__row {
  display: grid; grid-template-columns: minmax(0, 17rem) minmax(0, 1fr);
  gap: .4rem 2rem; padding: 1.05rem 0; border-bottom: 1px solid var(--border-gray);
}
.incl__row dt { font-weight: 700; color: var(--deep-haus); letter-spacing: -.015em; }
.incl__row dd { margin: 0; color: var(--muted); max-width: 60ch; }
@media (max-width: 700px) { .incl__row { grid-template-columns: 1fr; } }

/* bullets are reset globally, so lists inside copy need their own mark:
   the champagne bar the process card uses beside its outcome line */
.checks { display: grid; gap: .6rem; }
.checks li { position: relative; padding-left: 1.15rem; color: var(--charcoal); }
.checks li::before {
  content: ""; position: absolute; left: 0; top: .32em;
  width: 2px; height: 1.05em; border-radius: 2px; background: var(--champagne);
}

/* ---------- island rules that apply ----------
   The GOV.UK step-by-step pattern in this site's hand: numbered steps
   on a vertical rail, each a disclosure with the rule, who checks it
   and the document it leaves you. No script: every step open. */
.rules__bar { display: flex; justify-content: flex-end; margin-bottom: .5rem; }
.rules__all {
  background: none; border: 0; cursor: pointer; padding: .4rem 0;
  font-size: .86rem; font-weight: 600; color: var(--blue-ink);
  text-decoration: underline; text-underline-offset: 3px;
}
.rules { position: relative; display: grid; }
.rules::before {
  content: ""; position: absolute; left: 17px; top: 1.4rem; bottom: 1.4rem;
  width: 1px; background: var(--border-gray);
}
.rule-step { position: relative; }
.rule-step h3 { font-size: inherit; letter-spacing: normal; line-height: inherit; font-weight: inherit; }
.rule-step__q {
  display: grid; grid-template-columns: 35px minmax(0, 1fr) auto auto; align-items: center; gap: 1rem;
  width: 100%; padding: .9rem 0; background: none; border: 0; cursor: pointer; text-align: left;
}
.rule-step__n {
  position: relative; z-index: 1;
  display: grid; place-items: center; width: 35px; height: 35px; border-radius: 50%;
  background: #fff; border: 1px solid var(--border-gray);
  font-family: var(--sans); font-weight: 600; font-size: .74rem; color: var(--deep-haus);
  transition: background-color .25s ease, border-color .25s ease, color .25s ease;
}
.rule-step__t { font-size: 1.06rem; font-weight: 600; letter-spacing: -.015em; color: var(--deep-haus); }
.rule-step__doc {
  font-family: var(--sans); font-weight: 600; font-size: .72rem; letter-spacing: .06em; color: #8A7538; white-space: nowrap;
}
.rule-step__q .faq__icon { width: 16px; height: 16px; }
.rule-step__q[aria-expanded="true"] .faq__icon::after { scale: 1 0; }
.rule-step__q[aria-expanded="true"] .rule-step__n,
.rule-step__q:hover .rule-step__n { background: var(--deep-haus); border-color: var(--deep-haus); color: #fff; }
.rule-step__a { padding: 0 0 1.25rem calc(35px + 1rem); }
.rule-step__a dl { display: grid; gap: .55rem; max-width: 62ch; }
.rule-step__a div { display: grid; grid-template-columns: 8.5rem minmax(0, 1fr); gap: 1rem; }
.rule-step__a dt {
  font-family: var(--sans); font-weight: 600; font-size: .68rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--taupe); padding-top: .3rem;
}
.rule-step__a dd { margin: 0; color: var(--charcoal); }
.rule-step__src { margin-top: .75rem; font-size: .8rem; color: var(--muted); }
.rule-step__src a { color: var(--blue-ink); }
@media (max-width: 620px) {
  .rule-step__doc { display: none; }
  .rule-step__q { grid-template-columns: 35px minmax(0, 1fr) auto; }
  .rule-step__a { padding-left: 0; }
  /* the rail would run through the panel once it loses its indent */
  .rules::before { display: none; }
  .rule-step__a div { grid-template-columns: 1fr; gap: .15rem; }
}

/* ---------- projects of this type ----------
   Rows, not cards: an archive, not a shop window. */
.strip { display: grid; border-top: 1px solid var(--border-gray); }
.strip__row {
  display: grid; grid-template-columns: 76px minmax(0, 1fr) auto; gap: 1.2rem; align-items: center;
  padding: .9rem .6rem .9rem 0; border-bottom: 1px solid var(--border-gray);
  text-decoration: none; color: inherit;
  transition: background-color .25s ease;
}
a.strip__row:hover { background: rgba(107,160,222,.06); }
.strip__img {
  width: 76px; height: 76px; border-radius: 10px; object-fit: cover;
  border: 2px solid #fff;
  box-shadow: 0 0 0 1px rgba(207,202,193,.9), 1px 2px 0 rgba(22,35,38,.12), 4px 7px 10px -5px rgba(22,35,38,.28);
}
.strip__name { display: block; font-size: 1.08rem; font-weight: 700; letter-spacing: -.015em; color: var(--deep-haus); }
.strip__why { display: block; margin-top: .2rem; color: var(--muted); font-size: .95rem; }
.strip__meta {
  display: grid; justify-items: end; gap: .3rem; text-align: right;
  font-family: var(--sans); font-weight: 600; font-size: .72rem; letter-spacing: .06em; color: #8A7538; white-space: nowrap;
}
.strip__kind {
  font-family: var(--sans); font-size: .7rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--blue-ink); padding: .2rem .6rem; border-radius: var(--pill); background: rgba(44,95,158,.08);
}
@media (max-width: 620px) {
  .strip__row { grid-template-columns: 60px minmax(0, 1fr); }
  .strip__img { width: 60px; height: 60px; }
  .strip__meta { grid-column: 2; justify-items: start; text-align: left; }
}

/* ---------- materials: raw beside finished ---------- */
.pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(.9rem, 2vw, 1.4rem); }
.pair figure { margin: 0; display: grid; gap: .6rem; }
.pair .media { aspect-ratio: 4 / 5; }
.pair figcaption {
  font-family: var(--sans); font-weight: 600; font-size: .7rem; letter-spacing: .1em; text-transform: uppercase; color: var(--taupe);
}

/* ---------- cost & timeline ---------- */
.svfigs .statcards { grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-rows: auto; }
@media (max-width: 860px) { .svfigs .statcards { grid-template-columns: 1fr; } }
.svcost { display: grid; grid-template-columns: minmax(0, .95fr) minmax(0, 1.05fr); gap: clamp(1.6rem, 4vw, 3.5rem); align-items: center; }
@media (max-width: 960px) { .svcost { grid-template-columns: 1fr; } }

/* ---------- one testimonial, no carousel ---------- */
.svq { margin: 0; display: grid; gap: 1.2rem; max-width: 52rem; }
.svq__mark { width: 34px; height: 34px; fill: var(--champagne); }
.svq__text { font-size: var(--step-2); font-weight: 600; letter-spacing: -.02em; line-height: 1.28; color: var(--deep-haus); text-wrap: pretty; }
.svq__who { font-family: var(--sans); font-weight: 600; font-size: .74rem; letter-spacing: .1em; text-transform: uppercase; color: var(--taupe); }

/* ---------- other services ----------
   The homepage accordion row, as a link. The current page is left out,
   so four remain. */
.svlinks { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); gap: .75rem; }
.svlink {
  display: grid; grid-template-rows: auto 1fr auto; gap: .7rem; min-height: 100%;
  padding: 1.1rem 1.1rem 1rem; border-radius: 14px;
  background: var(--soft-white); border: 1px solid var(--border-gray);
  text-decoration: none; color: inherit;
  transition: background-color .25s ease, border-color .25s ease, translate .32s cubic-bezier(.2,.7,.3,1);
}
.svlink:hover { background: rgba(107,160,222,.08); border-color: var(--haus-blue); translate: 0 -2px; }
.svlink__name { font-size: 1.05rem; font-weight: 700; letter-spacing: -.015em; color: var(--deep-haus); }
.svlink__text { font-size: .9rem; color: var(--muted); }
.svlink .link-arrow { font-size: .85rem; }

/* ---------- the house section drawing (Custom Homes, Storm) ----------
   One line drawing, two pages. Parts carry data-part; each page's list
   says which parts a callout lights. Everything is at equal weight until
   a callout is chosen, so with no script it reads as a plain diagram. */
.sect { display: grid; grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr); gap: clamp(1.5rem, 3.5vw, 3rem); align-items: start; }
.sect__fig {
  position: sticky; top: clamp(96px, 9vw, 116px);
  margin: 0; padding: clamp(.6rem, 1.4vw, 1rem); border-radius: 20px; background: #fff;
  border: 1px solid var(--border-gray);
  box-shadow:
    2px 3px 0 0 rgba(22,35,38,.06), 10px 14px 13px -5px rgba(22,35,38,.10), 38px 54px 74px -24px rgba(22,35,38,.16);
}
.sect__svg { width: 100%; height: auto; }
.sect__svg .ln { fill: none; stroke: var(--deep-haus); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.sect__svg .ln-f { fill: none; stroke: rgba(22,35,38,.45); stroke-width: 1; stroke-linecap: round; }
.sect__svg .ms { fill: rgba(107,160,222,.14); stroke: none; }
.sect__svg .sand { fill: #F3EEE3; stroke: none; }
.sect__svg .hatch { stroke: rgba(166,159,148,.55); stroke-width: 1; }
.sect__svg .flood { stroke: #B89E5E; stroke-width: 1.6; stroke-dasharray: 7 6; fill: none; }
.sect__svg .band { fill: rgba(200,181,138,.16); }
.sect__svg .lbl { font-family: var(--sans); font-weight: 600; font-size: 11px; letter-spacing: .08em; fill: #7A6838; }
.sect__svg .pin circle { fill: #fff; stroke: var(--deep-haus); stroke-width: 1.4; }
.sect__svg .pin text { font-family: var(--sans); font-weight: 600; font-size: 11px; font-weight: 500; fill: var(--deep-haus); text-anchor: middle; dominant-baseline: central; }
.sect__svg [data-part] { transition: opacity .3s ease; }
.sect__svg [data-part] .ln, .sect__svg [data-part] .ln-f { transition: stroke .3s ease, stroke-width .3s ease; }
.sect__svg .pin circle, .sect__svg .pin text { transition: fill .3s ease, stroke .3s ease; }
/* a callout is chosen: its parts go blue, the rest step back */
.sect.is-picking .sect__svg [data-part] { opacity: .32; }
.sect.is-picking .sect__svg [data-part].is-lit { opacity: 1; }
.sect__svg [data-part].is-lit .ln { stroke: var(--blue-ink); stroke-width: 2.2; }
.sect__svg [data-part].is-lit .ln-f { stroke: var(--haus-blue); }
.sect__svg [data-part].is-lit .ms { fill: rgba(107,160,222,.3); }
.sect__svg .pin.is-lit circle { fill: var(--deep-haus); stroke: var(--deep-haus); }
.sect__svg .pin.is-lit text { fill: #fff; }

.sect__list { display: grid; border-top: 1px solid var(--border-gray); counter-reset: none; }
.sect__item { border-bottom: 1px solid var(--border-gray); }
.sect__btn {
  display: grid; grid-template-columns: 30px minmax(0, 1fr); gap: .9rem; align-items: start;
  width: 100%; padding: .85rem .5rem .85rem 0; background: none; border: 0; cursor: pointer; text-align: left;
  border-radius: 10px; transition: background-color .25s ease;
}
.sect__n {
  display: grid; place-items: center; width: 30px; height: 30px; border-radius: 50%;
  border: 1px solid var(--border-gray); background: #fff;
  font-family: var(--sans); font-weight: 600; font-size: .72rem; color: var(--deep-haus);
  transition: background-color .25s ease, border-color .25s ease, color .25s ease;
}
.sect__t { display: block; font-weight: 700; letter-spacing: -.015em; color: var(--deep-haus); padding-top: .2rem; }
.sect__d { display: block; margin-top: .3rem; font-size: .92rem; color: var(--muted); }
.sect__btn:hover { background: rgba(107,160,222,.06); }
.sect__btn[aria-pressed="true"] { background: rgba(107,160,222,.08); }
.sect__btn[aria-pressed="true"] .sect__n { background: var(--deep-haus); border-color: var(--deep-haus); color: #fff; }
.sect__note { margin-top: .9rem; font-size: .8rem; color: var(--muted); }
@media (max-width: 960px) { .sect { grid-template-columns: 1fr; } .sect__fig { position: static; } }
@media (prefers-reduced-motion: reduce) {
  .sect.is-picking .sect__svg [data-part] { opacity: 1; }
}

/* ---------- before / after (Restoration) ----------
   Two photographs of the same elevation. The range input is the real
   control, stretched invisibly over the figure; the handle is drawn. */
.ba { position: relative; margin: 0; overflow: hidden; aspect-ratio: 3 / 2; --x: 50%; touch-action: pan-y; }
.ba img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.ba__after { clip-path: inset(0 0 0 var(--x)); }
.ba__handle {
  position: absolute; top: 0; bottom: 0; left: var(--x); width: 2px; translate: -1px 0;
  background: #fff; box-shadow: 0 0 0 1px rgba(22,35,38,.18); pointer-events: none;
}
.ba__handle::after {
  content: "\2194"; position: absolute; top: 50%; left: 50%; translate: -50% -50%;
  display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%;
  background: #fff; color: var(--deep-haus); font-size: 1.1rem;
  box-shadow: 0 0 0 1px rgba(207,202,193,.9), 4px 6px 12px -4px rgba(22,35,38,.35);
}
.ba__range {
  position: absolute; inset: 0; width: 100%; height: 100%; margin: 0;
  opacity: 0; cursor: ew-resize; -webkit-appearance: none; appearance: none;
}
.ba__range:focus-visible + .ba__handle::after { outline: 2px solid var(--haus-blue); outline-offset: 3px; }
.ba__tag {
  position: absolute; top: .85rem; padding: .3rem .75rem; border-radius: var(--pill);
  background: rgba(16,25,28,.5); color: #fff; pointer-events: none;
  font-family: var(--sans); font-weight: 600; font-size: .68rem; letter-spacing: .12em; text-transform: uppercase;
}
.ba__tag--l { left: .85rem; }
.ba__tag--r { right: .85rem; }
.ba-cap { margin-top: .8rem; font-family: var(--sans); font-weight: 600; font-size: .72rem; letter-spacing: .08em; color: var(--taupe); }
/* no script: both pictures, side by side */
.ba:not(.is-live) { aspect-ratio: auto; display: grid; grid-template-columns: 1fr 1fr; gap: 2px; }
.ba:not(.is-live) img { position: static; aspect-ratio: 3 / 2; }
.ba:not(.is-live) .ba__after { clip-path: none; }
.ba:not(.is-live) .ba__handle, .ba:not(.is-live) .ba__range { display: none; }

/* ---------- kept / replaced register ---------- */
.keep { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 clamp(1.5rem, 4vw, 3.5rem); }
.keep__col h3 {
  font-size: 1.05rem; padding-bottom: .75rem; margin-bottom: .2rem; border-bottom: 1px solid var(--deep-haus);
  display: flex; align-items: center; gap: .6rem;
}
.keep__col h3 span { font-family: var(--sans); font-weight: 600; font-size: .68rem; font-weight: 500; letter-spacing: .12em; text-transform: uppercase; color: var(--taupe); }
.keep__col li { padding: .75rem 0; border-bottom: 1px solid var(--border-gray); color: var(--charcoal); }
.keep__col li > b { display: block; font-weight: 700; color: var(--deep-haus); letter-spacing: -.01em; }
.keep__col li > span { display: block; margin-top: .15rem; font-size: .92rem; color: var(--muted); }
@media (max-width: 700px) { .keep { grid-template-columns: 1fr; gap: 2rem; } }

/* ---------- the found list (Remodels) ----------
   The open book applied to the one thing remodel clients fear. Rows
   arrive as they scroll in; the markup already holds every figure. */
.found { border-radius: 20px; background: #fff; border: 1px solid var(--border-gray); overflow: hidden;
  box-shadow: 2px 3px 0 0 rgba(22,35,38,.06), 10px 14px 13px -5px rgba(22,35,38,.10), 38px 54px 74px -24px rgba(22,35,38,.16); }
.found__head {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .6rem 1.5rem;
  padding: 1.1rem 1.3rem; border-bottom: 1px solid rgba(207,202,193,.5); background: var(--soft-white);
}
.found__id b { display: block; font-weight: 700; color: var(--deep-haus); letter-spacing: -.015em; }
.found__id span { font-family: var(--sans); font-weight: 600; font-size: .7rem; letter-spacing: .1em; text-transform: uppercase; color: var(--taupe); }
.found__tot { text-align: right; }
.found__tot span { display: block; font-family: var(--sans); font-weight: 600; font-size: .66rem; letter-spacing: .12em; text-transform: uppercase; color: var(--taupe); }
.found__tot b { font-size: 1.5rem; font-weight: 700; letter-spacing: -.03em; font-variant-numeric: tabular-nums; color: var(--deep-haus); }
.found__table { width: 100%; border-collapse: collapse; }
.found__table th {
  text-align: left; padding: .7rem 1.3rem; font-family: var(--sans); font-weight: 600; font-size: .66rem; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase; color: var(--taupe); border-bottom: 1px solid rgba(207,202,193,.5);
}
.found__table td { padding: .95rem 1.3rem; vertical-align: top; border-bottom: 1px solid rgba(207,202,193,.5); font-size: .93rem; }
.found__table tr:last-child td { border-bottom: 0; }
.found__table td:first-child { color: var(--deep-haus); font-weight: 600; }
.found__table td:nth-child(2) { color: var(--muted); }
.found__table .found__amt { text-align: right; font-family: var(--sans); font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--deep-haus); }
.found__table .found__day { display: block; margin-top: .2rem; font-family: var(--sans); font-weight: 600; font-size: .68rem; font-weight: 400; letter-spacing: .08em; color: #8A7538; }
.found__foot { padding: .9rem 1.3rem; font-size: .82rem; color: var(--muted); background: var(--soft-white); border-top: 1px solid rgba(207,202,193,.5); }
.found.is-live tbody tr { opacity: 0; translate: 0 10px; }
.found.is-live tbody tr.is-in { opacity: 1; translate: none; transition: opacity .5s ease, translate .52s cubic-bezier(.22,.75,.3,1); }
@media (max-width: 700px) {
  .found__table thead { display: none; }
  .found__table tr { display: grid; grid-template-columns: 1fr auto; padding: .9rem 1.1rem; border-bottom: 1px solid rgba(207,202,193,.5); }
  .found__table td { padding: 0; border: 0; }
  .found__table td:nth-child(2) { grid-column: 1 / -1; order: 3; margin-top: .35rem; }
}
@media (prefers-reduced-motion: reduce) {
  .found.is-live tbody tr { opacity: 1; translate: none; }
}

/* ---------- follow a dollar (How We Work) ----------
   One bar, every segment named, the fee in champagne and stated. */
.dollar { display: grid; gap: 1.1rem; }
.dollar__bar {
  display: flex; height: clamp(56px, 7vw, 76px); border-radius: 14px; overflow: hidden; gap: 3px;
  background: var(--warm-stone);
}
.dollar__seg {
  flex: 0 0 var(--w); display: flex; align-items: flex-end; padding: .55rem .65rem;
  background: linear-gradient(160deg, #4176BE, var(--blue-ink)); color: #fff;
  font-family: var(--sans); font-weight: 600; font-size: .72rem; font-variant-numeric: tabular-nums; min-width: 0; overflow: hidden;
}
.dollar__seg:nth-child(2) { background: linear-gradient(160deg, #6BA0DE, #4176BE); color: var(--deep-haus); }
.dollar__seg:nth-child(3) { background: linear-gradient(160deg, #9DC0EA, #6BA0DE); color: var(--deep-haus); }
.dollar__seg:nth-child(4) { background: linear-gradient(160deg, #C9DBF1, #9DC0EA); color: var(--deep-haus); }
.dollar__seg--fee { background: linear-gradient(160deg, #D9C9A2, var(--champagne)) !important; color: var(--deep-haus) !important; }
.dollar.is-live .dollar__seg { flex-basis: 0; }
.dollar.is-live.is-in .dollar__seg { flex-basis: var(--w); transition: flex-basis .95s cubic-bezier(.22,.75,.3,1); transition-delay: var(--d, 0s); }
.dollar__key { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 170px), 1fr)); gap: .5rem 1.2rem; }
.dollar__key div { display: grid; grid-template-columns: 12px minmax(0, 1fr) auto; gap: .6rem; align-items: baseline; padding: .55rem 0; border-bottom: 1px solid var(--border-gray); }
.dollar__key i { width: 10px; height: 10px; border-radius: 3px; background: var(--c); translate: 0 1px; }
.dollar__key dt { font-weight: 600; color: var(--deep-haus); font-size: .93rem; }
.dollar__key dd { margin: 0; font-family: var(--sans); font-weight: 600; font-size: .82rem; color: #8A7538; font-variant-numeric: tabular-nums; }
.dollar__formula {
  display: flex; flex-wrap: wrap; align-items: center; gap: .6rem .9rem;
  font-family: var(--sans); font-weight: 600; font-size: clamp(.95rem, .8rem + .6vw, 1.3rem); color: var(--deep-haus);
}
.dollar__formula span { padding: .45rem .9rem; border-radius: 10px; background: var(--soft-white); border: 1px solid var(--border-gray); }
.dollar__formula .is-fee { background: rgba(200,181,138,.2); border-color: rgba(200,181,138,.7); }
.dollar__formula em { font-style: normal; color: var(--taupe); }
@media (max-width: 640px) { .dollar__seg { font-size: 0; padding: 0; } }
@media (prefers-reduced-motion: reduce) { .dollar.is-live .dollar__seg { flex-basis: var(--w); } }

/* ---------- is / is not register (How We Work) reuses .keep ---------- */

/* ---------- chapter number for the long read ---------- */
.chap { font-family: var(--sans); font-weight: 600; font-size: .72rem; letter-spacing: .13em; text-transform: uppercase; color: var(--taupe); }

/* ---------- same job, two contracts ---------- */
.versus { width: 100%; border-collapse: collapse; font-size: .95rem; }
.versus th, .versus td { padding: .9rem 1rem; text-align: left; vertical-align: top; border-bottom: 1px solid var(--border-gray); }
.versus thead th { font-family: var(--sans); font-weight: 600; font-size: .68rem; font-weight: 500; letter-spacing: .12em; text-transform: uppercase; color: var(--taupe); }
.versus tbody th { font-weight: 700; color: var(--deep-haus); width: 26%; }
.versus td { color: var(--muted); }
.versus td.is-us { color: var(--deep-haus); background: rgba(107,160,222,.06); }
.versus-wrap { overflow-x: auto; }

/* ---------- hub: the pinned index ----------
   The homepage accordion turned from click to scroll. The picture
   column sticks; the row crossing the middle of the screen owns it. */
.hub { display: grid; grid-template-columns: minmax(0, .92fr) minmax(0, 1.08fr); gap: clamp(2rem, 5vw, 5rem); align-items: start; }
.hub__pics { position: sticky; top: clamp(96px, 10vw, 124px); aspect-ratio: 4 / 4.6; }
.hub__pic { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity .45s ease; }
.hub__pic.is-on { opacity: 1; }
.hub__list { display: grid; }
.hub__row {
  display: grid; gap: 1rem; padding: clamp(2rem, 4.5vw, 3.4rem) 0;
  border-top: 1px solid var(--border-gray); min-height: min(58vh, 620px); align-content: center;
}
.hub__row:first-child { border-top: 0; padding-top: 0; align-content: start; }
.hub__top { display: flex; align-items: center; gap: .9rem; }
.hub__n { font-family: var(--sans); font-weight: 600; font-size: .74rem; letter-spacing: .1em; color: var(--taupe); }
.hub__row h2 { font-size: var(--step-2); color: var(--deep-haus); transition: color .3s ease; }
.hub__stance { font-size: var(--step-1); line-height: 1.45; color: var(--deep-haus); font-weight: 500; letter-spacing: -.01em; max-width: 40ch; }
.hub__row .prose { max-width: 54ch; }
.hub__row .checks { margin-top: .2rem; }
.hub__img { display: none; }
.hub__row--model .hub__stance { color: var(--blue-ink); }

/* each row is a card that opens its page: the row's own link is
   stretched over the card, so keyboard and SEO keep one real anchor */
.hub__list { gap: clamp(1.1rem, 2vw, 1.6rem); }
.hub__row, .hub__row:first-child {
  position: relative; border: 1px solid var(--border-gray); border-radius: 24px;
  padding: clamp(1.6rem, 3vw, 2.6rem); background: #fff;
  min-height: min(52vh, 560px); align-content: center;
  box-shadow: 2px 3px 0 0 rgba(22,35,38,.05), 14px 20px 40px -24px rgba(22,35,38,.28);
  transition: translate .3s ease, box-shadow .3s ease, border-color .3s ease;
}
.hub__row.is-on { border-color: rgba(107,155,215,.55); box-shadow: 0 0 0 3px rgba(107,155,215,.14), 14px 20px 40px -24px rgba(22,35,38,.28); }
.hub__row:hover { translate: 0 -4px; box-shadow: 2px 3px 0 0 rgba(22,35,38,.06), 22px 32px 54px -26px rgba(22,35,38,.40); }
.hub__row:hover h2 { color: var(--blue-ink); }
.hub__link::before { content: ""; position: absolute; inset: 0; border-radius: inherit; z-index: 1; }
.hub__link:focus-visible { outline: none; }
.hub__row:has(.hub__link:focus-visible) { outline: 2px solid var(--haus-blue); outline-offset: 4px; }
.hub__go {
  margin-left: auto; display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%;
  background: var(--deep-haus); color: #fff; font-size: 1.05rem; line-height: 1;
  transition: background .25s ease, color .25s ease, rotate .25s ease;
}
.hub__row:hover .hub__go, .hub__row:has(.hub__link:focus-visible) .hub__go { background: var(--haus-blue); color: var(--deep-haus); rotate: 45deg; }
@media (prefers-reduced-motion: reduce) { .hub__row, .hub__go { transition: none; } .hub__row:hover { translate: none; } }
@media (max-width: 900px) {
  .hub__row, .hub__row:first-child { min-height: 0; }
  .hub__img { border-radius: 16px; overflow: hidden; }
}
@media (max-width: 900px) {
  .hub { grid-template-columns: 1fr; }
  .hub__pics { display: none; }
  .hub__row { min-height: 0; }
  .hub__img { display: block; aspect-ratio: 16 / 10; }
}
@media (prefers-reduced-motion: reduce) { .hub__pic { transition: none; } }


/* ---------- restoration page ---------- */
/* ---------- Restoration & Preservation: page-only additions ----------
   Two small things the shared SERVICES block does not have. Both are
   built from the homepage vocabulary (hairline, mono caption, muted
   prose) and prefixed rp- so they can be merged into site.css without
   colliding with anything. */

/* The founder, first person (SEO brief 1.6). A quiet pull quote in the
   stance column: a hairline on the left rather than a card, body size
   rather than display size so it does not fight the h2 above it. */
.rp-quote {
  margin: .4rem 0 0; padding: .2rem 0 .2rem 1.15rem;
  border-left: 2px solid var(--champagne);
}
.rp-quote p { color: var(--charcoal); font-size: 1.02rem; line-height: 1.55; }
.rp-quote footer { margin-top: .6rem; }
.rp-quote cite {
  font-style: normal; font-family: var(--sans); font-weight: 600; font-size: .7rem;
  letter-spacing: .12em; text-transform: uppercase; color: var(--taupe);
}

/* The one finished photograph that stands in for the before/after until
   Carl's "before" frames arrive. Wide band, same mono caption the slider
   will use (.ba-cap), so swapping in .ba later changes nothing around it. */
.rp-still { margin: 0; }
.rp-still .media { aspect-ratio: 21 / 10; }
@media (max-width: 700px) { .rp-still .media { aspect-ratio: 4 / 3; } }

/* ---------- the versus table on phones ----------
   Three columns do not fit in 390px. Below 640px each row becomes a
   block: the change, then the two contracts one under the other, each
   with its column name as a mono label. Cells carry data-label for the mono name. */
@media (max-width: 640px) {
  .versus,
  .versus tbody,
  .versus tr,
  .versus th,
  .versus td { display: block; width: auto; }
  .versus thead { display: none; }
  .versus tr { padding: 1rem 0; border-bottom: 1px solid var(--border-gray); }
  .versus th,
  .versus td { padding: 0; border-bottom: 0; }
  .versus td { margin-top: .75rem; }
  .versus td::before {
    content: attr(data-label); display: block; margin-bottom: .2rem;
    font-family: var(--sans); font-weight: 600; font-size: .66rem; letter-spacing: .12em; text-transform: uppercase; color: var(--taupe);
  }
  .versus td.is-us { padding: .7rem .85rem; border-radius: 10px; }
}


/* ---------- service hero: the homepage hero, per service ----------
   Same plate, scrim, push-in and glass card as the homepage (.hero,
   .proof); only the words and the card's facts change. The header ships
   in its dark-glass state over it, exactly as on the homepage. */
.hero--svc .hero__media { min-height: 76vh; }
.hero .crumbs { margin-bottom: 1rem; }
.hero .crumbs li { color: rgba(255,255,255,.72); text-shadow: 0 1px 14px rgba(10,18,20,.5); }
.hero .crumbs li + li::before { color: rgba(255,255,255,.45); }
.hero .crumbs a:hover { color: #fff; }
.hero .crumbs [aria-current] { color: #fff; }
.proof__num--sm { font-size: 2.1rem; }
.proof__facts { display: grid; gap: .35rem; margin-top: .2rem; }
.proof__facts li { display: flex; align-items: center; gap: .5rem; font-size: .82rem; color: rgba(22,35,38,.88); }
.proof__facts li::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: var(--champagne-ink); flex: none; }

/* ---------- chapters: no sidebar ----------
   Each section opens with its number and a hairline ruled across the
   page; a champagne line along the top edge reads out progress; the
   folio at the foot of the screen names the chapter and opens the list.
   The anchors work without script; script only adds the folio and the
   line, and only once the hero has gone. */
.pg--flow { display: block; }
.pg--flow .pg__body > .section:first-child { padding-top: clamp(2.2rem, 4.7vw, 4rem); }
.chap-open { display: flex; align-items: center; gap: .6rem; margin-bottom: clamp(1.2rem, 2.4vw, 1.8rem); }
.chap-open::after { content: ""; flex: 1; height: 1px; background: var(--border-gray); }
.chap-open b { font-family: var(--sans); font-weight: 600; font-size: .8rem; font-weight: 500; color: var(--champagne-ink); font-variant-numeric: tabular-nums; }
.chap-open i { font-style: normal; font-family: var(--sans); font-weight: 600; font-size: .66rem; letter-spacing: .08em; color: var(--taupe); }

.pgline { position: fixed; inset: 0 0 auto 0; height: 2px; z-index: 61; pointer-events: none; opacity: 0; transition: opacity .35s ease; }
.pgline.is-on { opacity: 1; }
.pgline i { display: block; height: 100%; background: var(--champagne); transform-origin: 0 50%; scale: var(--p, 0) 1; }

.folio { position: fixed; z-index: 58; left: 50%; translate: -50% 10px; bottom: calc(env(safe-area-inset-bottom, 0px) + 14px);
  max-width: calc(100vw - 2 * var(--gutter)); opacity: 0; visibility: hidden;
  transition: opacity .32s cubic-bezier(.2,.7,.3,1), translate .52s cubic-bezier(.22,.75,.3,1), visibility 0s linear .52s; }
.folio.is-in { opacity: 1; visibility: visible; translate: -50% 0; transition-delay: 0s; }
@media (min-width: 1040px) {
  .folio { left: max(var(--gutter), calc((100vw - var(--shell)) / 2 + var(--gutter))); translate: 0 10px; bottom: 22px; }
  .folio.is-in { translate: 0 0; }
}
.folio__btn, .folio__list {
  background: linear-gradient(180deg, rgba(255,255,255,.9), rgba(255,255,255,.8)), rgba(250,249,246,.75);
  -webkit-backdrop-filter: blur(22px) saturate(185%); backdrop-filter: blur(22px) saturate(185%);
  border: 1px solid rgba(22,35,38,.10);
  box-shadow: 0 1px 0 rgba(255,255,255,.9) inset, 0 14px 34px -20px rgba(22,35,38,.40), 0 2px 8px -4px rgba(22,35,38,.14);
}
.folio__btn { display: inline-flex; align-items: center; gap: .7rem; min-height: 44px; padding: 0 1rem 0 .95rem; border-radius: var(--pill); cursor: pointer; }
.folio__n { font-family: var(--sans); font-weight: 600; font-size: .68rem; font-weight: 500; letter-spacing: .06em; color: var(--champagne-ink); font-variant-numeric: tabular-nums; }
.folio__n i { font-style: normal; color: var(--taupe); margin: 0 .2em; }
.folio__name { font-size: .84rem; font-weight: 600; letter-spacing: -.01em; color: var(--deep-haus); white-space: nowrap; max-width: 46vw; overflow: hidden; text-overflow: ellipsis; }
.folio__mark { position: relative; width: 12px; height: 12px; transition: rotate .35s cubic-bezier(.2,.7,.3,1); }
.folio__mark::before { content: ""; position: absolute; inset: 2px; border-left: 1.5px solid var(--muted); border-top: 1.5px solid var(--muted); rotate: 45deg; translate: 0 3px; }
.folio__btn[aria-expanded="true"] .folio__mark { rotate: 180deg; }
.folio__list { position: absolute; bottom: calc(100% + 10px); left: 0; width: min(320px, calc(100vw - 2 * var(--gutter))); padding: .5rem; border-radius: 22px;
  background: linear-gradient(180deg, rgba(255,255,255,.94), rgba(255,255,255,.86)), rgba(250,249,246,.8);
  opacity: 0; visibility: hidden; translate: 0 8px; scale: .98; transform-origin: bottom left;
  transition: opacity .3s ease, translate .42s cubic-bezier(.2,.7,.3,1), scale .42s cubic-bezier(.2,.7,.3,1), visibility 0s linear .42s; }
@media (max-width: 1039.98px) {
  .folio__list { left: 50%; translate: -50% 8px; transform-origin: bottom center; }
  .folio.is-open .folio__list { translate: -50% 0; }
}
.folio.is-open .folio__list { opacity: 1; visibility: visible; translate: 0 0; scale: 1; transition-delay: 0s; }
.folio__list li + li { border-top: 1px solid rgba(207,202,193,.5); margin: 0 .75rem; }
.folio__list li + li a { margin: 0 -.75rem; }
.folio__list a { position: relative; display: grid; grid-template-columns: 2.2rem 1fr; align-items: center; min-height: 42px; padding: 0 .75rem 0 .9rem; border-radius: 12px; text-decoration: none; color: var(--muted); font-size: .9rem; font-weight: 500; }
.folio__list a span { font-family: var(--sans); font-weight: 600; font-size: .66rem; color: var(--taupe); }
.folio__list a:hover { background: rgba(107,160,222,.08); color: var(--deep-haus); }
.folio__list a[aria-current] { color: var(--deep-haus); font-weight: 600; }
.folio__list a[aria-current]::before { content: ""; position: absolute; left: 0; top: 10px; bottom: 10px; width: 2px; border-radius: 2px; background: var(--champagne); }
.folio :focus-visible { outline: 2px solid var(--blue-ink); outline-offset: 2px; }
@media (max-width: 1039.98px) { html:has(.folio) { scroll-padding-bottom: 72px; } }


/* ---------- service pages: headers stacked, not side by side ----------
   The heading first, the line that explains it directly underneath in
   ink rather than grey. Nothing sits out on the right of a heading. */
.pg .sec-head { grid-template-columns: minmax(0, 1fr); gap: 1.1rem; align-items: start; }
.pg .sec-head__aside {
  max-width: 58ch; padding-bottom: 0;
  font-size: var(--step-1); line-height: 1.5; font-weight: 500; letter-spacing: -.01em;
  color: rgba(22,35,38,.78);
}

/* ---------- stacking cards (island rules, claim steps) ----------
   Each card sticks a little lower than the last, so as the reader
   scrolls the next one slides up and settles over it; the one beneath
   eases back. The paper on the right is the document the step leaves
   you with, drawn, not photographed. */
.scards { display: grid; gap: clamp(1.5rem, 4vh, 2.5rem); padding-bottom: 4vh; }
.scard { position: sticky; top: calc(clamp(88px, 10vw, 112px) + var(--i) * 14px); }
.scard__in {
  display: grid; grid-template-columns: minmax(0, 1.45fr) minmax(0, .8fr); gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: center; min-height: min(420px, 62vh);
  padding: clamp(1.6rem, 3.4vw, 2.8rem); border-radius: 26px;
  background: #fff; border: 1px solid rgba(207,202,193,.8);
  box-shadow:
    0 1px 0 rgba(255,255,255,.9) inset,
    2px 3px 0 0 rgba(22,35,38,.05),
    10px 14px 13px -5px rgba(22,35,38,.10),
    22px 30px 34px -14px rgba(22,35,38,.14),
    38px 54px 74px -24px rgba(22,35,38,.18);
  transform-origin: 50% 0;
  transition: scale .5s cubic-bezier(.2,.7,.3,1), filter .5s ease;
}
.scard:nth-child(even) .scard__in { background: var(--soft-white); }
.scard.is-under .scard__in { scale: .955; filter: brightness(.97); }
.scard__n {
  font-size: clamp(2.6rem, 2rem + 2vw, 3.6rem); font-weight: 700; line-height: 1; letter-spacing: -.04em;
  font-variant-numeric: tabular-nums;
  background: linear-gradient(160deg, var(--champagne) 0%, var(--champagne-ink) 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.scard__t { margin-top: .9rem; font-size: var(--step-2); color: var(--deep-haus); }
.scard__lead { margin-top: .9rem; max-width: 56ch; font-size: 1.06rem; line-height: 1.6; color: var(--charcoal); }
.scard__meta { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); gap: .9rem 1.6rem; margin-top: 1.4rem; padding-top: 1.2rem; border-top: 1px solid var(--border-gray); }
.scard__k { font-size: .82rem; font-weight: 700; letter-spacing: -.005em; color: var(--blue-ink); }
.scard__v { margin-top: .2rem; font-size: .95rem; line-height: 1.5; color: var(--muted); }
.scard__src { margin-top: 1rem; font-size: .8rem; color: var(--muted); }
.scard__src a { color: var(--blue-ink); }

.scard__doc { display: grid; place-items: center; }
.scard__paper {
  position: relative; width: min(100%, 250px); aspect-ratio: 5 / 6.4; padding: 1.4rem 1.3rem;
  border-radius: 10px; background: linear-gradient(180deg, #fff, #FBFAF7);
  border: 1px solid rgba(207,202,193,.9);
  box-shadow: 2px 3px 0 0 rgba(22,35,38,.06), 10px 14px 20px -8px rgba(22,35,38,.18), 24px 32px 44px -22px rgba(22,35,38,.22);
  rotate: 2.5deg;
  transition: rotate .6s cubic-bezier(.2,.7,.3,1);
}
.scard:nth-child(even) .scard__paper { rotate: -2deg; }
.scard__paper::before {
  content: ""; position: absolute; left: 1.3rem; right: 1.3rem; top: 3.3rem; height: 2px; border-radius: 2px;
  background: var(--champagne);
}
.scard__tag { font-size: .76rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--blue-ink); }
.scard__dtitle { margin-top: 1.55rem; font-size: 1.02rem; font-weight: 700; line-height: 1.3; letter-spacing: -.015em; color: var(--deep-haus); }
.scard__lines { display: grid; gap: .55rem; margin-top: 1.1rem; }
.scard__lines i { display: block; height: 6px; border-radius: 4px; background: var(--warm-stone); }
.scard__lines i:nth-child(2) { width: 86%; }
.scard__lines i:nth-child(3) { width: 72%; }
.scard__lines i:nth-child(4) { width: 44%; }
.scard__seal {
  position: absolute; right: 1.1rem; bottom: 1.1rem; display: grid; place-items: center;
  width: 46px; height: 46px; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #86B2E6, var(--blue-ink)); box-shadow: 0 0 0 4px rgba(107,160,222,.18);
}
.scard__seal svg { width: 22px; height: 22px; fill: none; stroke: #fff; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
@media (max-width: 860px) {
  .scard__in { grid-template-columns: 1fr; min-height: 0; }
  .scard__doc { display: none; }
  .scard { top: calc(76px + var(--i) * 10px); }
}
@media (prefers-reduced-motion: reduce) {
  .scard.is-under .scard__in { scale: 1; filter: none; }
}


/* ---------- the house section, pinned and scrolled ----------
   Wide screens with motion allowed: the drawing and one dark card hold
   still while the page scrolls under them. Each step turns the card like
   a page and moves the drawing's camera onto the parts it describes.
   Everything else keeps the tap-to-light list above. */
.sect.is-scrolly { display: block; height: calc(100vh + var(--n) * 58vh); }
.sect__stage {
  position: sticky; top: clamp(92px, 9vw, 112px); height: calc(100vh - clamp(120px, 12vw, 150px));
  display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: clamp(1.5rem, 3.5vw, 3rem); align-items: center;
}
.sect.is-scrolly .sect__fig { position: static; }
.sect.is-scrolly .sect__svg { transition: none; }
.sect__deck { position: relative; perspective: 1400px; }
.sect.is-scrolly .sect__list { position: relative; border: 0; height: clamp(300px, 46vh, 400px); }
.sect.is-scrolly .sect__item {
  position: absolute; inset: 0; border: 0;
  opacity: 0; pointer-events: none;
  transform: translateX(46px) rotateY(-16deg); transform-origin: 0 50%;
  transition: opacity .45s ease, transform .75s cubic-bezier(.22,.75,.3,1);
}
.sect.is-scrolly .sect__item.is-past { transform: translateX(-46px) rotateY(14deg); transform-origin: 100% 50%; }
.sect.is-scrolly .sect__item.is-now { opacity: 1; transform: none; pointer-events: auto; }
.sect.is-scrolly .sect__btn {
  height: 100%; align-content: start; grid-template-columns: 1fr; gap: 0; cursor: default;
  padding: clamp(1.6rem, 3vw, 2.4rem); border-radius: 24px;
  background:
    radial-gradient(120% 80% at 100% 0%, rgba(107,160,222,.16), transparent 60%),
    var(--deep-haus);
  box-shadow: 0 1px 0 rgba(255,255,255,.08) inset, 10px 14px 13px -5px rgba(22,35,38,.22), 38px 54px 74px -24px rgba(22,35,38,.42);
}
.sect.is-scrolly .sect__btn:hover, .sect.is-scrolly .sect__btn[aria-pressed="true"] { background:
    radial-gradient(120% 80% at 100% 0%, rgba(107,160,222,.16), transparent 60%),
    var(--deep-haus); }
.sect.is-scrolly .sect__n {
  width: auto; height: auto; border: 0; background: none; place-items: start;
  font-family: var(--sans); font-size: clamp(2.8rem, 2rem + 2.4vw, 4rem); font-weight: 700; letter-spacing: -.04em; line-height: 1;
  background: linear-gradient(160deg, #E4D3A8 0%, var(--champagne) 100%); -webkit-background-clip: text; background-clip: text; color: transparent;
}
.sect.is-scrolly .sect__btn[aria-pressed="true"] .sect__n { background: linear-gradient(160deg, #E4D3A8 0%, var(--champagne) 100%); -webkit-background-clip: text; background-clip: text; color: transparent; border: 0; }
.sect.is-scrolly .sect__t { margin-top: 1rem; font-size: var(--step-2); line-height: 1.12; letter-spacing: -.025em; color: #fff; padding: 0; }
.sect.is-scrolly .sect__d { margin-top: .9rem; font-size: 1.02rem; line-height: 1.6; color: rgba(239,236,228,.82); }
.sect.is-scrolly .sect__d .todo { color: #F6E5DC; background: rgba(138,58,30,.5); border-color: rgba(217,169,146,.6); }
.sect__ticks { display: flex; align-items: center; gap: .45rem; margin-top: 1.2rem; }
.sect__tick {
  width: 30px; height: 30px; display: grid; place-items: center; border: 0; background: none; cursor: pointer; padding: 0;
}
.sect__tick::before { content: ""; width: 22px; height: 3px; border-radius: 3px; background: var(--border-gray); transition: background-color .3s ease, width .3s ease; }
.sect__tick.is-done::before { background: var(--champagne); }
.sect__tick.is-now::before { background: var(--deep-haus); width: 30px; }
.sect__tick:focus-visible { outline: 2px solid var(--blue-ink); outline-offset: 2px; border-radius: 4px; }
.sect__count { margin-left: .5rem; font-size: .8rem; font-weight: 600; color: var(--muted); font-variant-numeric: tabular-nums; }
.sect.is-scrolly .sect__note { margin-top: .6rem; }

/* the heading rides with the pinned stage, so the section never loses its name */
.sect__stage { grid-template-rows: auto minmax(0, 1fr); row-gap: clamp(1rem, 2.4vh, 1.8rem); align-content: center; }
.sect__stage > .sec-head { grid-column: 1 / -1; gap: .6rem; }
.sect__stage > .sec-head .h-lg { font-size: clamp(1.9rem, 1.3rem + 1.9vw, 2.9rem); }
.sect__stage > .sec-head .sec-head__aside { font-size: 1.02rem; max-width: 64ch; }
.sect.is-scrolly .sect__svg { max-height: calc(100vh - 380px); }
/* the base badge is a circle; at card size its radius clipped the figure */
.sect.is-scrolly .sect__n { border-radius: 0; overflow: visible; padding-left: .04em; }


/* =================================================================
   SERVICE PAGES, SECOND WAVE
   What made the rules cards and the pinned drawing work, applied to
   the rest: one idea at a time, no label column with a gap beside it,
   something to look at, ink instead of grey, and a different motion
   for every section of a page.
   ================================================================= */

/* ---------- what's included: tiles, not a two-column form ---------- */
.incl { grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr)); gap: clamp(.8rem, 1.6vw, 1.1rem); border-top: 0; counter-reset: incl; }
.incl__row {
  counter-increment: incl; position: relative;
  grid-template-columns: 1fr; gap: .55rem; align-content: start;
  padding: 1.35rem 1.35rem 1.4rem; border: 1px solid rgba(207,202,193,.8); border-radius: 18px;
  background: var(--soft-white);
  transition: background-color .3s ease, border-color .3s ease, translate .4s cubic-bezier(.2,.7,.3,1);
}
.incl__row::before {
  content: counter(incl, decimal-leading-zero);
  font-size: 1.5rem; font-weight: 700; letter-spacing: -.03em; line-height: 1;
  background: linear-gradient(160deg, var(--champagne), var(--champagne-ink)); -webkit-background-clip: text; background-clip: text; color: transparent;
}
.incl__row:hover { background: #fff; border-color: var(--haus-blue); translate: 0 -3px; }
.incl__row dt { font-size: 1.08rem; }
.incl__row dd { font-size: .95rem; color: var(--muted); }

/* ---------- who it's for: cards, icon on top, no label column ---------- */
.who { grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); gap: clamp(.8rem, 1.6vw, 1.1rem); border-top: 0; }
.who__row {
  grid-template-columns: 1fr; gap: .7rem; align-content: start;
  padding: 1.5rem; border: 1px solid rgba(207,202,193,.8); border-radius: 20px; background: #fff;
  box-shadow: 2px 3px 0 0 rgba(22,35,38,.04), 10px 14px 18px -10px rgba(22,35,38,.12);
}
.who__row .svc__ico { width: 46px; height: 46px; border-radius: 14px; }
.who__name, .who__text { padding-top: 0; grid-column: auto; }
.who__name { font-size: 1.12rem; }
@media (max-width: 700px) { .who__row { grid-template-columns: 1fr; } .who__text { grid-column: auto; } }

/* ---------- reveal on scroll (tiles, cards, registers) ----------
   Finished state is the default; script winds each item back only once
   it can promise to bring it in again. */
.rise-live .rise-i { opacity: 0; translate: 0 22px; }
.rise-live .rise-i.from-l { translate: -28px 0; }
.rise-live .rise-i.from-r { translate: 28px 0; }
.rise-live .rise-i.is-in { opacity: 1; translate: none; transition: opacity .55s ease var(--rd, 0s), translate .7s cubic-bezier(.22,.75,.3,1) var(--rd, 0s); }

/* ---------- persona: photo cards for who a service is for ---------- */
.persona { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); gap: clamp(1rem, 2vw, 1.5rem); }
.persona__card { display: grid; grid-template-rows: auto 1fr; border-radius: 22px; overflow: hidden; background: #fff; border: 1px solid rgba(207,202,193,.8);
  box-shadow: 2px 3px 0 0 rgba(22,35,38,.05), 10px 14px 13px -5px rgba(22,35,38,.10), 30px 44px 60px -28px rgba(22,35,38,.24);
  transition: translate .45s cubic-bezier(.2,.7,.3,1), box-shadow .45s ease; }
.persona__card:hover { translate: 0 -5px; }
.persona__img { position: relative; aspect-ratio: 4 / 3; overflow: hidden; }
.persona__img img { width: 100%; height: 100%; object-fit: cover; transition: scale .8s cubic-bezier(.2,.7,.3,1); }
.persona__card:hover .persona__img img { scale: 1.05; }
.persona__tag {
  position: absolute; left: .9rem; top: .9rem; padding: .35rem .8rem; border-radius: var(--pill);
  background: rgba(250,249,246,.9); font-size: .74rem; font-weight: 700; color: var(--blue-ink);
}
.persona__body { padding: 1.4rem 1.5rem 1.6rem; display: grid; gap: .6rem; align-content: start; }
.persona__name { font-size: var(--step-1); font-weight: 700; letter-spacing: -.02em; color: var(--deep-haus); }
.persona__text { color: var(--muted); }

/* ---------- track: a filmstrip that slides sideways as you scroll ----------
   Wide screens with motion allowed pin the stage and translate the rail;
   everywhere else it is a swipeable, snapping row. */
.track__stage { display: grid; gap: clamp(1.2rem, 3vh, 2rem); }
.track__rail { display: flex; gap: clamp(1rem, 2vw, 1.4rem); overflow-x: auto; scroll-snap-type: x mandatory; padding: .4rem .2rem 1.6rem; scrollbar-width: thin; }
.track__card {
  flex: 0 0 min(340px, 82vw); scroll-snap-align: start; display: grid; grid-template-rows: auto auto 1fr; gap: .8rem;
  min-height: 380px; padding: 1.7rem; border-radius: 24px; background: var(--deep-haus); color: #fff;
  background-image: radial-gradient(120% 70% at 100% 0%, rgba(107,160,222,.18), transparent 60%);
  box-shadow: 0 1px 0 rgba(255,255,255,.08) inset, 10px 14px 13px -5px rgba(22,35,38,.2), 30px 44px 60px -28px rgba(22,35,38,.4);
}
.track__card:nth-child(3n+2) { background-color: #1D3035; }
.track__card:nth-child(3n) { background-color: #203A43; }
.track__n { font-size: 3rem; font-weight: 700; letter-spacing: -.04em; line-height: 1;
  background: linear-gradient(160deg, #E4D3A8, var(--champagne)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.track__t { font-size: var(--step-1); font-weight: 700; letter-spacing: -.02em; line-height: 1.2; color: #fff; }
.track__d { color: rgba(239,236,228,.8); font-size: .97rem; line-height: 1.6; align-self: start; }
.track__d .todo { color: #F6E5DC; background: rgba(138,58,30,.5); border-color: rgba(217,169,146,.6); }
.track__img { margin: -1.7rem -1.7rem .2rem; aspect-ratio: 16 / 10; overflow: hidden; border-radius: 24px 24px 0 0; }
.track__img img { width: 100%; height: 100%; object-fit: cover; }
.track__bar { height: 3px; border-radius: 3px; background: var(--border-gray); overflow: hidden; }
.track__bar i { display: block; height: 100%; background: var(--champagne); transform-origin: 0 50%; scale: var(--tp, 0) 1; }
.track.is-pinned { height: var(--th, auto); }
.track.is-pinned .track__stage { position: sticky; top: clamp(92px, 9vw, 112px); height: calc(100vh - clamp(120px, 12vw, 150px)); align-content: center; overflow: hidden; }
.track.is-pinned .track__rail { overflow: visible; scroll-snap-type: none; will-change: transform; }
.track.is-pinned .track__stage > .sec-head .h-lg { font-size: clamp(1.9rem, 1.3rem + 1.9vw, 2.9rem); }

/* ---------- wipe: raw to finished, scrubbed by scroll ---------- */
.wipe { position: relative; margin: 0; aspect-ratio: 4 / 3; border-radius: var(--radius); overflow: hidden; --w: 0%;
  border: 2px solid #fff;
  box-shadow: 0 0 0 1px rgba(207,202,193,.9), 2px 3px 0 0 rgba(22,35,38,.14), 10px 14px 13px -5px rgba(22,35,38,.20), 38px 54px 74px -24px rgba(22,35,38,.32); }
.wipe img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.wipe__b { clip-path: inset(0 calc(100% - var(--w)) 0 0); }
.wipe__edge { position: absolute; top: 0; bottom: 0; left: var(--w); width: 2px; translate: -1px 0; background: var(--champagne); box-shadow: 0 0 18px rgba(200,181,138,.8); }
.wipe__lbl { position: absolute; bottom: .9rem; padding: .35rem .8rem; border-radius: var(--pill); background: rgba(16,25,28,.55); color: #fff; font-size: .74rem; font-weight: 700; }
.wipe__lbl--a { right: .9rem; }
.wipe__lbl--b { left: .9rem; }
.wipe__edge, .wipe__lbl { pointer-events: none; }
/* the hand control: an invisible range over the picture (as on .ba),
   and a champagne-ringed knob riding the edge to say it can be held */
.wipe__range {
  position: absolute; inset: 0; width: 100%; height: 100%; margin: 0;
  opacity: 0; cursor: ew-resize; touch-action: pan-y; -webkit-appearance: none; appearance: none;
}
/* on the figure, not the edge, so it can stop short of the frame at 0% and 100% */
.wipe.is-live::after {
  content: "\2194"; position: absolute; top: 50%; left: clamp(26px, var(--w), calc(100% - 26px)); translate: -50% -50%;
  display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%;
  background: #fff; color: var(--deep-haus); font-size: 1.1rem; pointer-events: none;
  box-shadow: 0 0 0 2px var(--champagne), 4px 6px 14px -4px rgba(22,35,38,.45);
  transition: scale .2s ease;
}
.wipe.is-live:hover::after { scale: 1.08; }
.wipe.is-live:has(.wipe__range:focus-visible)::after { outline: 2px solid var(--haus-blue); outline-offset: 3px; }

/* ---------- project photo cards ---------- */
.pcards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); gap: clamp(1rem, 2vw, 1.5rem); }
.pcard { position: relative; display: block; aspect-ratio: 4 / 5; border-radius: 22px; overflow: hidden; text-decoration: none; color: #fff;
  box-shadow: 2px 3px 0 0 rgba(22,35,38,.08), 10px 14px 13px -5px rgba(22,35,38,.18), 38px 54px 74px -24px rgba(22,35,38,.32); }
.pcard img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: scale .9s cubic-bezier(.2,.7,.3,1); }
.pcard::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(11,20,23,0) 40%, rgba(11,20,23,.78) 100%); }
.pcard:hover img { scale: 1.06; }
.pcard__body { position: absolute; left: 0; right: 0; bottom: 0; z-index: 1; padding: 1.4rem 1.5rem; display: grid; gap: .35rem; }
.pcard__kind { width: fit-content; padding: .3rem .75rem; border-radius: var(--pill); background: rgba(250,249,246,.9); color: var(--blue-ink); font-size: .72rem; font-weight: 700; }
.pcard__name { font-size: var(--step-1); font-weight: 700; letter-spacing: -.02em; }
.pcard__why { color: rgba(255,255,255,.86); font-size: .92rem; }
.pcard__why .todo { vertical-align: baseline; }

/* ---------- a dark band for Carl's own words ---------- */
.qband { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: clamp(1.5rem, 4vw, 3.5rem); align-items: center;
  padding: clamp(2rem, 5vw, 3.6rem); border-radius: 28px; color: #fff;
  background: radial-gradient(120% 90% at 0% 0%, rgba(107,160,222,.2), transparent 55%), var(--deep-haus);
  box-shadow: 10px 14px 13px -5px rgba(22,35,38,.2), 38px 54px 74px -24px rgba(22,35,38,.4); }
.qband__face { width: clamp(96px, 12vw, 150px); aspect-ratio: 1; border-radius: 50%; object-fit: cover; border: 3px solid rgba(200,181,138,.7); }
.qband__q { margin: 0; font-size: var(--step-2); font-weight: 600; line-height: 1.3; letter-spacing: -.02em; }
.qband__q .todo { font-size: .62rem; }
.qband__who { margin-top: 1rem; font-size: .9rem; font-weight: 600; color: var(--champagne); }
@media (max-width: 620px) { .qband { grid-template-columns: 1fr; } }

/* light filmstrip, for a page that already has a dark card elsewhere */
.track--light .track__card { background: #fff; background-image: none; color: var(--deep-haus); border: 1px solid rgba(207,202,193,.8);
  box-shadow: 2px 3px 0 0 rgba(22,35,38,.05), 10px 14px 13px -5px rgba(22,35,38,.10), 30px 44px 60px -28px rgba(22,35,38,.22); }
.track--light .track__card:nth-child(even) { background-color: var(--soft-white); }
.track--light .track__n { background: linear-gradient(160deg, var(--champagne), var(--champagne-ink)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.track--light .track__t { color: var(--deep-haus); }
.track--light .track__d { color: var(--muted); }
.track--light .track__d .todo { color: #8A3A1E; background: #F6E5DC; border-color: #D9A992; }
.track__card { min-height: 320px; }
/* the rail must be as wide as the column, not its seven cards, or there is nothing to slide */
.track__stage { grid-template-columns: minmax(0, 1fr); }
.track__rail { min-width: 0; width: 100%; }


/* ======================================================================
   SERVICE PAGES, SECOND WAVE: page-only additions, merged 2026-10-11
   (were build/css-remodels.css, css-storm.css, css-how-we-work.css)
   ====================================================================== */

/* ---------- Remodels & Renovations: page-only additions ----------
   Three small things the shared SERVICES blocks do not have, prefixed
   rm- so they merge into site.css without touching anything. Built from
   the second-wave vocabulary: 22-24px radii, the qband's dark plate and
   shadows, the champagne accent, Figtree semibold labels. */

/* ---- why it's different: a dark plate beside a light one ----
   Left, what the island does to a house; right, what that means for the
   job. The two panels slide in from their own sides as the section
   arrives (scroll-driven, CSS only, so it is a different motion from the
   rise the cards use and needs no script). */
.rm-contrast { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr)); gap: clamp(1rem, 2vw, 1.5rem); align-items: stretch; }
.rm-panel {
  display: grid; gap: 1.1rem; align-content: start;
  padding: clamp(1.7rem, 3.2vw, 2.6rem); border-radius: 24px;
}
.rm-panel .eyebrow { margin: 0; }
.rm-panel__t { font-size: var(--step-2); font-weight: 700; letter-spacing: -.02em; line-height: 1.15; }
.rm-panel .checks { gap: .85rem; margin-top: .2rem; }
.rm-panel .checks li { padding-left: 1.25rem; line-height: 1.55; }
.rm-panel .checks li::before { height: 1.15em; }
.rm-panel .checks b { font-weight: 700; }
.rm-panel__foot { margin-top: auto; padding-top: .8rem; display: grid; gap: .8rem; }

.rm-panel--dark {
  color: #fff;
  background: radial-gradient(120% 90% at 100% 0%, rgba(107,160,222,.2), transparent 55%), var(--deep-haus);
  box-shadow: 0 1px 0 rgba(255,255,255,.08) inset, 10px 14px 13px -5px rgba(22,35,38,.2), 38px 54px 74px -24px rgba(22,35,38,.4);
}
.rm-panel--dark .eyebrow { color: var(--champagne); }
.rm-panel--dark .rm-panel__t { color: #fff; }
.rm-panel--dark .checks li { color: rgba(239,236,228,.84); }
.rm-panel--dark .checks b { color: #fff; }

.rm-panel--light {
  color: var(--deep-haus);
  background: #fff; border: 1px solid rgba(207,202,193,.8);
  box-shadow: 2px 3px 0 0 rgba(22,35,38,.05), 10px 14px 13px -5px rgba(22,35,38,.10), 30px 44px 60px -28px rgba(22,35,38,.22);
}
.rm-panel--light .checks li { color: var(--charcoal); }
.rm-panel--light .checks b { color: var(--deep-haus); }
.rm-panel__note { color: var(--muted); font-size: .95rem; }

/* the panels slide in from their own sides as the section enters the viewport */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) and (min-width: 700px) {
    .rm-panel { animation: rm-slide-l linear both; animation-timeline: view(); animation-range: entry 5% entry 70%; }
    .rm-panel--light { animation-name: rm-slide-r; }
  }
}
@keyframes rm-slide-l { from { opacity: 0; translate: -36px 0; } to { opacity: 1; translate: 0 0; } }
@keyframes rm-slide-r { from { opacity: 0; translate: 36px 0; } to { opacity: 1; translate: 0 0; } }

/* ---- the next remodel: a dashed placeholder beside the one real card ----
   Text only, no photograph, no invented job. Same corner and footprint
   as .pcard on wide screens; on a phone it is only as tall as its words. */
.rm-next {
  display: grid; align-content: center; gap: .6rem;
  padding: clamp(1.4rem, 3vw, 2.2rem); border-radius: 22px;
  border: 2px dashed rgba(166,159,148,.7); background: var(--soft-white); color: var(--deep-haus);
}
.rm-next__kind { width: fit-content; padding: .3rem .75rem; border-radius: var(--pill); border: 1px dashed rgba(166,159,148,.8); color: var(--taupe); font-size: .72rem; font-weight: 700; }
.rm-next__name { font-size: var(--step-1); font-weight: 700; letter-spacing: -.02em; }
.rm-next__why { color: var(--muted); font-size: .95rem; }
.rm-next__why .todo { vertical-align: baseline; }
@media (min-width: 700px) { .rm-next { aspect-ratio: 4 / 5; } }

/* ---- materials pair: a slight opposite tilt and a champagne caption bar ----
   The pair stays static (Custom Homes and Restoration already use the
   wipe); each photograph sits on its own card with the caption as a
   champagne bar under it, tilted a degree or so against its neighbour. */
.rm-pair figure {
  gap: 0; border-radius: 18px; overflow: hidden; background: var(--champagne);
  box-shadow: 2px 3px 0 0 rgba(22,35,38,.08), 10px 14px 13px -5px rgba(22,35,38,.18), 34px 48px 66px -24px rgba(22,35,38,.32);
  transition: rotate .5s cubic-bezier(.2,.7,.3,1), translate .5s cubic-bezier(.2,.7,.3,1);
}
.rm-pair figure:first-child { rotate: -1.6deg; translate: 0 .6rem; }
.rm-pair figure:last-child { rotate: 1.6deg; translate: 0 -.6rem; }
.rm-pair figure:hover { rotate: 0deg; translate: 0 0; }
.rm-pair .media { border-radius: 0; }
.rm-pair figcaption {
  padding: .7rem 1rem; background: var(--champagne); color: var(--deep-haus);
  font-family: var(--sans); font-weight: 700; font-size: .72rem; letter-spacing: .1em; text-transform: uppercase;
}
@media (max-width: 809.98px) { .rm-pair { padding: .6rem .4rem; } }


/* ---------- Storm Repair & Resilience: page-only additions ----------
   Three things the shared blocks do not have, built from the same
   vocabulary (deep-haus card, hairline, champagne accent) and prefixed
   st- so they merge into site.css without colliding. */

/* Repair, then resilience: two panels side by side. The repair is the
   dark card, the retrofit the light one, so the pairing reads before a
   word is. Each carries its photograph across the top and its checks. */
.st-panels { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(1rem, 2vw, 1.5rem); align-items: stretch; }
.st-panel { display: grid; grid-template-rows: auto 1fr; border-radius: 26px; overflow: hidden; }
.st-panel__img { aspect-ratio: 16 / 8; overflow: hidden; }
.st-panel__img img { width: 100%; height: 100%; object-fit: cover; }
.st-panel__body { display: grid; gap: .9rem; align-content: start; padding: clamp(1.5rem, 3vw, 2.4rem); }
.st-panel__k { font-size: .78rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
.st-panel__t { font-size: var(--step-2); line-height: 1.12; letter-spacing: -.025em; }
.st-panel__d { font-size: 1.02rem; line-height: 1.6; }
.st-panel .checks { margin-top: .5rem; padding-top: 1.2rem; border-top: 1px solid var(--border-gray); }
.st-panel .checks li { font-size: .95rem; line-height: 1.5; }
.st-panel--dark {
  color: #fff;
  background: radial-gradient(120% 80% at 100% 0%, rgba(107,160,222,.16), transparent 60%), var(--deep-haus);
  box-shadow: 0 1px 0 rgba(255,255,255,.08) inset, 10px 14px 13px -5px rgba(22,35,38,.22), 38px 54px 74px -24px rgba(22,35,38,.42);
}
.st-panel--dark .st-panel__k { color: var(--champagne); }
.st-panel--dark .st-panel__t { color: #fff; }
.st-panel--dark .st-panel__d { color: rgba(239,236,228,.82); }
.st-panel--dark .checks { border-color: rgba(255,255,255,.14); }
.st-panel--dark .checks li { color: rgba(239,236,228,.88); }
.st-panel--dark .todo { color: #F6E5DC; background: rgba(138,58,30,.5); border-color: rgba(217,169,146,.6); }
.st-panel--light {
  color: var(--deep-haus); background: #fff; border: 1px solid rgba(207,202,193,.8);
  box-shadow: 2px 3px 0 0 rgba(22,35,38,.05), 10px 14px 13px -5px rgba(22,35,38,.10), 30px 44px 60px -28px rgba(22,35,38,.22);
}
.st-panel--light .st-panel__k { color: var(--blue-ink); }
.st-panel--light .st-panel__d { color: var(--charcoal); }
@media (max-width: 860px) {
  .st-panels { grid-template-columns: 1fr; }
  .st-panel__img { aspect-ratio: 16 / 9; }
}

/* The claim as a filmstrip: each dark card ends with two short lines,
   who acts and what you keep, inline rather than a label column. */
/* Five cards with two footer lines run taller than the shared rail, so
   the pinned stage is packed a little tighter to keep the bar and the
   sources line inside a 900px-high window. */
.st-track .track__stage { gap: clamp(.8rem, 1.8vh, 1.2rem); }
.st-track .sec-head__aside { max-width: 74ch; }
.st-track .track__rail { padding-bottom: .6rem; }
.st-track .track__card { position: relative; grid-template-rows: auto auto 1fr auto; padding: 1.5rem; }
.st-track .track__d { font-size: .94rem; }
.st-tag {
  position: absolute; top: 1.5rem; right: 1.5rem;
  padding: .3rem .7rem; border-radius: var(--pill);
  border: 1px solid rgba(255,255,255,.18); background: rgba(250,249,246,.1);
  font-size: .68rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--champagne);
}
.st-kv { display: grid; gap: .4rem; padding-top: .8rem; border-top: 1px solid rgba(255,255,255,.14); font-size: .82rem; line-height: 1.45; color: rgba(239,236,228,.78); }
.st-kv b { color: var(--champagne); font-weight: 700; margin-right: .3rem; }
.st-kv .todo { color: #F6E5DC; background: rgba(138,58,30,.5); border-color: rgba(217,169,146,.6); }
.st-src { margin-top: -.5rem; font-size: .78rem; line-height: 1.3; color: var(--muted); }
.st-src a { color: var(--blue-ink); }

/* Storm work: an honest empty state, one dashed card, until Carl names
   the jobs that can be shown. */
.st-empty {
  display: grid; gap: .55rem; justify-items: center; text-align: center;
  padding: clamp(2rem, 5vw, 3.2rem) clamp(1.2rem, 4vw, 2.5rem);
  border: 2px dashed rgba(207,202,193,.95); border-radius: 22px; background: var(--soft-white);
}
.st-empty__ico { width: 46px; height: 46px; border-radius: 14px; margin-bottom: .4rem; }
.st-empty__t { font-size: var(--step-1); font-weight: 700; letter-spacing: -.02em; color: var(--deep-haus); }
.st-empty__d { max-width: 52ch; color: var(--muted); }


/* ---------- How We Work: the three changes as stacking cards ----------
   Each card's meta row holds Fixed price | Open-book cost-plus side by
   side, then "You keep" on its own line underneath, the way the old
   versus table read. Our column is set in ink, the other in the
   muted grey the meta values use everywhere else. */
.hww-change .scard__meta { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.hww-change .scard__m:nth-child(3) { grid-column: 1 / -1; }
.hww-change .scard__m:nth-child(2) .scard__v { color: var(--deep-haus); }
@media (max-width: 560px) {
  .hww-change .scard__meta { grid-template-columns: 1fr; }
}

/* ======================================================================
   KEPT & REPLACED as two bands (Restoration): .keep--tags
   Kept is a cream band of salvage tags: white, a punched hole and a
   champagne string, each hung at its own small angle; they swing in
   from the hole. Replaced is a dark band of spec cards, each closing on
   the spec its own line names. Only .keep--tags; How We Work's .keep
   register is untouched.
   ====================================================================== */
.keep--tags { grid-template-columns: 1fr; gap: clamp(1.25rem, 2.4vw, 2rem); }
.keep--tags .keep__col { border-radius: 28px; padding: clamp(1.6rem, 3.6vw, 3.2rem); }
.keep__col--kept { background: #F3EFE6; box-shadow: inset 0 0 0 1px #E6DFD1; }
.keep__col--repl { background: var(--deep-haus); color: #fff;
  box-shadow: 2px 3px 0 0 rgba(22,35,38,.14), 24px 34px 60px -28px rgba(22,35,38,.55); }

.keep__head { display: flex; align-items: center; gap: 1rem; margin-bottom: clamp(1.4rem, 3vw, 2.4rem); }
.keep--tags .keep__col h3 { display: grid; gap: .3rem; margin: 0; padding: 0; border: 0;
  font-size: clamp(1.9rem, 1.4rem + 1.6vw, 2.8rem); line-height: 1; letter-spacing: -.03em; color: var(--deep-haus); }
.keep--tags .keep__col h3 span { font-size: .95rem; font-weight: 500; letter-spacing: 0; text-transform: none; color: var(--muted); }
.keep__col--repl h3 { color: #fff !important; }
.keep__col--repl h3 span { color: rgba(255,255,255,.66) !important; }
.keep__badge { flex: none; display: grid; place-items: center; width: 52px; height: 52px; border-radius: 50%; }
.keep__badge svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.keep__col--kept .keep__badge { background: #fff; color: var(--champagne-ink); box-shadow: 0 0 0 2px var(--champagne); }
.keep__col--repl .keep__badge { background: var(--haus-blue); color: var(--deep-haus); }

.keep--tags ul { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(1rem, 2vw, 1.6rem); }
.keep--tags .keep__col li { position: relative; display: flex; flex-direction: column; gap: .55rem;
  padding: 1.5rem 1.4rem 1.4rem; border: 0; border-radius: 18px; }
.keep--tags .keep__col li > b { font-size: 1.12rem; line-height: 1.3; }
.keep--tags .keep__col li > span { margin: 0; font-size: .98rem; line-height: 1.55; }
.keep__no { font-style: normal; font-weight: 700; font-size: .78rem; letter-spacing: .1em; }

/* the tags */
.keep__col--kept li { background: #fff; padding-top: 2.6rem !important; rotate: var(--tilt, 0deg); transform-origin: 30px 22px;
  box-shadow: 0 0 0 1px #E6DFD1, 2px 3px 0 0 rgba(22,35,38,.05), 12px 18px 30px -18px rgba(22,35,38,.30); }
.keep__col--kept li:nth-child(3n+1) { --tilt: -1.3deg; }
.keep__col--kept li:nth-child(3n+2) { --tilt: .8deg; }
.keep__col--kept li:nth-child(3n)   { --tilt: -.5deg; }
.keep__col--kept li::before { /* the punched hole */
  content: ""; position: absolute; top: 16px; left: 24px; width: 13px; height: 13px; border-radius: 50%;
  background: #F3EFE6; box-shadow: inset 0 1px 2px rgba(22,35,38,.25), 0 0 0 2px var(--champagne); }
.keep__col--kept li::after { /* the string, out through the top */
  content: ""; position: absolute; top: -18px; left: 29px; width: 2px; height: 41px; border-radius: 2px;
  background: var(--champagne); rotate: -14deg; transform-origin: bottom center; }
.keep__col--kept .keep__no { position: absolute; top: 16px; right: 1.3rem; color: var(--champagne-ink); }
.keep__col--kept li > b { color: var(--deep-haus); }
.keep__col--kept li > span { color: #3E4746; }

/* the spec cards */
.keep__col--repl li { background: #1F3236; box-shadow: inset 0 0 0 1px rgba(255,255,255,.08), 0 18px 30px -20px rgba(0,0,0,.6); }
.keep__col--repl .keep__no { color: var(--champagne); }
.keep__col--repl li > b { color: #fff !important; }
.keep__col--repl li > span { color: rgba(255,255,255,.74) !important; flex: 1; }
.keep__spec { align-self: flex-start; margin-top: .35rem; padding: .38rem .8rem; border-radius: var(--pill);
  font-style: normal; font-weight: 600; font-size: .8rem; background: rgba(107,160,222,.16); color: #B5D0F1;
  box-shadow: inset 0 0 0 1px rgba(107,160,222,.35); }

/* motion: the tags swing in from the hole, the spec cards rise */
.rise-live .keep__col--kept li.rise-i { translate: none; rotate: -9deg; }
.rise-live .keep__col--kept li.rise-i.is-in { rotate: var(--tilt, 0deg);
  transition: opacity .5s ease var(--rd, 0s), rotate 1.1s cubic-bezier(.3,1.6,.5,1) var(--rd, 0s); }

@media (max-width: 1000px) { .keep--tags ul { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px) {
  .keep--tags ul { grid-template-columns: 1fr; }
  .keep--tags .keep__col { border-radius: 22px; }
  .keep__col--kept li:nth-child(n) { --tilt: 0deg; }
  .keep__col--kept li:nth-child(odd) { --tilt: -.8deg; }
  .keep__col--kept li:nth-child(even) { --tilt: .6deg; }
}

/* a quote printed in two parts; a source link in the credit line */
.qband__q p + p { margin-top: .7em; }
.qband__who a { color: inherit; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }
.qband__who a:hover { color: #fff; }

/* ======================================================================
   NAV DROPDOWNS (built by site.js): Services, Projects, About
   Desktop: a light card hangs under the bar, opened by hover or by the
   caret. It is near-opaque because the bar's own backdrop-filter stops a
   nested one from blurring the page. Phone menu: the caret opens the
   sub-links in place, under their name.
   ====================================================================== */
.nav__item { position: relative; display: flex; align-items: center; gap: .15rem; }
.nav__caret {
  display: grid; place-items: center; width: 20px; height: 20px; padding: 0; margin-right: -.35rem;
  border: 0; border-radius: 50%; background: none; color: inherit; cursor: pointer;
  color: rgba(255,255,255,.7); transition: rotate .25s ease, color .2s ease, background .2s ease;
}
.nav__caret svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.is-scrolled .nav__caret { color: var(--muted); }
.nav__item:hover .nav__caret, .nav__item.is-open .nav__caret { color: var(--haus-blue); }
.is-scrolled .nav__item:hover .nav__caret, .is-scrolled .nav__item.is-open .nav__caret { color: var(--blue-ink); }
.nav__item.is-open .nav__caret { rotate: 180deg; }
.nav__caret:focus-visible { outline: 2px solid var(--haus-blue); outline-offset: 2px; }
.nav__item.is-open > .nav__link::after { scale: 1 1; }

.navdd {
  position: absolute; top: calc(100% + 22px); left: 50%; z-index: 5;
  width: max-content; min-width: 300px; padding: .55rem;
  border-radius: 22px; background: #fff;
  border: 1px solid rgba(22,35,38,.08);
  box-shadow: 0 1px 0 #fff inset, 0 30px 60px -24px rgba(22,35,38,.45), 0 6px 16px -8px rgba(22,35,38,.18);
  opacity: 0; visibility: hidden; translate: -50% 8px;
  transition: opacity .2s ease, translate .25s cubic-bezier(.2,.7,.3,1), visibility 0s .25s;
}
.navdd::before { /* a bridge over the gap, so the pointer can travel down to the card */
  content: ""; position: absolute; left: 0; right: 0; bottom: 100%; height: 26px; }
.nav__item.is-open .navdd { opacity: 1; visibility: visible; translate: -50% 0; transition-delay: 0s; }

.navdd__in { display: grid; gap: .15rem; }
.navdd--svc .navdd__in { grid-template-columns: repeat(2, minmax(0, 260px)); }
.navdd__link {
  display: flex; align-items: center; gap: .8rem; padding: .65rem .75rem; border-radius: 14px;
  text-decoration: none; color: var(--deep-haus); transition: background .18s ease;
}
.navdd__link:hover, .navdd__link:focus-visible { background: #EEF3FA; outline: none; }
.navdd__link[aria-current="page"] { background: #F3EFE6; }
.navdd__ico {
  flex: none; display: grid; place-items: center; width: 42px; height: 42px; border-radius: 12px;
  background: linear-gradient(155deg, rgba(107,160,222,.22) 0%, rgba(107,160,222,.08) 100%);
  border: 1px solid rgba(107,160,222,.30); transition: translate .25s ease;
}
.navdd__ico svg { width: 24px; height: 24px; fill: none; stroke: var(--blue-ink); stroke-width: 1.3; stroke-linecap: round; stroke-linejoin: round; }
.navdd__link:hover .navdd__ico { translate: 0 -2px; }
.navdd__thumb { flex: none; width: 64px; height: 48px; border-radius: 10px; object-fit: cover; box-shadow: 0 0 0 1px rgba(22,35,38,.08); }
.navdd__txt { display: grid; gap: .1rem; }
.navdd__txt b { font-size: .92rem; font-weight: 600; letter-spacing: -.01em; }
.navdd__txt small { font-size: .8rem; color: var(--muted); }
.navdd__all {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  margin-top: .4rem; padding: .75rem .9rem; border-radius: 14px;
  background: var(--deep-haus); color: #fff; text-decoration: none; font-size: .86rem; font-weight: 600;
  transition: background .2s ease;
}
.navdd__all span { transition: translate .2s ease; }
.navdd__all:hover { background: #22363A; }
.navdd__all:hover span { translate: 3px 0; }

/* the phone menu: sub-links open in place under their name */
@media (max-width: 1040px) {
  .nav__item { flex-wrap: wrap; justify-content: space-between; }
  .nav__item > .nav__link { flex: 1; }
  .nav__caret { width: 40px; height: 40px; margin: 0; }
  .nav__caret svg { width: 14px; height: 14px; }
  .navdd {
    position: static; flex-basis: 100%; width: auto; min-width: 0; translate: none;
    padding: 0; border: 0; border-radius: 0; background: none; box-shadow: none;
    display: none; opacity: 1; visibility: visible;
  }
  .navdd::before { display: none; }
  .nav__item.is-open .navdd { display: block; translate: none; }
  .navdd--svc .navdd__in { grid-template-columns: 1fr; }
  .navdd__in { padding: .2rem 0 .5rem .4rem; border-left: 2px solid rgba(107,160,222,.45); margin-left: .2rem; }
  .navdd__link { padding: .5rem .6rem; color: #fff; }
  .navdd__link:hover, .navdd__link:focus-visible { background: rgba(255,255,255,.08); }
  .navdd__link[aria-current="page"] { background: rgba(255,255,255,.10); }
  .navdd__txt small { color: rgba(255,255,255,.62); }
  .navdd__ico { width: 34px; height: 34px; border-radius: 10px; background: rgba(255,255,255,.9); }
  .navdd__ico svg { width: 20px; height: 20px; }
  .navdd__thumb { width: 48px; height: 36px; }
  .navdd__all { display: none; }
  .is-scrolled .navdd__link { color: var(--deep-haus); }
  .is-scrolled .navdd__link:hover { background: #EEF3FA; }
  .is-scrolled .navdd__txt small { color: var(--muted); }
}
@media (prefers-reduced-motion: reduce) { .navdd, .nav__caret, .navdd__ico { transition: none; } }

/* anchor targets reached from the menus clear the floating bar */
#gibbs, #nautilus, #paradise, #team-h, #reviews-h, #process, #quality { scroll-margin-top: clamp(96px, 10vw, 124px); }

/* The phone menu sits inside the glass bar, whose own backdrop-filter
   stops this one from blurring the page; with sub-links open it grows
   over the hero text, so it needs a near-solid ground to stay legible. */
@media (max-width: 1040px) {
  .nav { background: linear-gradient(180deg, rgba(255,255,255,.06) 0%, rgba(255,255,255,0) 100%), #162326; }
  .is-scrolled .nav { background: #FAF9F6; }
  .nav { max-height: calc(100vh - 110px); max-height: calc(100dvh - 110px); overflow-y: auto; overscroll-behavior: contain; }
}

/* ---------- sections float in (every page; site.js adds .rv) ----------
   A short rise and fade, once. Only translate and opacity move, and both
   are cleared on arrival, so pinned and sticky sections are unaffected. */
.rv { opacity: 0; translate: 0 28px; }
.rv.is-in { opacity: 1; translate: none;
  transition: opacity .7s ease var(--rv, 0s), translate .9s cubic-bezier(.2,.7,.2,1) var(--rv, 0s); }
@media (prefers-reduced-motion: reduce) { .rv { opacity: 1; translate: none; } }

/* ---------- services hub hero: plain words + a four-photo mosaic ----------
   One photograph per service, each a link into its page. The right
   column sits a step lower than the left so the four read as a set
   rather than a grid of thumbnails. */
.svh--hub .svh__grid { display: grid; grid-template-columns: minmax(0, .95fr) minmax(0, 1.05fr); gap: clamp(2rem, 5vw, 4.5rem); align-items: center; }
.svh--hub .svh__title { font-size: clamp(2.8rem, 1.8rem + 4vw, 5.2rem); }
.svh__mosaic { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(.75rem, 1.4vw, 1.1rem); padding-bottom: 1.8rem; }
.svh__tile {
  position: relative; display: block; overflow: hidden; aspect-ratio: 5 / 4;
  border-radius: 22px; border: 3px solid #fff; text-decoration: none;
  box-shadow: 0 0 0 1px rgba(207,202,193,.9), 14px 20px 34px -18px rgba(22,35,38,.42);
  transition: translate .35s cubic-bezier(.2,.7,.3,1), box-shadow .35s ease;
}
.svh__tile:nth-child(even) { translate: 0 1.8rem; }
.svh__tile img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: scale .6s cubic-bezier(.2,.7,.3,1); }
.svh__tile::after { content: ""; position: absolute; inset: 45% 0 0; background: linear-gradient(180deg, rgba(16,25,28,0) 0%, rgba(16,25,28,.55) 100%); pointer-events: none; }
.svh__tag {
  position: absolute; left: .7rem; right: .7rem; bottom: .7rem; z-index: 1;
  display: flex; align-items: center; justify-content: space-between; gap: .6rem;
  padding: .55rem .55rem .55rem .95rem; border-radius: 999px;
  background: rgba(255,255,255,.94); color: var(--deep-haus); font-size: .88rem; font-weight: 600; letter-spacing: -.01em;
  box-shadow: 0 8px 18px -10px rgba(16,25,28,.6);
}
.svh__tag i { flex: none; display: grid; place-items: center; width: 28px; height: 28px; border-radius: 50%;
  background: var(--deep-haus); color: #fff; font-style: normal; font-size: .82rem; transition: background .25s ease, rotate .25s ease, color .25s ease; }
.svh__tile:hover { box-shadow: 0 0 0 1px rgba(207,202,193,.9), 20px 30px 46px -20px rgba(22,35,38,.5); }
.svh__tile:hover img { scale: 1.06; }
.svh__tile:hover .svh__tag i, .svh__tile:focus-visible .svh__tag i { background: var(--haus-blue); color: var(--deep-haus); rotate: 45deg; }
.svh__tile:nth-child(odd):hover { translate: 0 -4px; }
.svh__tile:nth-child(even):hover { translate: 0 calc(1.8rem - 4px); }
.svh__tile:focus-visible { outline: 2px solid var(--haus-blue); outline-offset: 3px; }
@media (max-width: 900px) {
  .svh--hub .svh__grid { grid-template-columns: 1fr; }
  .svh__mosaic { padding-bottom: 0; }
  .svh__tile { aspect-ratio: 1 / 1; border-radius: 18px; }
  .svh__tile:nth-child(even), .svh__tile:nth-child(even):hover, .svh__tile:nth-child(odd):hover { translate: none; }
  .svh__tag { font-size: .8rem; left: .5rem; right: .5rem; bottom: .5rem; padding-left: .75rem; }
  .svh__tag i { width: 24px; height: 24px; }
}
@media (prefers-reduced-motion: reduce) { .svh__tile, .svh__tile img { transition: none; } }
/* the Services menu: How We Work sits apart from the four, on a ruled row */
/* a plain centred text link, deliberately unlike the four service rows */
.navdd__extra { justify-content: center; text-align: center; margin-top: .35rem; padding: .85rem .75rem .7rem;
  border-top: 1px solid rgba(22,35,38,.08); border-radius: 0 0 14px 14px; }
.navdd__extra .navdd__txt { justify-items: center; }
.navdd__extra b { color: var(--blue-ink); }
.navdd__extra b span { display: inline-block; transition: translate .2s ease; }
.navdd__extra:hover { background: none; }
.navdd__extra:hover b span { translate: 3px 0; }
/* the closing bar in every menu: label centred, arrow beside it */
.navdd__all { justify-content: center; gap: .6rem; }
@media (max-width: 1040px) { .navdd__extra { justify-content: flex-start; text-align: left; margin: 0 0 .5rem .6rem; border-top-color: rgba(255,255,255,.12); } .navdd__extra .navdd__txt { justify-items: start; } .navdd__extra b { color: #A9C8EE; } .is-scrolled .navdd__extra { border-top-color: rgba(22,35,38,.08); } .is-scrolled .navdd__extra b { color: var(--blue-ink); } }

/* ---------- services hub hero, filled out ----------
   A warm ground across the hero; a dark plate behind the mosaic gives
   the photographs depth and contrast; the left column carries a kicker,
   the two actions and the three figures as numbers, not pills. */
.svh--hub {
  position: relative; overflow: hidden;
  padding-top: clamp(6.2rem, 9vw, 7.4rem); padding-bottom: clamp(3rem, 5vw, 4.6rem);
  background:
    radial-gradient(60rem 30rem at 0% 0%, rgba(200,181,138,.18), transparent 70%),
    linear-gradient(180deg, #F7F4EE 0%, #FFFFFF 100%);
}
.svh--hub .svh__copy { gap: 1.25rem; }
.svh__kicker { display: inline-flex; align-items: center; gap: .6rem; justify-self: start;
  padding: .42rem .9rem; border-radius: 999px; background: #fff; box-shadow: inset 0 0 0 1px #E6DFD1;
  font-size: .82rem; font-weight: 600; color: var(--deep-haus); }
.svh__kicker span { width: 7px; height: 7px; border-radius: 50%; background: var(--champagne); box-shadow: 0 0 0 3px rgba(200,181,138,.25); }
.svh__lede { max-width: 46ch; color: #4D5655; }
.svh__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 1.1rem 1.6rem; margin-top: .3rem; }
.svh__stats { display: grid; grid-template-columns: repeat(3, auto); justify-content: start; gap: 0; margin: 1rem 0 0; padding-top: 1.4rem; border-top: 1px solid #E2DBCD; }
.svh__stats > div { display: flex; flex-direction: column-reverse; gap: .2rem; padding-right: clamp(1.2rem, 2.6vw, 2.2rem); margin-right: clamp(1.2rem, 2.6vw, 2.2rem); border-right: 1px solid #E2DBCD; }
.svh__stats > div:last-child { border-right: 0; margin-right: 0; padding-right: 0; }
.svh__stats dd { margin: 0; font-size: clamp(1.7rem, 1.3rem + 1.3vw, 2.4rem); font-weight: 700; letter-spacing: -.03em; line-height: 1; color: var(--deep-haus); }
.svh__stats dt { font-size: .82rem; color: var(--muted); }

/* the plate behind the mosaic */
.svh--hub .svh__mosaic { position: relative; padding: clamp(1rem, 2vw, 1.6rem); padding-bottom: calc(clamp(1rem, 2vw, 1.6rem) + 1.8rem); }
.svh--hub .svh__mosaic::before {
  content: ""; position: absolute; inset: 0; z-index: -1; border-radius: 32px;
  background:
    radial-gradient(26rem 18rem at 85% 0%, rgba(107,160,222,.28), transparent 70%),
    var(--deep-haus);
  box-shadow: 24px 34px 70px -30px rgba(22,35,38,.6);
}
.svh--hub .svh__grid { position: relative; z-index: 0; }
.svh--hub .svh__tile { border-color: rgba(255,255,255,.9); box-shadow: 0 18px 30px -18px rgba(0,0,0,.6); }
.svh--hub .svh__tile:hover { box-shadow: 0 26px 40px -18px rgba(0,0,0,.7); }

@media (max-width: 900px) {
  .svh--hub .svh__mosaic { padding: .8rem; }
  .svh--hub .svh__mosaic::before { border-radius: 24px; }
  .svh__stats { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .svh__stats > div { padding-right: .8rem; margin-right: .8rem; }
  .svh__stats dt { font-size: .74rem; }
}

/* hub: the pinned picture is sized to the screen, not the column, and
   centred in the space under the nav bar, so it never runs off the
   bottom on a short screen and sits level with the card being read */
@media (min-width: 901px) {
  .hub { align-items: stretch; }
  .hub__pics {
    --ph: min(calc(100vh - 190px), 640px);
    --ph: min(calc(100svh - 190px), 640px);
    aspect-ratio: auto; height: var(--ph);
    top: calc(50vh + 42px - var(--ph) / 2);
    top: calc(50svh + 42px - var(--ph) / 2);
    align-self: start;
  }
}

/* ---------- restoration: then / today ----------
   Two photographs of Gibbs Cottage 5 side by side at one shape. The old
   one keeps its paper tone; each carries a plain label. */
.rp-then { margin: 0; }
.rp-then__pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(.75rem, 1.6vw, 1.25rem); }
.rp-then__ph { position: relative; aspect-ratio: 4 / 3; margin: 0; }
.rp-then__ph img { width: 100%; height: 100%; object-fit: cover; }
.rp-then__ph:first-child img { filter: sepia(.18) contrast(1.02); }
.rp-then__tag {
  position: absolute; left: .9rem; top: .9rem; padding: .35rem .85rem; border-radius: 999px;
  background: rgba(255,255,255,.92); color: var(--deep-haus); font-size: .8rem; font-weight: 700; letter-spacing: .02em;
  box-shadow: 0 6px 14px -8px rgba(16,25,28,.5);
}
.rp-then__tag--now { background: var(--deep-haus); color: #fff; }
@media (max-width: 640px) { .rp-then__pair { grid-template-columns: 1fr; } }

/* nothing waiting to slide in from the side may widen the page on a
   phone; clip (not hidden) so sticky and pinned sections still work */
main { overflow-x: clip; }

/* =====================================================================
   PROJECT PAGES (/projects/ and /projects/<group>/)
   Built from the services vocabulary: facts strip, dated build timeline,
   photo mosaic, house blocks and the "now building" band.
   ===================================================================== */
.pjf { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr)); margin: 0;
  border-block: 1px solid var(--border-gray); }
.pjf > div { padding: 1rem 1.1rem 1.05rem 0; }
.pjf > div + div { border-left: 1px solid var(--border-gray); padding-left: 1.1rem; }
.pjf dt { font-family: var(--mono); font-size: .68rem; letter-spacing: .1em; text-transform: uppercase; color: var(--taupe); margin-bottom: .35rem; }
.pjf dd { margin: 0; font-weight: 700; color: var(--deep-haus); line-height: 1.35; }
@media (max-width: 620px) {
  .pjf { grid-template-columns: 1fr 1fr; }
  .pjf > div, .pjf > div + div { padding: .85rem .8rem .85rem 0; border-left: 0; }
  .pjf > div:nth-child(n+3) { border-top: 1px solid var(--border-gray); }
  .pjf > div:nth-child(even) { padding-left: .9rem; border-left: 1px solid var(--border-gray); }
}

/* the hero facts card reuses .proof; its number can be a word */
.proof__num--sm { font-size: clamp(1.6rem, 1.2rem + 1.4vw, 2.2rem); }

/* the story: copy beside a stacked pair of photographs */
.pjstory__ph { display: grid; gap: clamp(.75rem, 1.6vw, 1.1rem); margin: 0; }
.pjstory__ph img { width: 100%; border-radius: var(--radius); }
.pjstory__ph figcaption { font-size: .78rem; color: var(--taupe); font-weight: 600; letter-spacing: .02em; }
.pjstory__ph--old img { filter: sepia(.15) contrast(1.02); }

/* the work: numbered items, two columns on wide screens */
.pjwork { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); gap: clamp(1rem, 2vw, 1.4rem); list-style: none; padding: 0; margin: 0; counter-reset: pjw; }
.pjwork li { position: relative; padding: 1.3rem 1.35rem 1.35rem 3.6rem; border-radius: var(--radius); background: var(--soft-white); border: 1px solid #ECE8E1; counter-increment: pjw; }
.pjwork li::before { content: counter(pjw, decimal-leading-zero); position: absolute; left: 1.2rem; top: 1.3rem; font-family: var(--mono); font-size: .8rem; color: var(--champagne-ink); }
.pjwork b { display: block; color: var(--deep-haus); font-size: 1.05rem; letter-spacing: -.01em; margin-bottom: .3rem; }
.pjwork span { color: var(--muted); font-size: .95rem; line-height: 1.55; }

/* dated build timeline */
.pjt { list-style: none; margin: 0; padding: 0; position: relative; }
.pjt::before { content: ""; position: absolute; left: 7px; top: .6rem; bottom: .6rem; width: 2px; background: linear-gradient(var(--champagne), var(--haus-blue)); opacity: .55; }
.pjt__item { position: relative; display: grid; grid-template-columns: 9.5rem minmax(0, 1fr) minmax(0, 1.05fr); gap: clamp(1rem, 2.6vw, 2.4rem); align-items: start; padding: 0 0 clamp(1.6rem, 3.4vw, 2.6rem) 2.2rem; }
.pjt__item::before { content: ""; position: absolute; left: 0; top: .35rem; width: 16px; height: 16px; border-radius: 50%; background: #fff; border: 3px solid var(--champagne); box-sizing: border-box; }
.pjt__item:last-child { padding-bottom: 0; }
.pjt__item--done::before { background: var(--haus-blue); border-color: var(--haus-blue); }
.pjt__date { margin: 0; font-family: var(--mono); font-size: .8rem; letter-spacing: .06em; color: var(--champagne-ink); padding-top: .15rem; }
.pjt__t { margin: 0 0 .4rem; font-size: 1.15rem; font-weight: 800; letter-spacing: -.02em; color: var(--deep-haus); }
.pjt__d { margin: 0; color: var(--muted); line-height: 1.6; }
.pjt__ph { margin: 0; border-radius: 14px; overflow: hidden; aspect-ratio: 3 / 2; background: var(--warm-stone); }
.pjt__ph img { width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 860px) {
  .pjt__item { grid-template-columns: minmax(0, 1fr); gap: .55rem; }
  .pjt__ph { margin-top: .5rem; }
}

/* photo mosaic */
.pjm { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); grid-auto-rows: clamp(150px, 17vw, 230px); gap: clamp(.6rem, 1.3vw, 1rem); margin: 0; }
.pjm__i { position: relative; margin: 0; border-radius: 14px; overflow: hidden; background: var(--warm-stone); }
.pjm__i img { width: 100%; height: 100%; object-fit: cover; transition: scale .6s ease; }
.pjm__i:hover img { scale: 1.035; }
.pjm__i--lead { grid-column: span 2; grid-row: span 2; }
.pjm__i--tall { grid-row: span 2; }
.pjm__i--wide { grid-column: span 2; }
.pjm__i figcaption { position: absolute; left: .6rem; bottom: .6rem; width: fit-content; max-width: calc(100% - 1.2rem);
  padding: .32rem .6rem; border-radius: 8px; background: rgba(22,35,38,.72); color: #fff; font-size: .74rem; font-weight: 600; line-height: 1.35;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
@media (max-width: 760px) {
  .pjm { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-rows: 42vw; }
  .pjm__i--tall { grid-row: span 1; }
}

/* one house inside a group page */
.pjh { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); gap: clamp(1.25rem, 3.5vw, 3rem); align-items: center; scroll-margin-top: 6rem; }
.pjh + .pjh { margin-top: clamp(2.5rem, 5vw, 4rem); }
.pjh--flip > .pjh__ph { order: 2; }
.pjh__ph { margin: 0; border-radius: var(--radius); overflow: hidden; aspect-ratio: 4 / 3; }
.pjh__ph img { width: 100%; height: 100%; object-fit: cover; }
.pjh__kind { margin: 0; font-family: var(--mono); font-size: .72rem; letter-spacing: .1em; text-transform: uppercase; color: var(--blue-ink); }
@media (max-width: 820px) { .pjh { grid-template-columns: 1fr; } .pjh--flip > .pjh__ph { order: 0; } }

/* cards on the index link to their story */
a.pj-card { display: block; color: inherit; text-decoration: none; }
.pj-card__hook { margin: .15rem 0 .4rem; font-size: .95rem; color: var(--charcoal); line-height: 1.45; }
.pj-card__go { position: absolute; right: .75rem; bottom: .75rem; width: 2.25rem; height: 2.25rem; border-radius: 50%; display: grid; place-items: center;
  background: var(--deep-haus); color: #fff; transition: background .25s ease, rotate .25s ease; }
.pj-card__go svg { width: 1rem; height: 1rem; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.pj-card:hover .pj-card__go { background: var(--blue-ink); rotate: 45deg; }
.pj-card__media { aspect-ratio: 4 / 3; }
.pj-card__media img { width: 100%; height: 100%; object-fit: cover; }
.pj-group__head { flex-wrap: wrap; }
.pj-group__why { flex-basis: 100%; margin: .2rem 0 0; color: var(--muted); max-width: 62ch; }
.pj-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 1100px) { .pj-grid--4 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 960px) { .pj-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 600px) { .pj-grid--4 { grid-template-columns: 1fr; } }

/* the build going up now */
.pjnow { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); border-radius: calc(var(--radius) + 6px); overflow: hidden; background: var(--deep-haus); color: #E8ECEC; text-decoration: none; }
.pjnow__ph { position: relative; margin: 0; min-height: 320px; }
.pjnow__ph img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.pjnow__body { padding: clamp(1.5rem, 3.5vw, 2.75rem); display: flex; flex-direction: column; gap: 1rem; }
.pjnow__live { display: inline-flex; align-items: center; gap: .55rem; width: fit-content; padding: .35rem .75rem; border-radius: var(--pill); background: rgba(255,255,255,.08);
  font-family: var(--mono); font-size: .7rem; letter-spacing: .1em; text-transform: uppercase; color: #fff; margin: 0; }
.pjnow__live::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: #5BD08A; box-shadow: 0 0 0 0 rgba(91,208,138,.6); animation: pjpulse 2s infinite; }
@keyframes pjpulse { 70% { box-shadow: 0 0 0 9px rgba(91,208,138,0); } 100% { box-shadow: 0 0 0 0 rgba(91,208,138,0); } }
@media (prefers-reduced-motion: reduce) { .pjnow__live::before { animation: none; } }
.pjnow__t { margin: 0; font-size: var(--step-2); font-weight: 800; letter-spacing: -.03em; color: #fff; line-height: 1.1; }
.pjnow__d { margin: 0; color: #B9C3C4; line-height: 1.6; }
.pjnow__steps { list-style: none; margin: 0; padding: 0; display: grid; gap: .55rem; }
.pjnow__steps li { display: grid; grid-template-columns: 5.5rem 1fr; gap: .75rem; font-size: .92rem; color: #DDE3E3; }
.pjnow__steps time { font-family: var(--mono); font-size: .74rem; color: var(--champagne); padding-top: .12rem; }
.pjnow__go { margin-top: auto; color: #fff; font-weight: 700; }
@media (max-width: 820px) { .pjnow { grid-template-columns: 1fr; } .pjnow__ph { min-height: 240px; } }

/* homepage service rows now link to their pages; the On the record slides
   cite the South Jetty */
.svc__panel p + p { margin-top: .85rem; }
.tmnl__place cite { font-style: normal; }
.tmnl__place a, .rv-q__text a, .sec-head__aside a { color: var(--blue-ink); text-decoration: underline; text-underline-offset: 2px; }
/* Quality runs on 240 Nautilus framed against a clear sky (3 Mar 2023):
   "here is the part you cannot see", shown before it is covered. The
   tiles keep the light/dark alternation, but both are near-solid frosted
   glass with a thin edge, so the copy is high-contrast on every tile and
   the framing still reads through the gaps and the blur. */
#quality.section--photo { background-image: url("../img/quality-frame.webp"); background-position: 50% 45%; }
#quality.section--photo::before {
  background: linear-gradient(180deg, rgba(10,16,20,.78) 0%, rgba(10,16,20,.45) 45%, rgba(10,16,20,.70) 100%);
}
#quality .qtile {
  min-height: clamp(280px, 22vw, 340px);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
          backdrop-filter: blur(18px) saturate(150%);
}
#quality .qtile--light {
  background: rgba(250,249,246,.90); color: var(--deep-haus);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.7);
}
#quality .qtile--dark {
  background: rgba(14,24,28,.66); color: #fff;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.16), inset 0 1px 0 rgba(255,255,255,.22);
}
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  #quality .qtile--light { background: rgba(250,249,246,.96); }
  #quality .qtile--dark  { background: rgba(14,24,28,.92); }
}
#quality .qtile__num { color: transparent; font-size: clamp(2.6rem, 4vw, 4rem); }
#quality .qtile--light .qtile__num { -webkit-text-stroke: 1.5px rgba(22,35,38,.42); }
#quality .qtile--dark  .qtile__num { -webkit-text-stroke: 1.5px rgba(214,190,140,.75); }
#quality .qtile--light .qtile__title { color: var(--deep-haus); }
#quality .qtile--dark  .qtile__title { color: #fff; }
#quality .qtile--light .qtile__text { color: rgba(22,35,38,.84); }
#quality .qtile--dark  .qtile__text { color: rgba(255,255,255,.86); }
@media (max-width: 560px) { #quality .qtile { min-height: 0; padding-top: 4.2rem; } }

@media (max-width: 1100px) {
  .positioning-photo { background-image: url("../img/section-bg-2.webp"); background-size: cover; background-position: 62% 40%; }
}
