/* ==========================================================================
   THORNE: Line and Shadow
   High contrast graphic. Heavy ink blocks against cool paper, oversized
   condensed type, cedar as a single restrained accent.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

:root {
  --bg:      #EDEDEA;
  --ink:     #111312;
  --cedar:   #A9713F;
  --muted:   #5E625F;

  /* Accessible pairings of the same two notes. Cedar at #A9713F clears AA on
     ink (4.55) and at large sizes, but only reaches 3.5 on paper, so small
     cedar text on paper uses the deepened value (4.78). Same for the grey:
     one value cannot clear 4.5 against both grounds. */
  --cedar-deep: #8F5C31;   /* small cedar text on paper */
  --muted-ink:  #909590;   /* supporting text on ink */

  --ink-2:   #1B1E1C;   /* raised surface on ink ground */
  --ink-3:   #2A2E2B;   /* hairline on ink ground */
  --paper-2: #E3E3DF;   /* raised surface on paper ground */
  --paper-3: #D2D2CC;   /* hairline on paper ground */

  /* Type. Big jumps only, nothing muddy in the middle. */
  --fs-display: clamp(3.75rem, 14vw, 13rem);
  --fs-h2:      clamp(2.5rem, 7.5vw, 6.75rem);
  --fs-h3:      clamp(1.25rem, 1.6vw, 1.6rem);
  --fs-lead:    clamp(1.125rem, 1.8vw, 1.5rem);
  --fs-body:    clamp(1rem, 1.05vw, 1.0625rem);
  --fs-small:   0.8125rem;
  --fs-eyebrow: 0.6875rem;

  --font-display: 'Anton', 'Arial Narrow', 'Haettenschweiler', Impact, sans-serif;
  --font-body: 'Archivo', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;

  /* Rhythm */
  --pad-x: clamp(1.25rem, 5vw, 6rem);
  --sec-y: clamp(5rem, 13vh, 11rem);
  --maxw: 1440px;
  --measure: 62ch;

  /* Shadow sweep. Unitless so GSAP can interpolate them cleanly,
     units get composed with calc() at the point of use. */
  --sweep-angle: -20;    /* degrees, morning light low from the east */
  --sweep-shift: -12;    /* percent */
  --sweep-scale: 1.3;    /* shadow length, long at the ends of the day */
  --sweep-opacity: 0.34;
  --slat: clamp(88px, 11vw, 190px);
}

/* Typed custom properties interpolate correctly and stay off the main thread
   where the browser supports it. Harmless where it does not. */
@supports (background: paint(id)) {}
@property --sweep-angle   { syntax: '<number>'; inherits: true; initial-value: -20; }
@property --sweep-shift   { syntax: '<number>'; inherits: true; initial-value: -12; }
@property --sweep-scale   { syntax: '<number>'; inherits: true; initial-value: 1.3; }
@property --sweep-opacity { syntax: '<number>'; inherits: true; initial-value: 0.34; }

/* --------------------------------------------------------------------------
   2. Base
   -------------------------------------------------------------------------- */

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

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

a { color: inherit; }

button, input, select, textarea { font: inherit; color: inherit; }

::selection { background: var(--cedar); color: var(--bg); }

.skip {
  position: absolute; top: -100px; left: 0; z-index: 200;
  background: var(--ink); color: var(--bg);
  padding: 0.9rem 1.4rem; text-decoration: none;
  font-weight: 600; letter-spacing: 0.02em;
}
.skip:focus { top: 0; }

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

.section--ink :focus-visible { outline-color: var(--cedar); }

/* --------------------------------------------------------------------------
   3. Typography
   -------------------------------------------------------------------------- */

.display {
  font-family: var(--font-display);
  font-size: var(--fs-display);
  line-height: 0.82;
  letter-spacing: -0.015em;
  text-transform: uppercase;
  margin: 0;
  font-weight: 400;
}

.h2 {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  line-height: 0.9;
  letter-spacing: -0.012em;
  text-transform: uppercase;
  margin: 0;
  font-weight: 400;
}

.h3 {
  font-family: var(--font-body);
  font-size: var(--fs-h3);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.005em;
  margin: 0;
}

.eyebrow {
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.24em;
  color: var(--cedar-deep);
  margin: 0 0 1.5rem;
}

.lead {
  font-size: var(--fs-lead);
  line-height: 1.45;
  max-width: var(--measure);
  margin: 0;
}

.body { max-width: var(--measure); margin: 0; }

.num {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--cedar);
  letter-spacing: 0;
  line-height: 1;
}

/* --------------------------------------------------------------------------
   4. THE SHADOW SWEEP
   Raking light through slats, tracking across the page like the sun across
   a day. Sits above section backgrounds and imagery but always below text,
   so it can never pull text contrast down.
   -------------------------------------------------------------------------- */

.sweep {
  position: absolute;
  inset: -70%;
  z-index: 1;
  pointer-events: none;
  opacity: var(--sweep-opacity);
  mix-blend-mode: soft-light;
  /* Extra intermediate stops keep the ramp gentle. Near black there are very
     few 8 bit levels to work with, so every hard stop becomes a visible
     contour once the layer animates. */
  background-image: repeating-linear-gradient(
    90deg,
    rgba(0, 0, 0, 0.95)       0,
    rgba(0, 0, 0, 0.78)       calc(var(--slat) * 0.10),
    rgba(0, 0, 0, 0.52)       calc(var(--slat) * 0.22),
    rgba(0, 0, 0, 0.24)       calc(var(--slat) * 0.32),
    rgba(0, 0, 0, 0)          calc(var(--slat) * 0.42),
    rgba(255, 255, 255, 0.30) calc(var(--slat) * 0.52),
    rgba(255, 255, 255, 0.62) calc(var(--slat) * 0.63),
    rgba(255, 255, 255, 0.38) calc(var(--slat) * 0.74),
    rgba(255, 255, 255, 0.10) calc(var(--slat) * 0.85),
    rgba(0, 0, 0, 0.42)       calc(var(--slat) * 0.93),
    rgba(0, 0, 0, 0.95)       var(--slat)
  );
  transform:
    rotate(calc(var(--sweep-angle) * 1deg))
    translate3d(calc(var(--sweep-shift) * 1%), 0, 0)
    scaleX(var(--sweep-scale));
  will-change: transform, opacity;
}

/* Dither. Near black, the sweep only has a handful of 8 bit levels to move
   through, so it quantises into hard contour lines that crawl as it animates.
   Fine static grain breaks those contours up. It is deliberately NOT inside
   .sweep: it must stay still while the light moves under it. */
.grain {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0.045;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 160px 160px;
}

/* Ink ground responds differently to soft light than paper does, so the same
   sun needs its own weighting on each to land at the same apparent strength.
   Ink runs stronger than it looks it should: a narrow range on near black
   reads as artefact, a wider one reads as actual raking light. */
.section--ink .sweep,
.footer .sweep { opacity: calc(var(--sweep-opacity) * 1.85); }

.section--ink .grain,
.footer .grain { opacity: 0.07; }

/* The hero already carries real raking light in the footage. */
.hero .sweep { opacity: calc(var(--sweep-opacity) * 0.45); }

/* Over photography the sweep reads as real light, so it can run a touch
   stronger without ever touching type. */
.has-sweep-media .sweep { opacity: calc(var(--sweep-opacity) * 1.15); }

/* --------------------------------------------------------------------------
   5. Slat assembly dividers
   Groups of slats that draw up on scroll, one after another, evenly spaced.
   Crisp and mechanical. Nothing bouncy.
   -------------------------------------------------------------------------- */

.slats {
  display: flex;
  align-items: flex-end;
  gap: clamp(4px, 0.55vw, 10px);
  height: clamp(78px, 10.5vw, 150px);
  width: 100%;
  position: relative;
  z-index: 2;
}

.slats i {
  display: block;
  flex: 1 1 0;
  height: 100%;
  background: var(--ink);
  transform: scaleY(0);
  transform-origin: bottom center;
}

.section--ink .slats i,
.footer .slats i { background: var(--bg); }

/* Single cedar slat, the restrained warm note */
.slats i.is-cedar { background: var(--cedar); }

.slats--top i { transform-origin: top center; }

.slats--rule {
  height: clamp(20px, 2.2vw, 34px);
  opacity: 0.5;
}

/* Divider block wrapping a slat group */
.divider {
  padding: 0 var(--pad-x);
  margin: 0 auto;
  max-width: var(--maxw);
}

/* --------------------------------------------------------------------------
   6. Section shells
   The alternation between solid ink and cool paper IS the identity.
   -------------------------------------------------------------------------- */

.section {
  position: relative;
  padding: var(--sec-y) 0;
  overflow: hidden;
  contain: paint;
}

.section--paper { background: var(--bg); color: var(--ink); }
.section--ink   { background: var(--ink); color: var(--bg); }

.section--ink .eyebrow,
.section--ink .num { color: var(--cedar); }

.wrap {
  position: relative;
  z-index: 2;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--pad-x);
}

.wrap--bleed { max-width: none; padding: 0; }

/* Asymmetric split, deliberately uneven */
.split {
  position: relative;
  z-index: 2;
  display: grid;
  gap: clamp(2.5rem, 5vw, 5.5rem);
  align-items: start;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--pad-x);
}

.split--62 { grid-template-columns: 1fr; }
.split--55 { grid-template-columns: 1fr; }

@media (min-width: 900px) {
  /* Narrowed from 1.62 so the materials image reads bigger. The body copy
     is capped at 48ch and was already far inside the old column, so nothing
     rewraps. */
  .split--62 { grid-template-columns: 1.3fr 1fr; }
  .split--55 { grid-template-columns: 1.15fr 1fr; }
}

.sec-head { margin-bottom: clamp(2.5rem, 5vw, 4.5rem); }
.sec-head .h2 { margin-bottom: 1.5rem; }

/* Reveal primitive. Crisp, short travel, no bounce. */
.rise { opacity: 0; transform: translate3d(0, 22px, 0); }

/* --------------------------------------------------------------------------
   7. Nav
   -------------------------------------------------------------------------- */

.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: clamp(0.9rem, 1.6vw, 1.4rem) var(--pad-x);
  color: var(--bg);
  transition: background-color 0.35s ease, color 0.35s ease, box-shadow 0.35s ease;
}

/* Over the hero the nav sits on live video, and the frame behind it brightens
   as the build runs to golden hour. This guarantees the links stay legible
   at every scrub position, then clears once the solid bar takes over. */
.nav::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(to bottom, rgba(17, 19, 18, 0.72), rgba(17, 19, 18, 0));
  transition: opacity 0.35s ease;
}

.nav.is-stuck {
  background: var(--ink);
  box-shadow: 0 1px 0 var(--ink-3);
}

.nav.is-stuck::before { opacity: 0; }

.nav__brand {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  text-decoration: none;
  flex: none;
}

.nav__mark { display: block; width: 22px; height: 26px; flex: none; }

.nav__word {
  font-family: var(--font-display);
  font-size: 1.4rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  line-height: 1;
}

.nav__links {
  display: none;
  gap: clamp(1.25rem, 2.4vw, 2.5rem);
  list-style: none;
  margin: 0; padding: 0;
}

@media (min-width: 900px) { .nav__links { display: flex; } }

.nav__links a {
  text-decoration: none;
  font-size: var(--fs-small);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  position: relative;
  padding-bottom: 3px;
}

.nav__links a::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 2px;
  background: var(--cedar);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.nav__links a:hover::after,
.nav__links a:focus-visible::after { transform: scaleX(1); }

/* --------------------------------------------------------------------------
   8. Buttons
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  padding: 1.05rem 1.9rem;
  border: 2px solid transparent;
  background: var(--cedar);
  color: var(--ink);
  text-decoration: none;
  font-weight: 700;
  font-size: var(--fs-small);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color 0.25s ease, color 0.25s ease, border-color 0.25s ease;
}

.btn:hover { background: var(--ink); color: var(--bg); }
.section--ink .btn:hover,
.hero .btn:hover { background: var(--bg); color: var(--ink); }

.btn--ghost {
  background: transparent;
  border-color: currentColor;
  color: inherit;
}
.btn--ghost:hover { background: var(--bg); color: var(--ink); border-color: var(--bg); }

.btn__arrow { width: 14px; height: 10px; flex: none; }

/* The nav CTA has to stay on one line at 375, where the full label would
   otherwise wrap and swallow the header. */
.nav .btn { white-space: nowrap; }

@media (max-width: 620px) {
  .nav .btn {
    padding: 0.7rem 0.95rem;
    font-size: 0.625rem;
    letter-spacing: 0.1em;
    gap: 0.5rem;
  }
  .nav .btn .btn__arrow { display: none; }
}

/* --------------------------------------------------------------------------
   9. Hero
   -------------------------------------------------------------------------- */

.hero {
  position: relative;
  height: 100vh;
  height: 100svh;
  min-height: 560px;
  overflow: hidden;
  background: var(--ink);
  color: var(--bg);
  display: flex;
  align-items: flex-end;
}

.hero__media { position: absolute; inset: 0; z-index: 0; }

.hero__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Pushes the footage graphic rather than pastoral. Without this the
     overcast opening frames read soft and earthy, which is Calder territory. */
  filter: contrast(1.1) saturate(0.82);
}

/* Scrim keeps the headline readable across the whole bare to finished shift,
   including the bright overcast opening frames. Weighted to the left so the
   landscape on the right stays legible instead of going muddy. */
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  background:
    linear-gradient(to top, rgba(17,19,18,0.86) 0%, rgba(17,19,18,0.38) 30%, rgba(17,19,18,0.04) 58%, rgba(17,19,18,0.30) 100%),
    linear-gradient(to right, rgba(17,19,18,0.80) 0%, rgba(17,19,18,0.30) 42%, rgba(17,19,18,0) 72%);
}

/* Narrow screens get no help from the left to right gradient, so the vertical
   one has to carry the whole text block on its own. */
@media (max-width: 760px) {
  .hero__scrim {
    background: linear-gradient(to top,
      rgba(17,19,18,0.92) 0%,
      rgba(17,19,18,0.78) 34%,
      rgba(17,19,18,0.45) 62%,
      rgba(17,19,18,0.30) 100%);
  }
}

.hero__inner {
  position: relative;
  z-index: 3;
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  /* Top padding clears the fixed nav so the eyebrow can never ride under it. */
  padding: clamp(6rem, 14vh, 9rem) var(--pad-x) clamp(3rem, 8vh, 6rem);
}

/* The stacked hero wordmark has to fit one viewport alongside the sub and the
   actions, so it runs below the global display scale. The viewport unit is
   deliberately aggressive: on a narrow screen a poster headline still has to
   read as a poster, not as a large paragraph. */
.hero__headline .display { font-size: clamp(3.5rem, 19vw, 9.5rem); }

.hero .eyebrow { color: var(--bg); opacity: 0.92; }

.hero__headline { margin-bottom: clamp(1.25rem, 2.5vw, 2rem); }

.hero__headline .display { display: block; overflow: hidden; }
.hero__headline .display span { display: block; }

.hero__headline .display:nth-child(3) { color: var(--cedar); }

.hero__sub {
  font-size: var(--fs-lead);
  line-height: 1.45;
  max-width: 46ch;
  margin: 0 0 clamp(2rem, 4vw, 2.75rem);
  color: rgba(237, 237, 234, 0.88);
}

.hero__actions { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; }

.hero__hint {
  position: absolute;
  right: var(--pad-x);
  bottom: clamp(3rem, 8vh, 6rem);
  z-index: 3;
  display: none;
  align-items: center;
  gap: 0.75rem;
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  opacity: 0.6;
}

@media (min-width: 900px) { .hero__hint { display: flex; } }

.hero__hint i {
  display: block;
  width: 1px; height: 44px;
  background: currentColor;
  transform-origin: top;
  animation: hintDrop 2.4s cubic-bezier(0.6, 0, 0.2, 1) infinite;
}

@keyframes hintDrop {
  0%   { transform: scaleY(0); opacity: 0; }
  35%  { transform: scaleY(1); opacity: 1; }
  70%  { transform: scaleY(1); opacity: 1; }
  100% { transform: scaleY(1) translateY(44px); opacity: 0; }
}

/* --------------------------------------------------------------------------
   10. Statement
   -------------------------------------------------------------------------- */

.statement .h2 { max-width: 18ch; margin-bottom: clamp(2rem, 4vw, 3rem); }

.statement__grid {
  display: grid;
  gap: clamp(1.75rem, 3vw, 3rem);
  max-width: 74ch;
}

@media (min-width: 760px) {
  .statement__grid { grid-template-columns: 1fr 1fr; max-width: none; }
}

.statement__body p { margin: 0 0 1.25rem; max-width: 46ch; }
.statement__body p:last-child { margin-bottom: 0; }

.statement__pull {
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 3.2vw, 3rem);
  line-height: 1.02;
  text-transform: uppercase;
  letter-spacing: -0.01em;
  margin: 0;
  align-self: start;
}

.statement__divider { margin-top: clamp(3rem, 7vw, 6rem); }

/* --------------------------------------------------------------------------
   11. Styles grid (ink, full bleed)
   -------------------------------------------------------------------------- */

.styles__head {
  max-width: var(--maxw);
  margin: 0 auto clamp(2.5rem, 5vw, 4.5rem);
  padding: 0 var(--pad-x);
  position: relative;
  z-index: 2;
}

.styles__head .lead { color: rgba(237, 237, 234, 0.7); }

.styles__grid {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: 1fr;
  border-top: 1px solid var(--ink-3);
}

@media (min-width: 640px) { .styles__grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1080px) { .styles__grid { grid-template-columns: repeat(3, 1fr); } }

.style-card {
  position: relative;
  border-bottom: 1px solid var(--ink-3);
  border-right: 1px solid var(--ink-3);
  padding: clamp(1.75rem, 2.6vw, 2.75rem);
  display: flex;
  flex-direction: column;
  gap: 1rem;
  min-height: clamp(280px, 30vw, 370px);
  overflow: hidden;
  isolation: isolate;
}

.style-card__media {
  position: absolute;
  inset: 0;
  z-index: -1;
  opacity: 0;
  transition: opacity 0.5s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.style-card__media img {
  width: 100%; height: 100%;
  object-fit: cover;
  filter: grayscale(1) contrast(1.15) brightness(0.42);
}

.style-card:hover .style-card__media { opacity: 1; }

/* :focus-visible, not :focus-within. Clicking the card leaves focus on its
   trigger button (the viewer restores it there on close, which accessibility
   requires), and :focus-within cannot tell a mouse click from a Tab, so the
   hover state stuck on after every click. Kept as its own rule rather than a
   comma pair: an unsupported :has() invalidates only this line instead of
   taking the hover rule down with it. */
.style-card:has(.lb-open:focus-visible) .style-card__media { opacity: 1; }

.style-card__num {
  font-size: clamp(2.25rem, 3.4vw, 3.25rem);
  line-height: 1;
}

.style-card__title { margin-top: auto; }

.style-card__body {
  color: rgba(237, 237, 234, 0.66);
  font-size: var(--fs-body);
  margin: 0;
  max-width: 34ch;
}

/* Slat rhythm sitting at the foot of each card, drawing the motif through */
.style-card__slats {
  display: flex;
  gap: 5px;
  height: 46px;
  align-items: flex-end;
  margin-top: 0.5rem;
}
.style-card__slats i {
  flex: 1;
  height: 100%;
  background: var(--ink-3);
  transform: scaleY(0.5);
  transform-origin: bottom;
  transition: transform 0.4s cubic-bezier(0.2, 0.8, 0.2, 1), background-color 0.4s ease;
}
/* Slats stand up in sequence left to right. --i is stamped on each slat at
   render time so the stagger works for any number of them. */
.style-card__slats i { transition-delay: calc(var(--i, 0) * 0.035s); }

.style-card:hover .style-card__slats i { transform: scaleY(1); background: var(--cedar); }

/* These cedar slats are what read as an orange overlay when the state stuck. */
.style-card:has(.lb-open:focus-visible) .style-card__slats i { transform: scaleY(1); background: var(--cedar); }

/* --------------------------------------------------------------------------
   12. Portfolio (paper, full bleed)
   -------------------------------------------------------------------------- */

.portfolio__head {
  max-width: var(--maxw);
  margin: 0 auto clamp(2.5rem, 5vw, 4rem);
  padding: 0 var(--pad-x);
  position: relative;
  z-index: 2;
}

.pf-feature {
  position: relative;
  z-index: 2;
  height: clamp(340px, 62vh, 720px);
  overflow: hidden;
  isolation: isolate;
}

.pf-feature img {
  width: 100%; height: 100%;
  object-fit: cover;
}

.pf-feature__cap {
  position: absolute;
  left: var(--pad-x);
  bottom: clamp(1.5rem, 3vw, 2.75rem);
  z-index: 3;
  color: var(--bg);
  max-width: 40ch;
}

.pf-feature__cap .h3 { margin-bottom: 0.35rem; }
.pf-feature__cap p { margin: 0; font-size: var(--fs-small); opacity: 0.8; }

.pf-feature::after {
  content: '';
  position: absolute; inset: 0; z-index: 2;
  background: linear-gradient(to top, rgba(17,19,18,0.8) 0%, rgba(17,19,18,0.15) 45%, rgba(17,19,18,0) 70%);
}

.pf-grid {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: 1fr;
  gap: 1px;
  background: var(--paper-3);
  margin-top: 1px;
}

@media (min-width: 760px) { .pf-grid { grid-template-columns: repeat(3, 1fr); } }

.pf-item {
  position: relative;
  background: var(--bg);
  overflow: hidden;
  isolation: isolate;
  aspect-ratio: 4 / 3;
}

.pf-item img {
  width: 100%; height: 100%;
  object-fit: cover;
}

/* Slatted wipe reveal. The image sits muted behind ink bands at rest, and the
   bands retract in sequence on hover. Sharp, banded, never a fade. */
.pf-item__bars {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  pointer-events: none;
}

.pf-item__bars i {
  flex: 1 1 0;
  background: var(--ink);
  opacity: 0.44;
  transform-origin: top center;
  transition: transform 0.5s cubic-bezier(0.16, 0.84, 0.24, 1);
}

@media (hover: hover) and (pointer: fine) {
  .pf-item:hover .pf-item__bars i { transform: scaleY(0); }
  /* Same reason as the style cards: after a click the wipe stayed retracted,
     leaving one image permanently brighter than its neighbours. */
  .pf-item:has(.lb-open:focus-visible) .pf-item__bars i { transform: scaleY(0); }
  .pf-item__bars i:nth-child(1) { transition-delay: 0s; }
  .pf-item__bars i:nth-child(2) { transition-delay: 0.045s; }
  .pf-item__bars i:nth-child(3) { transition-delay: 0.09s; }
  .pf-item__bars i:nth-child(4) { transition-delay: 0.135s; }
  .pf-item__bars i:nth-child(5) { transition-delay: 0.18s; }
  .pf-item__bars i:nth-child(6) { transition-delay: 0.225s; }
  .pf-item__bars i:nth-child(7) { transition-delay: 0.27s; }
  .pf-item__bars i:nth-child(8) { transition-delay: 0.315s; }
}

/* Touch devices never get the hover, so they never get the bands either. */
@media not all and (hover: hover) {
  .pf-item__bars { display: none; }
}

.pf-item__cap {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 3;
  padding: clamp(1rem, 1.8vw, 1.5rem);
  color: var(--bg);
  background: linear-gradient(to top, rgba(17,19,18,0.85), rgba(17,19,18,0));
}

.pf-item__cap strong { display: block; font-size: var(--fs-body); font-weight: 700; }
.pf-item__cap span { font-size: var(--fs-small); opacity: 0.78; }


/* --------------------------------------------------------------------------
   13. Materials (ink, asymmetric split)
   -------------------------------------------------------------------------- */

.materials__media {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  aspect-ratio: 4 / 5;
  order: -1;
}

@media (min-width: 900px) {
  /* Stretched to the full row rather than a fixed 4:5 box pinned to the top.
     The row height is set by the text column, so the image grows into space
     that already existed: bigger picture, no taller section. Sticky goes with
     it, since an image that spans the whole row has nothing to stick to. */
  .materials__media {
    order: 0;
    align-self: stretch;
    aspect-ratio: auto;
    min-height: 420px;
  }
}

.materials__media img { width: 100%; height: 100%; object-fit: cover; }

.materials__list { display: grid; gap: clamp(0.9rem, 1.3vw, 1.15rem); }

.mat {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: clamp(1rem, 2vw, 1.75rem);
  padding-bottom: clamp(0.9rem, 1.3vw, 1.15rem);
  border-bottom: 1px solid var(--ink-3);
}
.mat:last-child { border-bottom: 0; padding-bottom: 0; }

.mat__num { font-size: clamp(1.5rem, 2.2vw, 2rem); }

.mat__title { margin-bottom: 0.6rem; }

.mat__body { color: rgba(237, 237, 234, 0.66); margin: 0; max-width: 48ch; line-height: 1.5; }

/* --------------------------------------------------------------------------
   14. Process (paper, contained)
   -------------------------------------------------------------------------- */

.process__steps {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
  border-top: 1px solid var(--paper-3);
}

@media (min-width: 860px) { .process__steps { grid-template-columns: repeat(4, 1fr); } }

.step {
  padding: clamp(1.75rem, 3vw, 2.75rem) 0;
  border-bottom: 1px solid var(--paper-3);
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

@media (min-width: 860px) {
  .step { padding: clamp(1.75rem, 3vw, 2.75rem) clamp(1.25rem, 2vw, 2rem); border-right: 1px solid var(--paper-3); border-bottom: 0; }
  .step:first-child { padding-left: 0; }
  .step:last-child { border-right: 0; padding-right: 0; }
}

.step__slats { display: flex; gap: 4px; height: 52px; align-items: flex-end; }
.step__slats i { flex: 1; height: 100%; background: var(--paper-3); transform: scaleY(0); transform-origin: bottom; }
.step__slats i.is-cedar { background: var(--cedar); }

.step__num { font-size: clamp(1.75rem, 2.4vw, 2.25rem); }
.step__body { color: var(--muted); margin: 0; }

/* --------------------------------------------------------------------------
   15. Why (ink, full bleed)
   -------------------------------------------------------------------------- */

.why__head {
  max-width: var(--maxw);
  margin: 0 auto clamp(2.5rem, 5vw, 4.5rem);
  padding: 0 var(--pad-x);
  position: relative;
  z-index: 2;
}

.why__grid {
  position: relative;
  z-index: 2;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--pad-x);
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(2rem, 4vw, 3.5rem);
}

@media (min-width: 720px) { .why__grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1180px) { .why__grid { grid-template-columns: repeat(4, 1fr); } }

.why-item { display: flex; flex-direction: column; gap: 1rem; }

.why-item__num {
  font-size: clamp(3.5rem, 6vw, 5.5rem);
  line-height: 0.85;
  border-bottom: 2px solid var(--ink-3);
  padding-bottom: 1rem;
}

.why-item__body { color: rgba(237, 237, 234, 0.66); margin: 0; }

/* --------------------------------------------------------------------------
   16. Testimonials (paper, contained)
   -------------------------------------------------------------------------- */

.quotes {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(2rem, 3.5vw, 3rem);
}

@media (min-width: 860px) { .quotes { grid-template-columns: repeat(3, 1fr); } }

.quote {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  padding-top: 1.75rem;
  border-top: 2px solid var(--ink);
}

.quote__text {
  font-size: clamp(1.0625rem, 1.35vw, 1.25rem);
  line-height: 1.5;
  margin: 0;
}

.quote__by { margin-top: auto; }
.quote__name { display: block; font-weight: 700; }
.quote__loc { display: block; font-size: var(--fs-small); color: var(--muted); }

/* --------------------------------------------------------------------------
   17. Contact (ink, asymmetric split)
   -------------------------------------------------------------------------- */

/* The photograph is the section background. Same arrangement as .hero__media:
   an absolute wrapper at z-index 0 holding image, sweep and scrim, with the
   content as a sibling above it. inset:0 covers the section's padding box,
   which here means edge to edge AND through the section's own --sec-y, so it
   genuinely runs top to bottom. */
.contact__media {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.contact__img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  /* left: the crop must keep the image's black half under the content. A
     centred crop spills the form onto lit slats at intermediate widths. */
  object-fit: cover;
  object-position: left center;
}

.contact__scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  /* Weighted left and gone by 78%. Over a region that is already black this
     costs nothing visually, it just insures the copy if the crop boundary
     moves, and the slats stay untouched. */
  background: linear-gradient(to right,
    rgba(17, 19, 18, 0.82) 0%,
    rgba(17, 19, 18, 0.66) 38%,
    rgba(17, 19, 18, 0.28) 60%,
    rgba(17, 19, 18, 0) 78%);
}

/* The sweep now lands on a photograph rather than flat ink. .section--ink
   runs it at 1.85x, which with the scrubbed --sweep-opacity peaking at 0.42
   would drop a 0.78 soft-light layer onto the image. The hero has the same
   problem and solves it the same way. */
.contact .sweep { opacity: calc(var(--sweep-opacity) * 0.5); }

.contact__inner {
  position: relative;
  z-index: 3;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--pad-x);
}

/* Held to the dark half of the photograph. Narrowed from 640: the section is
   now shorter, which means less cover upscale, which means the black region
   covers less viewport width. At 1440 the boundary lands near 735px, so 600
   keeps a real margin instead of clearing it by 20px. */
.contact__col { max-width: 600px; }

/* Both of these sections ran past a single screen. The vertical rhythm is
   pulled in locally rather than globally so the rest of the page keeps its
   generous spacing. */
/* Padding is deliberately uneven here. The nav is fixed and roughly 100px
   tall, so a symmetric shrink tucks the heading underneath it. Weighting the
   space to the top clears the nav and spends the same total height, which is
   what lets both of these sections be read without scrolling. */
.contact,
.materials {
  /* Floor of 6.5rem is deliberate: the nav measures about 103px, and a
     purely vh based value drops under that on short laptop screens. */
  padding-top: clamp(6.5rem, 11vh, 7rem);
  padding-bottom: clamp(2rem, 4vh, 3.5rem);
}

/* Audit 09-30: materials ran ~7rem top against ~3rem bottom. Now even on
   both sides; scroll-margin keeps an anchor jump from tucking the eyebrow
   under the fixed nav, which is what the old top weighting was for. */
.materials {
  padding-top: clamp(4.5rem, 9vh, 6rem);
  padding-bottom: clamp(4.5rem, 9vh, 6rem);
  scroll-margin-top: clamp(1.5rem, 3vw, 2.5rem);
}

/* Contact carries about 80px more floor than materials. The submit button was
   sitting close to the footer edge, and the extra height also lengthens the
   photograph's cover box, which keeps the slats reading as a full band rather
   than a strip. Materials keeps the tighter value. */
.contact { padding-bottom: clamp(7rem, 11vh, 9.5rem); }

.contact .sec-head,
.materials .sec-head { margin-bottom: clamp(1.25rem, 2vw, 1.75rem); }

.contact .h2,
.materials .h2 { font-size: clamp(2.25rem, 5.8vw, 5.5rem); }

.contact .lead { margin-top: 0.9rem !important; }
.contact .form { margin-top: clamp(1.1rem, 1.8vw, 1.5rem); gap: 0.95rem; }
.contact .field textarea { min-height: 82px; }

/* Over the photograph the eyebrow follows the hero rather than the ink
   sections: cedar at 11px measures 4.55 on flat ink, so the slightest lift
   from the image drops it below 4.5. Paper text, cedar bar. */
.contact .eyebrow { color: rgba(237, 237, 234, 0.92); }

/* Was an inline 0.7 on flat ink. */
.lead--onmedia { color: rgba(237, 237, 234, 0.86); }

@media (max-width: 899px) {
  /* At this width a left crop would show only the black void and the slats
     would vanish, so the image shifts back toward them and the scrim takes
     over the protection. Exactly what .hero__scrim does below 760px. */
  .contact__img { object-position: 64% center; }
  .contact__col { max-width: none; }
  .contact__scrim {
    background: linear-gradient(to top,
      rgba(17, 19, 18, 0.93) 0%,
      rgba(17, 19, 18, 0.86) 38%,
      rgba(17, 19, 18, 0.78) 70%,
      rgba(17, 19, 18, 0.8) 100%);
  }
}

.form { display: grid; gap: 1.25rem; margin-top: clamp(2rem, 3.5vw, 2.75rem); }

@media (min-width: 620px) { .form__row { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; } }

.field { display: grid; gap: 0.5rem; }

.field label {
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  /* Raised from 0.6. These were tuned against flat ink; the site's own
     over-photography precedent never drops supporting text below 0.78. */
  color: rgba(237, 237, 234, 0.82);
}

.field input,
.field select,
.field textarea {
  background: transparent;
  border: 0;
  /* --ink-3 was chosen to lift off flat #111312 and disappears over the
     photograph. This hairline is the field's entire affordance, since the
     input has no border and no background, so it has to hold on its own. */
  border-bottom: 1px solid rgba(237, 237, 234, 0.45);
  padding: 0.85rem 0;
  color: var(--bg);
  font-size: var(--fs-body);
  border-radius: 0;
  transition: border-color 0.25s ease;
  width: 100%;
}

.field textarea { resize: vertical; min-height: 120px; }

.field select { appearance: none; cursor: pointer; }
.field select option { background: var(--ink); color: var(--bg); }

.field input::placeholder,
.field textarea::placeholder { color: rgba(237, 237, 234, 0.56); }

.field input:focus,
.field select:focus,
.field textarea:focus { border-bottom-color: var(--cedar); outline: none; }

.field:focus-within label { color: var(--cedar); }

.form__note {
  font-size: var(--fs-small);
  color: rgba(237, 237, 234, 0.74);
  margin: 0;
}

.form__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 1.25rem; }

/* --------------------------------------------------------------------------
   18. Footer
   -------------------------------------------------------------------------- */

.footer {
  position: relative;
  background: var(--ink);
  color: var(--bg);
  padding: clamp(3.5rem, 7vw, 6rem) 0 clamp(2rem, 4vw, 3rem);
  overflow: hidden;
}

.footer__inner {
  position: relative;
  z-index: 2;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--pad-x);
}

.footer__top {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(2.5rem, 5vw, 4rem);
  padding-bottom: clamp(2.5rem, 5vw, 4rem);
  border-bottom: 1px solid var(--ink-3);
}

@media (min-width: 860px) { .footer__top { grid-template-columns: 1.4fr 1fr 1fr; } }

.footer__word {
  font-family: var(--font-display);
  font-size: clamp(2.5rem, 5vw, 4rem);
  line-height: 0.9;
  text-transform: uppercase;
  margin: 0 0 0.75rem;
}

.footer__tagline { color: var(--muted-ink); margin: 0 0 1.25rem; font-size: var(--fs-small); letter-spacing: 0.14em; text-transform: uppercase; }
.footer__line { color: rgba(237, 237, 234, 0.66); margin: 0; max-width: 34ch; }

.footer__col h4 {
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--cedar);
  margin: 0 0 1.25rem;
}

.footer__nav { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.7rem; }
.footer__nav a { text-decoration: none; color: rgba(237, 237, 234, 0.7); transition: color 0.2s ease; }
.footer__nav a:hover { color: var(--cedar); }

.footer__nap { font-style: normal; color: rgba(237, 237, 234, 0.7); display: grid; gap: 0.4rem; }
.footer__nap a { color: rgba(237, 237, 234, 0.7); text-decoration: none; transition: color 0.2s ease; }
.footer__nap a:hover { color: var(--cedar); }

.footer__bottom {
  padding-top: clamp(1.75rem, 3vw, 2.5rem);
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
  justify-content: space-between;
  align-items: flex-start;
}

.footer__disclaimer {
  margin: 0;
  font-size: var(--fs-small);
  color: var(--muted-ink);
  max-width: 68ch;
  line-height: 1.55;
}

.footer__copy { margin: 0; font-size: var(--fs-small); color: var(--muted-ink); flex: none; display: inline-flex; align-items: center; gap: 8px; text-decoration: none; }
.footer__copy .soak-mark { display: inline-block; width: 20px; height: 20px; flex: none;
  background-color: var(--cedar);
  -webkit-mask: url(../assets/images/summitoak-mark.png) center / contain no-repeat;
          mask: url(../assets/images/summitoak-mark.png) center / contain no-repeat;
  opacity: .9; transition: opacity .2s ease; }
.footer__copy:hover .soak-mark { opacity: 1; }
.footer__copy:focus-visible { outline: 2px solid var(--cedar); outline-offset: 3px; border-radius: 4px; }

@media (max-width: 640px) {
  .footer__bottom { flex-direction: column; align-items: flex-start; }
}

.footer__slats { margin-bottom: clamp(2.5rem, 5vw, 4rem); }

/* --------------------------------------------------------------------------
   19. Full screen image viewer
   The cards keep their grayscale hover. This is the only place the
   photography is shown in full colour.
   -------------------------------------------------------------------------- */

/* Transparent trigger covering a card. Sits above the media and the slats so
   the whole card is one hit target, and gives the card something focusable,
   which switches on the :focus-within hover rules. */
.lb-open {
  position: absolute;
  inset: 0;
  z-index: 5;
  appearance: none;
  border: 0;
  background: transparent;
  padding: 0;
  cursor: zoom-in;
}

.lb-open__icon {
  position: absolute;
  top: clamp(0.9rem, 1.6vw, 1.4rem);
  right: clamp(0.9rem, 1.6vw, 1.4rem);
  width: 20px; height: 20px;
  color: var(--bg);
  opacity: 0;
  transform: scale(0.82);
  transition: opacity 0.35s ease, transform 0.35s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.lb-open__icon svg { width: 100%; height: 100%; display: block; }

.style-card:hover .lb-open__icon,
.pf-item:hover .lb-open__icon,
.pf-feature:hover .lb-open__icon,
.lb-open:focus-visible .lb-open__icon {
  opacity: 0.85;
  transform: scale(1);
}

.lb-open:focus-visible { outline: 2px solid var(--cedar); outline-offset: -4px; }

/* Touch has no hover to reveal the affordance, and this is the only way a
   phone user can see these photographs at all, so the icon stays visible. */
@media (hover: none) {
  .lb-open__icon { opacity: 0.7; transform: none; }
}

/* Scroll lock. overflow:hidden on <html> rather than position:fixed, because
   the hero is pinned and the sweep timeline is triggered off document.body:
   anything that collapses document height invalidates both. This moves no
   geometry, so trigger positions survive. --sbw compensates for the
   scrollbar disappearing, including on the fixed nav. */
html.is-locked { overflow: hidden; }
html.is-locked body { padding-right: var(--sbw, 0px); }
html.is-locked .nav { padding-right: calc(var(--pad-x) + var(--sbw, 0px)); }

.lb {
  padding: 0;
  border: 0;
  margin: auto;
  width: 100%;
  max-width: 100%;
  height: 100%;
  max-height: 100%;
  /* The dialog fills the viewport, so it carries the blackout itself rather
     than leaving it to ::backdrop. At 0.94 the hero headline still ghosted
     through and on a phone it collided with the caption. */
  background: rgba(9, 10, 10, 0.975);
  color: var(--bg);
  overflow: hidden;
  overscroll-behavior: contain;
}

.lb::backdrop { background: rgba(9, 10, 10, 0.975); }

.lb__inner {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(3.25rem, 7vh, 5rem) clamp(1rem, 6vw, 5.5rem);
}

.lb__figure {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(1rem, 2.2vh, 1.75rem);
  max-width: 100%;
  max-height: 100%;
  min-height: 0;
}

.lb__frame { position: relative; min-height: 0; overflow: hidden; }

.lb__img {
  display: block;
  max-width: 100%;
  max-height: 68vh;
  width: auto; height: auto;
  object-fit: contain;
  margin: 0 auto;
}

/* Slat reveal. The image arrives in vertical bands, same motif and same --i
   stagger as the card slats. Bands cover at rest and retract on open. */
.lb__bands { position: absolute; inset: 0; display: flex; pointer-events: none; }
.lb__bands i {
  flex: 1 1 0;
  background: var(--ink);
  transform-origin: top center;
  transition: transform 0.52s cubic-bezier(0.16, 0.84, 0.24, 1);
  transition-delay: calc(var(--i, 0) * 0.045s);
}
.lb__bands.is-open i { transform: scaleY(0); }

.lb__cap { max-width: 62ch; }

.lb__title {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 3vw, 2.5rem);
  line-height: 1;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  margin: 0 0 0.6rem;
  font-weight: 400;
}

.lb__note { margin: 0; color: rgba(237, 237, 234, 0.82); max-width: 56ch; }

.lb__count {
  margin: 0.9rem 0 0;
  font-size: var(--fs-eyebrow);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--cedar);
}

.lb__close,
.lb__nav {
  position: absolute;
  z-index: 3;
  appearance: none;
  border: 0;
  background: rgba(237, 237, 234, 0.08);
  color: var(--bg);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 0.25s ease, color 0.25s ease;
}
.lb__close svg, .lb__nav svg { width: 40%; height: 40%; }

.lb__close:hover,
.lb__nav:hover { background: var(--cedar); color: var(--ink); }

.lb__close {
  top: clamp(0.75rem, 2vh, 1.5rem);
  right: clamp(0.75rem, 2vw, 1.5rem);
  width: clamp(44px, 5vw, 56px);
  height: clamp(44px, 5vw, 56px);
}

.lb__nav {
  top: 50%;
  transform: translateY(-50%);
  width: clamp(44px, 4.5vw, 58px);
  height: clamp(56px, 7vw, 84px);
}
.lb__nav--prev { left: clamp(0.4rem, 1.5vw, 1.25rem); }
.lb__nav--next { right: clamp(0.4rem, 1.5vw, 1.25rem); }
.lb__nav[hidden] { display: none; }

@media (max-width: 640px) {
  /* A 16:9 image on a portrait phone is width limited, so give it the full
     width and keep the padding for the caption only. */
  .lb__inner { padding: clamp(3.25rem, 7vh, 5rem) 0 clamp(4.5rem, 10vh, 6rem); align-items: center; }
  .lb__cap { padding: 0 1.25rem; }
  .lb__img { max-height: 52vh; }
  .lb__nav { top: auto; bottom: clamp(0.75rem, 3vh, 1.5rem); transform: none; height: 48px; }
  .lb__nav--prev { left: 0.75rem; }
  .lb__nav--next { right: 0.75rem; }
}

/* --------------------------------------------------------------------------
   20. The gauge cursor
   A cedar dot at the true pointer position, four corner brackets catching up
   behind it. Only ever drawn for a real mouse, and only when JS has added
   .has-cur, so the native cursor is never hidden without a replacement.
   -------------------------------------------------------------------------- */

@media (hover: hover) and (pointer: fine) {
  /* The native cursor only goes away once the custom one actually exists.
     These four selectors already declare a cursor elsewhere in the file and
     are more specific than html/body, so each has to be named. */
  html.has-cur,
  html.has-cur body,
  html.has-cur .btn,
  html.has-cur .field select,
  html.has-cur .lb-open,
  html.has-cur input,
  html.has-cur textarea,
  html.has-cur select,
  html.has-cur a { cursor: none; }

  /* While the viewer is open the dialog sits in the top layer, above every
     z-index, so the custom cursor cannot be seen. Hand back the real one. */
  html.is-native-cursor,
  html.is-native-cursor body,
  html.is-native-cursor a,
  html.is-native-cursor .lb-open { cursor: auto; }
  html.is-native-cursor .lb__close,
  html.is-native-cursor .lb__nav { cursor: pointer; }
  html.is-native-cursor .cur { opacity: 0; }
}

.cur {
  position: fixed;
  top: 0; left: 0;
  z-index: 300;              /* clears .skip at 200 and .nav at 100 */
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.cur.is-on { opacity: 1; }

.cur__dot,
.cur__box {
  position: absolute;
  top: 0; left: 0;
  will-change: transform;
}

/* A light halo AND a dark outline, the trick OS cursors use. One outline is
   not enough: cedar over the hero's mid tone grass measured 1.7:1, and a dark
   outline alone only reached 2.59. With both, whichever ground the pointer is
   over, one of the two edges contrasts. Chained drop-shadows apply to the
   result of the previous one, so this really does nest.

   drop-shadow rather than box-shadow rings on purpose: rings around a 1.25px
   bar would swell it into a ~5px blob, which is the blockiness being removed. */
/* Every slat is a lit face and a shadow face, which is what a board actually
   looks like under raking light, and is the same idea the shadow sweep is
   built on.

   It is also the only thing that survives a mid tone background. Blurred
   drop-shadows cannot: spread around a 1.5px source, a blur never reaches
   full strength, which is why a soft halo measured 2.51 on the hero's olive
   grass against a 3.0 floor. These edges are hard, so the light rail hits
   near white and the shadow rail near black no matter what is behind them. */
.cur__dot,
.cur__s {
  /* Cedar has to be the majority of the slat or the edges take over and the
     cursor reads black and white rather than orange. The edges stay hard, so
     even at 0.75px they render as a strong partial-alpha pixel and keep the
     contrast they are there for. */
  background:
    linear-gradient(90deg,
      rgba(237, 237, 234, 0.98) 0 0.75px,        /* lit edge */
      var(--cedar) 0.75px 2.75px,                 /* board face, 57% of it */
      rgba(9, 10, 10, 0.95) 2.75px 3.5px);        /* shadow edge */
}

.cur__box { filter: drop-shadow(0 1px 2px rgba(9, 10, 10, 0.4)); }
.cur__dot { filter: drop-shadow(0 1px 2px rgba(9, 10, 10, 0.4)); }

/* The centre slat. Sits on the true pointer position and marks the click
   point, so it is the tallest and the only one at full strength. */
.cur__dot {
  width: 3.5px; height: 18px;
  margin: -9px 0 0 -1.75px;
  transition: height 0.24s cubic-bezier(0.2, 0.8, 0.2, 1),
              margin 0.24s cubic-bezier(0.2, 0.8, 0.2, 1),
              opacity 0.24s ease;
}

/* Carrier for the two outer slats. --gap is the reveal between each outer
   slat and the centre, and it is the thing that expands and contracts. */
.cur__box {
  --gap: 3px;
  width: 0; height: 0;
  transition: --gap 0.26s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.cur__s {
  position: absolute;
  width: 3.5px; height: 13px;
  top: -6.5px;
  opacity: 0.72;
  transition: height 0.26s cubic-bezier(0.2, 0.8, 0.2, 1),
              top 0.26s cubic-bezier(0.2, 0.8, 0.2, 1),
              opacity 0.24s ease;
}

.cur__s--l { left: calc((var(--gap) + 3.5px) * -1); }
.cur__s--r { left: var(--gap); }

/* Over a link the reveal closes up to almost solid board. */
.cur.is-link .cur__box { --gap: 0px; }
.cur.is-link .cur__s { opacity: 0.85; }

/* Over a clickable image the fence opens up to look through it: the outer
   slats spread and come to full strength, the centre recedes. This replaces
   the zoom-in hint that .lb-open used to carry. */
.cur.is-view .cur__box { --gap: 9px; }
.cur.is-view .cur__s { height: 16px; top: -8px; opacity: 1; }
.cur.is-view .cur__dot { height: 16px; margin-top: -8px; opacity: 0.5; }

/* Over a text field the outer slats drop away and the centre becomes the
   caret, which reads as an I-beam and as a single slat at the same time. */
.cur.is-text .cur__s { opacity: 0; }
.cur.is-text .cur__dot {
  height: 20px; margin-top: -10px;
  width: 1.5px; margin-left: -0.75px;
  background: var(--cedar);          /* a caret wants a hairline, not a board */
}

/* Pressed: the reveal snaps shut. */
.cur.is-down .cur__box { --gap: 0px; }
.cur.is-down .cur__dot { height: 16px; margin-top: -8px; }
.cur.is-down .cur__s { height: 12px; top: -6px; }

/* Registered so --gap interpolates smoothly instead of snapping. It must
   inherit: --gap is declared on .cur__box but consumed by the .cur__s
   children, which would never see it otherwise. */
@property --gap {
  syntax: '<length>';
  inherits: true;
  initial-value: 3px;
}

/* --------------------------------------------------------------------------
   21. Reduced motion
   Sun parks at one pleasant raking angle, slats show assembled, no scrub.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }

  :root {
    --sweep-angle: -12;
    --sweep-shift: 0;
    --sweep-scale: 1.18;
    --sweep-opacity: 0.3;
  }

  .slats i,
  .step__slats i { transform: scaleY(1) !important; }
  .style-card__slats i { transform: scaleY(1) !important; }
  .pf-item__bars { display: none; }
  .rise { opacity: 1 !important; transform: none !important; }
  .hero__hint i { animation: none; }

  /* The viewer opens instantly. Bands are retracted from the start rather
     than animating, so the image is never left covered. */
  .lb__bands { display: none; }

  /* No custom cursor at all. Hiding the system pointer is a real cost for
     anyone relying on OS cursor size or contrast settings, and this is the
     closest signal available for "skip the effects". */
  .cur { display: none !important; }
  html.has-cur, html.has-cur body, html.has-cur a,
  html.has-cur .btn, html.has-cur .lb-open,
  html.has-cur input, html.has-cur textarea, html.has-cur select { cursor: auto !important; }
  html.has-cur .btn, html.has-cur .field select { cursor: pointer !important; }
  html.has-cur .lb-open { cursor: zoom-in !important; }
  .lb-open__icon { opacity: 0.85 !important; transform: none !important; }
}

/* --------------------------------------------------------------------------
   22. Print
   -------------------------------------------------------------------------- */

@media print {
  .sweep, .grain, .nav, .hero__hint, .pf-item__bars, .lb, .lb-open, .cur { display: none !important; }
  .section--ink, .footer { background: #fff !important; color: #000 !important; }
}


/* Brand-colored magnifier cursor on click-to-zoom photos (replaces removed on-card badges). */
.lb-open, html.has-cur .lb-open { cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cg fill='none' stroke-linecap='round'%3E%3Ccircle cx='13' cy='13' r='8' stroke='%23ffffff' stroke-width='4'/%3E%3Cline x1='19' y1='19' x2='29' y2='29' stroke='%23ffffff' stroke-width='4'/%3E%3Ccircle cx='13' cy='13' r='8' stroke='%23A9713F' stroke-width='2.4'/%3E%3Cline x1='19' y1='19' x2='29' y2='29' stroke='%23A9713F' stroke-width='2.4'/%3E%3C/g%3E%3C/svg%3E") 13 13, zoom-in !important; }

/* Mobile: two-column footer top (nav beside contact) to cut height. */
@media (max-width: 859px) {
  .footer__top { grid-template-columns: 1fr 1fr; }
}

/* --------------------------------------------------------------------------
   23. Design audit 2026-09-30
   -------------------------------------------------------------------------- */

/* Line rule: no wrapped line carries fewer than three words. Balanced wraps
   spread short card copy evenly instead of stranding the last word. */
p, blockquote, .h2, .h3, .quote__text { text-wrap: balance; }

/* Four Steps corner drawing: a hairline fence elevation, top right, the way
   step 01 hands it over. Grade line, posts a third below grade into concrete
   bells, gauged slats, dimension ticks. Decoration only. */
.process__elev {
  position: absolute;
  z-index: 1;
  top: clamp(1.25rem, 3.4vw, 3rem);
  right: var(--pad-x);
  width: clamp(140px, 26vw, 380px);
  height: auto;
  pointer-events: none;
}
.process__elev rect,
.process__elev line,
.process__elev .el-bell { stroke: #C6C6BF; stroke-width: 1.2; vector-effect: non-scaling-stroke; }
.process__elev .el-bell { stroke-dasharray: 3 4; }
.process__elev .el-grade { stroke: #A9A9A1; stroke-dasharray: 10 6; }
.process__elev .el-cedar { stroke: var(--cedar); opacity: 0.6; }
.process__elev .el-dim { stroke: #B3B3AB; stroke-width: 1; vector-effect: non-scaling-stroke; }

/* Glass quote form over the photograph: translucent, blurred, hairline. */
.contact .form {
  padding: clamp(1.25rem, 2.4vw, 2rem);
  background: linear-gradient(160deg, rgba(237, 237, 234, 0.08), rgba(237, 237, 234, 0.025)), rgba(17, 19, 18, 0.38);
  -webkit-backdrop-filter: blur(16px) saturate(120%);
          backdrop-filter: blur(16px) saturate(120%);
  border: 1px solid rgba(237, 237, 234, 0.16);
  border-radius: 3px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(237, 237, 234, 0.08);
}

/* Footer: a single concept line, on one line. */
.footer__copy { white-space: nowrap; }
@media (max-width: 420px) { .footer__copy { font-size: 0.75rem; } }

/* Small phones: brand, nav and address each get the full width so nothing
   wraps a lone word; nav runs as a compact three-column grid. */
@media (max-width: 599px) {
  .footer__top { grid-template-columns: 1fr; gap: 2rem; }
  .footer__nav { grid-template-columns: repeat(3, auto); justify-content: start; column-gap: 2rem; }
}

/* "Why It Stays Straight" reads on one line: sized to its column. */
.materials .h2 { font-size: clamp(2.25rem, 4.9vw, 4.5rem); }

/* Stacked rows on phones had no gap between them (Name ran into Email). */
.contact .form__row { display: grid; gap: 0.95rem; }

/* --------------------------------------------------------------------------
   24. Round 2 (10-01)
   -------------------------------------------------------------------------- */

.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* One section rhythm, top equals bottom everywhere. Anchor jumps clear the
   fixed nav through scroll-margin instead of a heavier top pad. */
:root { --sec-y: clamp(4.5rem, 11vh, 8rem); }
.section { scroll-margin-top: clamp(1.5rem, 3vw, 2.5rem); }
.contact,
.materials { padding-top: var(--sec-y); padding-bottom: var(--sec-y); }

/* From the Line: ink review cards on the paper ground. A short fence run
   heads each card, cedar stars, the project in small caps, monogram sign-off.
   Faint vertical slat texture and a ghost quote mark give the ink some depth. */
.reviews {
  gap: clamp(1rem, 1.8vw, 1.75rem);
  align-items: stretch;
}
.review {
  position: relative;
  isolation: isolate;
  margin: 0;
  gap: 0;
  padding: clamp(1.5rem, 2.2vw, 2.25rem);
  padding-top: 0;
  border-top: 0;
  color: var(--bg);
  background:
    repeating-linear-gradient(90deg, rgba(237, 237, 234, 0.028) 0 1px, transparent 1px 22px),
    linear-gradient(170deg, #1B1E1C 0%, var(--ink) 62%);
  border: 1px solid rgba(17, 19, 18, 0.9);
  border-radius: 2px;
  box-shadow: 0 1px 0 rgba(237, 237, 234, 0.06) inset, 0 22px 48px -18px rgba(17, 19, 18, 0.55), 0 2px 6px rgba(17, 19, 18, 0.18);
  overflow: hidden;
  transition: transform 0.35s cubic-bezier(.2, .7, .2, 1), box-shadow 0.35s ease;
}
.review::after {
  content: '\201D';
  position: absolute;
  z-index: -1;
  right: clamp(0.75rem, 1.4vw, 1.25rem);
  bottom: clamp(-6.5rem, -5.5vw, -4.5rem);
  font-family: var(--font-display);
  font-size: clamp(8rem, 11vw, 11rem);
  line-height: 1;
  color: rgba(237, 237, 234, 0.04);
  pointer-events: none;
}
@media (hover: hover) {
  .review:hover { transform: translateY(-4px); box-shadow: 0 1px 0 rgba(237, 237, 234, 0.06) inset, 0 30px 60px -20px rgba(17, 19, 18, 0.6), 0 2px 6px rgba(17, 19, 18, 0.18); }
}
.review__rail {
  margin: 0 calc(clamp(1.5rem, 2.2vw, 2.25rem) * -1) clamp(1.5rem, 2.2vw, 2rem);
  padding: 0 clamp(1.5rem, 2.2vw, 2.25rem);
  border-bottom: 1px solid var(--ink-3);
}
.review .slats.review__slats { height: clamp(22px, 2vw, 30px); gap: clamp(4px, 0.45vw, 7px); }
.review .slats.review__slats i { background: rgba(237, 237, 234, 0.82); transform-origin: top center; }
.review .slats.review__slats i.is-cedar { background: var(--cedar); }
.review__top { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 1.1rem; }
.review__stars { display: inline-flex; gap: 3px; }
.review__stars svg { width: 15px; height: 15px; fill: var(--cedar); }
.review__num { font-family: var(--font-display); font-size: 1.5rem; line-height: 1; color: rgba(237, 237, 234, 0.2); letter-spacing: 0.02em; }
.review__project {
  margin: 0 0 1rem;
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--cedar);
}
.review__text { color: rgba(237, 237, 234, 0.9); margin-bottom: clamp(1.75rem, 2.6vw, 2.5rem); }
.review__by {
  margin-top: auto;
  display: flex;
  align-items: center;
  gap: 0.9rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--ink-3);
}
.review__mono {
  flex: none;
  display: grid;
  place-items: center;
  width: 46px; height: 46px;
  border: 1px solid rgba(169, 113, 63, 0.7);
  font-family: var(--font-display);
  font-size: 1.15rem;
  letter-spacing: 0.04em;
  color: var(--bg);
  background: rgba(169, 113, 63, 0.1);
}
.review__who { display: grid; gap: 0.1rem; }
.review .quote__name { color: var(--bg); }
.review .quote__loc { color: var(--muted-ink); }
@media (min-width: 860px) and (max-width: 1100px) {
  .review__text { font-size: 1rem; }
  .review__project { letter-spacing: 0.12em; white-space: nowrap; }
}
@media (prefers-reduced-motion: reduce) {
  .review { transition: none; }
  .review:hover { transform: none; }
}

/* Footer bottom row: showcase line left, credit plus mark right. Phones
   stack them, centred. */
.footer__bottom { align-items: center; justify-content: space-between; flex-wrap: nowrap; }
.footer__showcase { margin: 0; font-size: var(--fs-small); color: var(--muted-ink); white-space: nowrap; }
.footer__copy { margin-left: auto; }
@media (max-width: 640px) {
  .footer__bottom { flex-direction: column; align-items: center; justify-content: center; text-align: center; gap: 0.75rem; flex-wrap: wrap; }
  .footer__copy { margin-left: 0; }
}
@media (max-width: 420px) { .footer__showcase { font-size: 0.75rem; } }
