/* ──────────────────────────────────────────────────────────────
   Rayan HealthInfra+ — concept homepage.
   Colour, easing and shell tokens are lifted from Rayan Architects
   V2 so this reads as the same practice. Two things are deliberately
   not inherited: the accent, which becomes a healthcare green, and
   the type scale, which is rebuilt as a single ratio rather than the
   per-component clamps the parent site accumulated.
   ────────────────────────────────────────────────────────────── */
:root {
  --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
  --ease-out-expo:  cubic-bezier(0.16, 1, 0.3, 1);

  /* Text-safe green. 4.83:1 on --bg and 5.36:1 carrying white, so it
     clears AA as body type on the cream, as large type anywhere, and
     as a filled band carrying white. This is the brand colour and it
     is used on structure: numerals, buttons, bands. */
  --accent:         #15795C;
  /* Graphic-only green. 2.85:1 on --bg, below the text floor, so it is
     confined to rules, seams, dots and marks on dark grounds. */
  --accent-lift:    #2FA37B;
  /* Lighter still, for small type over the dark hero imagery only. */
  --accent-on-dark: #7FD6B4;

  --bg:             #F5F3ED;
  --cream:          #FAF8F3;
  --ink:            #1A1A1A;
  --muted:          #6B6B6B;              /* 4.80:1 on --bg */
  --hairline:       rgba(0, 0, 0, 0.08);
  --hairline-strong:rgba(0, 0, 0, 0.18);

  /* ── Type. One major-third ladder (×1.25) off a 1.18rem lead, so every
     display size is a step on the same scale instead of a number picked
     per component. Line-height and tracking follow the step: tighter as
     the step rises, which is the only reason either should vary. ── */
  --t-0: 0.72rem;
  --t-1: 0.86rem;
  --t-2: clamp(1rem, 0.94rem + 0.28vw, 1.18rem);
  --t-3: clamp(1.18rem, 1.02rem + 0.62vw, 1.48rem);
  --t-4: clamp(1.4rem, 1.08rem + 1.05vw, 1.84rem);
  --t-5: clamp(1.65rem, 1.02rem + 1.9vw, 2.3rem);
  --t-6: clamp(1.95rem, 1.08rem + 2.9vw, 2.88rem);
  --t-7: clamp(2.3rem, 1.05rem + 4.2vw, 3.6rem);
  --t-8: clamp(2.7rem, 0.95rem + 5.9vw, 4.5rem);
  --t-9: clamp(3.05rem, 0.7rem + 7.9vw, 5.63rem);
  --t-10:clamp(2.6rem, 0.2rem + 10.4vw, 7.03rem);

  --lh-tight: 0.92; --lh-snug: 0.98; --lh-mid: 1.12; --lh-copy: 1.6;
  --ls-10: -0.042em; --ls-9: -0.04em;  --ls-8: -0.036em;
  --ls-7:  -0.03em;  --ls-6: -0.024em; --ls-5: -0.02em; --ls-4: -0.016em;

  --gutter:         clamp(1.25rem, 5vw, 5rem);
  --shell:          1400px;
}

@font-face { font-family: 'Satoshi'; src: url('../vendor/satoshi/satoshi-300.woff2') format('woff2'); font-weight: 300; font-style: normal; font-display: swap; }
@font-face { font-family: 'Satoshi'; src: url('../vendor/satoshi/satoshi-400.woff2') format('woff2'); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: 'Satoshi'; src: url('../vendor/satoshi/satoshi-500.woff2') format('woff2'); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: 'Satoshi'; src: url('../vendor/satoshi/satoshi-700.woff2') format('woff2'); font-weight: 700; font-style: normal; font-display: swap; }

*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; overflow-x: clip; overscroll-behavior-x: none; }
html { scroll-padding-top: 5rem; }
body {
  font-family: 'Satoshi', system-ui, -apple-system, 'Segoe UI', sans-serif;
  color: var(--ink); background: var(--bg);
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
  font-kerning: normal; font-feature-settings: "kern" 1, "liga" 1, "calt" 1;
  overflow-x: clip; letter-spacing: -0.005em;
  font-size: var(--t-2); line-height: var(--lh-copy);
}
h1, h2, h3, p { text-wrap: pretty; }
h1, h2 { text-wrap: balance; }
img, video, canvas { display: block; max-width: 100%; }
::selection { background: var(--accent); color: #FFF; }
::-moz-selection { background: var(--accent); color: #FFF; }
.tabular { font-variant-numeric: tabular-nums; }
.u-visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
a:focus-visible, button:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 2px;
}
/* Over the dark hero the ink-green ring disappears, so interactive
   elements on that ground take the light ring instead. */
.hero a:focus-visible, .nav:not(.is-scrolled) a:focus-visible,
.foot a:focus-visible, .band a:focus-visible,
.nav:not(.is-scrolled) button:focus-visible,
.menu a:focus-visible, .menu button:focus-visible,
.foot button:focus-visible {
  outline-color: #FFF;
}

/* The shell grows past the design width rather than leaving a fixed
   column stranded in the middle of a very wide display. */
.container { max-width: var(--shell); margin: 0 auto; padding: 0 var(--gutter); }
@media (min-width: 1700px) { :root { --shell: 1560px; } }
@media (min-width: 2100px) { :root { --shell: 1760px; --gutter: 6rem; } }

/* ── Skip link ─────────────────────────────────────────────── */
.skip {
  position: absolute; left: 1rem; top: -4rem; z-index: 200;
  background: var(--ink); color: var(--bg); padding: 0.75rem 1.15rem;
  border-radius: 999px; font-size: var(--t-1); letter-spacing: 0.08em;
  text-transform: uppercase; text-decoration: none;
  transition: top 250ms var(--ease-out-quart);
}
.skip:focus { top: 1rem; }

/* ── Preloader ─────────────────────────────────────────────── */
.pre {
  position: fixed; inset: 0; z-index: 150; background: var(--ink);
  display: flex; align-items: center; justify-content: center;
  transition: opacity 700ms var(--ease-out-quart);
}
.pre__inner { width: min(420px, 68vw); text-align: center; }
.pre__label {
  font-size: 0.62rem; letter-spacing: 0.26em; text-transform: uppercase;
  color: rgba(245, 243, 237, 0.72); margin: 0 0 1.1rem;
}
.pre__rule { height: 1px; background: rgba(245, 243, 237, 0.2); overflow: hidden; }
.pre__rule i { display: block; height: 100%; width: 0%; background: var(--accent-lift); }
.pre__num {
  margin: 1.1rem 0 0; font-size: 0.62rem; letter-spacing: 0.2em;
  color: rgba(245, 243, 237, 0.72); font-variant-numeric: tabular-nums;
}
body.is-lit .pre { opacity: 0; pointer-events: none; }

/* ── Nav ───────────────────────────────────────────────────── */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 80;
  padding: max(1.4rem, env(safe-area-inset-top, 0px)) clamp(1.25rem, 3.5vw, 3rem);
  display: flex; justify-content: center; pointer-events: none;
  transition: padding 450ms var(--ease-out-expo);
}
.nav::before {
  content: ''; position: absolute; inset: 0 0 auto 0; height: 190%;
  background: linear-gradient(180deg, rgba(8,12,10,0.50) 0%, rgba(8,12,10,0.24) 46%, transparent 100%);
  /* Frosted only at rest. The blur has to be masked to nothing at its lower
     edge or the band ends on a visible horizontal seam across the render. */
  backdrop-filter: blur(11px) saturate(1.25);
  -webkit-backdrop-filter: blur(11px) saturate(1.25);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 42%, transparent 100%);
          mask-image: linear-gradient(180deg, #000 0%, #000 42%, transparent 100%);
  pointer-events: none; opacity: 1;
  transition: opacity 520ms var(--ease-out-quart);
}
.nav.is-scrolled::before { opacity: 0; }
/* While the hero is scrubbing, the band leaves on the same number that
   carries the title card off, so the two halves of the opening move as one
   gesture instead of on a 40px threshold and a progress ratio that disagree.
   Equal specificity to the rule above, declared after it, so it wins; the
   class is only set when there is a scrub to drive it. */
.nav.is-driven::before { opacity: var(--band, 1); }
.nav__pill {
  pointer-events: auto; display: flex; align-items: center; gap: 2.5rem;
  width: 100%; max-width: none; padding: 0.3rem 0;
  background: transparent; border: 1px solid transparent; border-radius: 999px;
  backdrop-filter: blur(0px); -webkit-backdrop-filter: blur(0px);
  /* Every geometric property runs on one clock and one curve, so the pill
     reads as a single object contracting rather than four properties racing.
     The surface (fill, border, blur, shadow) is held back 90ms and crossfades
     faster, which is what makes the glass look like it forms around the
     contents once they have settled. */
  transition:
    max-width 760ms var(--ease-out-expo), padding 760ms var(--ease-out-expo),
    gap 760ms var(--ease-out-expo),
    background 380ms ease 90ms, border-color 380ms ease 90ms,
    backdrop-filter 380ms ease 90ms, -webkit-backdrop-filter 380ms ease 90ms,
    box-shadow 420ms ease 90ms;
}
.nav.is-scrolled { padding: max(1rem, env(safe-area-inset-top, 0px)) 2rem; }
.nav.is-scrolled .nav__pill {
  max-width: 780px; padding: 0.5rem 0.55rem 0.5rem 1.2rem; gap: 1.5rem;
  background: rgba(255, 255, 255, 0.58);
  border-color: rgba(255, 255, 255, 0.5);
  backdrop-filter: blur(22px) saturate(1.8);
  -webkit-backdrop-filter: blur(22px) saturate(1.8);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.6),
              0 8px 32px rgba(0,0,0,0.10), 0 1px 2px rgba(0,0,0,0.06);
}
.nav__mark {
  display: inline-flex; align-items: baseline; gap: 0.06em;
  color: var(--bg); text-decoration: none;
  font-size: var(--t-2); font-weight: 500; letter-spacing: var(--ls-4);
  /* Two elements in a flex row: without this the wordmark breaks across
     two lines in the narrow pill and the plus floats away from it. */
  white-space: nowrap; flex-shrink: 0;
  transition: color 350ms ease;
}
.nav.is-scrolled .nav__mark { color: var(--ink); }
.nav__mark b { font-weight: 500; }
.nav__mark i { font-style: normal; color: var(--accent-on-dark); }
.nav.is-scrolled .nav__mark i { color: var(--accent); }
.nav__links { display: flex; gap: 1.9rem; align-items: center; margin-left: auto;
               transition: gap 760ms var(--ease-out-expo); }
.nav.is-scrolled .nav__links { gap: 1.5rem; }
.nav__link {
  position: relative; font-size: 0.78rem; font-weight: 400;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--bg);
  text-decoration: none; padding: 0.25rem 0;
  /* The links settle into the pill last, one after another from the left, so
     the collapse has a direction instead of everything arriving at once. */
  transform: translateX(16px);
  transition: color 320ms ease, transform 820ms var(--ease-out-expo);
}
.nav.is-scrolled .nav__link { transform: translateX(0); }
.nav__link:nth-child(2) { transition-delay: 0ms, 70ms; }
.nav__link:nth-child(3) { transition-delay: 0ms, 140ms; }
.nav.is-scrolled .nav__link { color: var(--ink); }
.nav__link::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -2px; height: 1px;
  background: var(--accent-lift); transform: scaleX(0);
  transform-origin: left center; transition: transform 350ms var(--ease-out-expo);
}
.nav__link:hover::after { transform: scaleX(1); }
.nav__cta {
  display: inline-flex; align-items: center; gap: 0.55rem;
  background: transparent; color: var(--bg);
  border: 1px solid rgba(255,255,255,0.55);
  padding: 0.7rem 1.4rem; border-radius: 999px;
  font-size: 0.8rem; font-weight: 500; letter-spacing: 0.14em;
  text-transform: uppercase; text-decoration: none; white-space: nowrap;
  transition: transform 250ms var(--ease-out-quart), background-color 250ms var(--ease-out-quart),
              border-color 350ms ease, color 250ms var(--ease-out-quart), box-shadow 350ms var(--ease-out-quart);
}
/* Green, not ink. The accent has to hold a structural surface or it
   never reads as the brand. 5.36:1 carrying white. */
.nav.is-scrolled .nav__cta {
  background: var(--accent); color: #FFF; border-color: var(--accent);
  box-shadow: 0 12px 28px -16px rgba(21,121,92,0.55);
}
.nav__cta .dot { width: 6px; height: 6px; border-radius: 999px; background: var(--accent-lift);
                 transition: transform 300ms var(--ease-out-quart), background-color 250ms ease; }
.nav__cta-short { display: none; }
.nav__menu {
  display: none; background: none; border: 0; padding: 0.55rem 0.2rem; cursor: pointer;
  font-family: inherit; font-size: 0.68rem; font-weight: 400; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--bg); margin-left: auto;
  transition: color 320ms ease;
}
.nav.is-scrolled .nav__menu { color: var(--ink); }
@media (max-width: 767px) { .nav__menu { display: inline-block; } }
/* Both of these carry a display that would otherwise beat the UA rule for
   [hidden], and both are hidden in the markup so a no-JS visitor never gets a
   dead control or a panel with no way out. */
.nav__menu[hidden], .menu[hidden] { display: none; }
@media (max-width: 400px) {
  .nav__mark { font-size: 0.82rem; }
  .nav { padding-left: 0.75rem; padding-right: 0.75rem; }
}
@media (max-width: 359px) {
  /* Only here do three controls stop fitting beside the wordmark. The CTA is
     the one that can go: the panel behind Menu carries Start a project, the
     phone number and the address. Every common phone width keeps it. */
  .nav__cta { display: none; }
}
.nav.is-scrolled .nav__cta .dot { background: #FFF; }
.nav__cta:active { transform: scale(0.97); }
@media (hover: hover) {
  .nav__cta:hover { transform: scale(1.02); background: var(--accent); color: #FFF;
                    border-color: var(--accent); box-shadow: 0 18px 36px -16px rgba(21,121,92,0.6); }
  .nav__cta:hover .dot { background: #FFF; }
}
@media (max-width: 767px) {
  /* .nav__links carries the margin-left:auto that pushes the CTA right, so
     hiding it alone would collapse the CTA against the mark. The Menu button
     takes the push over, and the links move into the panel. */
  .nav__links { display: none; }
  .nav__cta { padding: 0.58rem 0.9rem; font-size: 0.62rem; letter-spacing: 0.08em; }
  .nav__mark { font-size: 0.88rem; }
  .nav__pill { gap: 0.75rem; }
  .nav { padding-top: max(1rem, env(safe-area-inset-top, 0px)); }
  .nav.is-scrolled .nav__pill { max-width: calc(100% - 0.5rem); padding: 0.45rem 0.45rem 0.45rem 1rem; }
}

/* ── Menu. Phone navigation. The panel covers the header outright and
   carries its own mark and close, so it never has to reach into the nav's
   scrolled and unscrolled states to keep itself legible. ── */
.menu {
  position: fixed; inset: 0; z-index: 120; background: var(--ink);
  display: flex; flex-direction: column;
  padding: max(1.4rem, env(safe-area-inset-top, 0px)) var(--gutter)
           calc(2rem + env(safe-area-inset-bottom, 0px));
  opacity: 0; transition: opacity 420ms var(--ease-out-quart);
}
.menu.is-open { opacity: 1; }
.menu__bar { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.menu__mark {
  margin: 0; color: var(--bg); font-size: 0.88rem; font-weight: 500;
  letter-spacing: var(--ls-4); white-space: nowrap;
}
.menu__mark i { font-style: normal; color: var(--accent-on-dark); }
.menu__x {
  background: none; border: 0; cursor: pointer; font-family: inherit;
  padding: 0.6rem 0.2rem 0.6rem 1rem; color: var(--bg);
  font-size: 0.68rem; letter-spacing: 0.14em; text-transform: uppercase;
}
.menu__nav { margin-top: auto; margin-bottom: auto; display: grid; gap: 0.2rem; }
.menu__link {
  display: block; overflow: hidden; text-decoration: none;
  color: #FFF; font-weight: 500; font-size: clamp(2.1rem, 11vw, 3.1rem);
  line-height: 1.24; letter-spacing: var(--ls-6);
}
.menu__link > span {
  display: block; transform: translateY(105%); padding-bottom: 0.14em;
  transition: transform 760ms var(--ease-out-expo), color 300ms ease;
}
.menu.is-open .menu__link > span { transform: translateY(0); }
.menu.is-open .menu__link:nth-child(1) > span { transition-delay: 60ms; }
.menu.is-open .menu__link:nth-child(2) > span { transition-delay: 120ms; }
.menu.is-open .menu__link:nth-child(3) > span { transition-delay: 180ms; }
.menu__nav .menu__link:last-child > span { color: var(--accent-on-dark); }
.menu__foot {
  display: flex; flex-direction: column; gap: 0.45rem;
  padding-top: 1.6rem; border-top: 1px solid rgba(245,243,237,0.16);
}
.menu__meta {
  color: rgba(245,243,237,0.78); font-size: var(--t-1); text-decoration: none;
  padding: 0.3rem 0;
}
@media (hover: hover) { .menu__meta:hover { color: #FFF; } }
@media (prefers-reduced-motion: reduce) {
  .menu { opacity: 1; }
  .menu__link > span { transform: none; }
}

/* ── Hero ──────────────────────────────────────────────────
   The stage is sticky rather than GSAP-pinned: the browser holds it in
   place with no layout shift and no pin-spacer, and ScrollTrigger is
   left to do nothing but report progress. Travel is set so each frame
   gets about 25px of scroll, the density at which a scrubbed sequence
   stops reading as a slideshow. */
.hero { position: relative; background: var(--ink); height: calc(100svh + 2200px); }
.hero__stage {
  position: sticky; top: 0; height: 100vh; height: 100svh; width: 100%;
  overflow: hidden; background: var(--ink);
}
@media (max-width: 899px) { .hero { height: calc(100svh + 1250px); } }
.hero--static { height: 100vh; height: 100svh; }

.hero__canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.hero__poster {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; filter: blur(18px); transform: scale(1.08);
  opacity: 1; transition: opacity 500ms ease;
}
body.is-lit .hero__poster { opacity: 0; }
/* Promoted from blurred placeholder to the hero itself when the sequence
   cannot run: no AVIF support, no canvas context, or no frames fetched. */
.hero__poster.is-fallback { filter: none; transform: none; opacity: 1; }
body.is-lit .hero__poster.is-fallback { opacity: 1; }

.hero__scrim {
  position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(180deg, rgba(10,14,12,0.46) 0%, rgba(10,14,12,0.10) 24%,
                    rgba(10,14,12,0.04) 48%, rgba(10,14,12,0.62) 100%);
}
/* Beat copy sits over a bright facade for part of the scrub, so it carries
   its own left wash that is only painted while a beat is on screen. */
.hero__beatwash {
  position: absolute; inset: 0; pointer-events: none; opacity: 0;
  background: linear-gradient(90deg, rgba(10,14,12,0.88) 0%, rgba(10,14,12,0.62) 26%, rgba(10,14,12,0.22) 50%, transparent 70%);
  transition: opacity 700ms var(--ease-out-quart);
}
.hero__stage[data-beat] .hero__beatwash { opacity: 1; }

.hero__stack { display: contents; }
.hero__beats {
  position: absolute; left: var(--gutter); top: 50%; transform: translateY(-50%);
  width: min(30rem, 62vw); pointer-events: none;
}
.beat { position: absolute; left: 0; top: 0; width: 100%; opacity: 0; }
.beat__n {
  display: block; font-size: 0.62rem; letter-spacing: 0.28em;
  color: var(--accent-on-dark); margin: 0 0 0.9rem; font-variant-numeric: tabular-nums;
  text-shadow: 0 1px 12px rgba(10, 14, 12, 0.7);
}
.beat__t {
  margin: 0; font-weight: 500; line-height: var(--lh-tight); letter-spacing: var(--ls-9);
  font-size: var(--t-9); color: #FFF;
  /* The sequence runs from a dark blueprint to a bright dusk render, so the
     wash alone cannot be tuned for both ends. A shadow carries the rest. */
  text-shadow: 0 2px 24px rgba(10, 14, 12, 0.55);
}
.beat__s {
  margin: 1rem 0 0; font-size: var(--t-2); line-height: 1.5;
  color: rgba(255,255,255,0.88); max-width: 26ch;
  text-shadow: 0 1px 14px rgba(10, 14, 12, 0.6);
}

.hero__foot {
  position: absolute; left: var(--gutter); right: var(--gutter); bottom: clamp(3.8rem, 8vh, 5.6rem);
  display: flex; flex-direction: column; align-items: stretch;
  gap: clamp(0.9rem, 2.2vh, 1.7rem); pointer-events: none;
  /* Leaves on the first pixel of scroll. Driven from renderHero() so it is
     tied to sequence progress rather than to a second, unsynced listener. */
  will-change: opacity, transform;
}
.hero__mark {
  margin: 0; color: #FFF; font-weight: 500; line-height: 0.95;
  /* Deliberately off the --t ladder. Every other display size on the page is
     a step on the ratio; this one is fitted to a fixed eighteen character
     string, and its whole job is to be larger than --t-10 at every width. Its
     vw coefficient is therefore higher than the ladder's and its floor sits
     above the ladder's floor. */
  letter-spacing: -0.045em; font-size: clamp(3rem, 12.5vw, 8.8rem);
  text-shadow: 0 2px 40px rgba(10, 14, 12, 0.5);
}
.hero__mark i { font-style: normal; color: var(--accent-on-dark); }

/* ── Featured project line ──────────────────────────────────
   A single spaced rule across the middle of the stage rather than a caption
   parked in a corner: label, project, place, and the one thing there is to
   do. It belongs to the title card, so it leaves on the first pixel of
   scroll and hands the midline to the beats. */
.hero__feat {
  position: absolute; left: var(--gutter); right: var(--gutter);
  /* Centred by auto block margins rather than a translate, so the scroll
     handler owns the transform outright and the mobile override, which is
     not centred at all, does not have to fight it. */
  top: 0; bottom: 0; margin-block: auto; height: max-content;
  display: flex; align-items: center; gap: clamp(0.9rem, 2.2vw, 2.2rem);
  pointer-events: none; will-change: opacity, transform;
}
.hero__feat-i {
  font-size: 0.68rem; letter-spacing: 0.2em; text-transform: uppercase;
  color: #FFF; white-space: nowrap; flex-shrink: 0;
  /* This line crosses the middle of the frame, which over the finished render
     is pale stone in full sun. A single soft shadow is not enough there: a
     tight one holds the letterform edges, a wide one kills the ground. */
  text-shadow: 0 1px 2px rgba(10, 14, 12, 0.9), 0 1px 20px rgba(10, 14, 12, 0.8);
}
.hero__feat-r { flex: 1 1 auto; height: 1px; min-width: 1.5rem; background: rgba(255,255,255,0.42);
                box-shadow: 0 1px 10px rgba(10,14,12,0.55); }
.hero__feat-link {
  pointer-events: auto; display: inline-flex; align-items: center; gap: 0.55rem;
  text-decoration: none; color: #FFF;
  /* The label is 0.68rem of tracked caps. Without padding the hit area is
     about eleven pixels tall, well under the 24px floor. */
  padding: 0.7rem 0; margin: -0.7rem 0;
  transition: color 300ms var(--ease-out-quart);
}
.hero__feat-dot {
  width: 6px; height: 6px; border-radius: 999px; background: var(--accent-on-dark);
  transition: transform 420ms var(--ease-out-expo), background-color 300ms ease;
}
@media (hover: hover) { .hero__feat-link:hover .hero__feat-dot { transform: scale(1.9); } }
@media (hover: none) { .hero__feat-link:active .hero__feat-dot { transform: scale(1.9); } }
.hero__bar { position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: rgba(255,255,255,0.16); }
.hero__bar i { display: block; height: 100%; width: 0%; background: var(--accent-lift); }
.hero__cue {
  position: absolute; left: 50%; bottom: clamp(1.1rem, 2.4vh, 1.7rem);
  transform: translateX(-50%);
  font-size: 0.58rem; letter-spacing: 0.28em; text-transform: uppercase;
  color: rgba(255,255,255,0.8); pointer-events: none;
  text-shadow: 0 1px 12px rgba(10, 14, 12, 0.7);
}
@media (max-width: 899px) {
  /* Beats and wordmark were each absolutely positioned against the stage, one
     anchored in vh and one in rem, so they converged as the viewport got
     shorter and the beat body struck through the wordmark. On phones they
     become one bottom-anchored column instead, which cannot overlap at any
     height. The beats keep their own stacking context because the three
     states sit on top of each other. */
  .hero__stack {
    display: flex; flex-direction: column; gap: clamp(1.5rem, 5vh, 2.75rem);
    position: absolute; left: var(--gutter); right: var(--gutter);
    bottom: clamp(3.4rem, 8vh, 5rem); pointer-events: none;
  }
  .hero__beats {
    position: relative; left: auto; top: auto; bottom: auto; transform: none;
    width: 100%; min-height: 11rem;
  }
  .beat { top: auto; bottom: 0; }
  .hero__foot {
    position: relative; left: auto; right: auto; bottom: auto;
    align-items: flex-start; gap: 0.85rem;
  }
  .hero__feat-i { font-size: 0.62rem; letter-spacing: 0.17em; }
  .hero__beatwash { background: linear-gradient(0deg, rgba(10,14,12,0.9) 12%, rgba(10,14,12,0.52) 46%, transparent 78%); }
}
@media (max-width: 699px) {
  /* Four items cannot hold a single line here, and at mid height the line
     would land on the bottom stack. It wraps two up and moves under the nav,
     which is empty at every phone height. */
  .hero__feat {
    top: clamp(5.2rem, 13vh, 7.5rem); bottom: auto; margin-block: 0;
    flex-wrap: wrap; gap: 0.55rem clamp(0.8rem, 3vw, 1.4rem);
    justify-content: space-between;
  }
  .hero__feat-r { display: none; }
  .hero__feat-i { font-size: 0.6rem; letter-spacing: 0.16em; }
}
@media (max-width: 430px), (max-width: 899px) and (max-height: 560px) {
  /* Three ragged rows at phone width, and a second row at landscape heights
     where the rule has nowhere to put one. The label is the one item carrying
     no information the others do not already give. */
  .hero__feat-label { display: none; }
}

/* A phone held in landscape keeps the mobile layout on a viewport barely
   400px tall. The beat body is the first thing to give. */
@media (max-width: 899px) and (max-height: 560px) {
  .beat__s { display: none; }
  .beat__t { font-size: 2.1rem; }
  .beat__n { margin-bottom: 0.5rem; }
  /* The reserved beat shelf has to shrink with the copy it holds, or the
     stack grows taller than the viewport and pushes the beats under the nav. */
  .hero__beats { min-height: 4.6rem; }
  .hero__stack { gap: 1rem; bottom: 2.2rem; }
  /* The display sizes on this page are width-fluid only, so on a viewport
     390px tall --t-10 still resolves to 91px: one word of a section heading
     taking a quarter of the screen. Here, and only here, both the step and the
     wordmark take a second ceiling derived from the height rather than a flat
     replacement size, so the reduction is proportional to how short the
     viewport actually is instead of a fixed cut. The two ceilings are set in
     the same ratio, which is what keeps the wordmark ahead of the step at
     every width in the band. */
  :root { --t-10: clamp(2.4rem, min(0.2rem + 10.4vw, 15vh), 7.03rem); }
  .hero__mark { font-size: clamp(2.65rem, min(12.5vw, 17vh), 8.8rem); }
  /* Freed of the midline, the wordmark can take the height the ceiling allows
     without the featured rule landing on the beat shelf above it. It has to
     clear the nav, which is only about 80px down in this band, and it has to
     hold one row: two rows here would reach the shelf. Dropping the label is
     what buys the row back at 568px. */
  .hero__feat { top: clamp(6.2rem, 26vh, 7.5rem); bottom: auto; margin-block: 0; }
  /* In landscape the wordmark runs most of the width, and a centred cue lands
     on its last glyphs. It moves to the corner instead. */
  .hero__cue { left: auto; right: var(--gutter); transform: none; }
}

/* ── Section furniture ─────────────────────────────────────── */
.sec { padding: clamp(5rem, 14vh, 11rem) 0; }
.sec--cream { background: var(--cream); }
/* The counter has to be able to drop below the title. At 320px a display
   title and a tracked label cannot share a row, and the label was being
   pushed past the viewport edge where overflow-x:clip hid it entirely. */
.head {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem clamp(1rem, 2vw, 2rem); margin-bottom: clamp(2.5rem, 6vh, 4.5rem);
}
.band__head { flex-wrap: wrap; }
.head__t { font-weight: 500; font-size: var(--t-10); line-height: var(--lh-tight); letter-spacing: var(--ls-10); margin: 0; }
.head__c {
  font-weight: 500; font-size: var(--t-1); letter-spacing: 0.18em;
  font-variant-numeric: tabular-nums; color: var(--muted); flex-shrink: 0;
}
.over { font-size: var(--t-0); letter-spacing: 0.24em; text-transform: uppercase; color: var(--accent); margin: 0 0 1.2rem; }

/* ── Statement ─────────────────────────────────────────────── */
.stmt__grid { display: grid; gap: clamp(1.5rem, 4vw, 4rem); }
@media (min-width: 860px) { .stmt__grid { grid-template-columns: 1fr 4fr; } }
.stmt__p {
  font-weight: 400; font-size: var(--t-7); line-height: var(--lh-mid);
  letter-spacing: var(--ls-7); margin: 0;
  /* Measure has to hold after JS splits this into word spans, or the
     single most important sentence runs 40ch-plus on a wide display. */
  max-width: 20ch;
}
.stmt__p .w { opacity: 0.24; transition: opacity 200ms linear; }
.stmt__sub { margin: 1.8rem 0 0; font-size: var(--t-3); line-height: var(--lh-copy); color: var(--muted); max-width: 46ch; }

/* ── Proof. The numerals are the page's largest green field: at these
   sizes --accent clears AA by a wide margin and the band is where the
   brand colour has to register. ── */
.proof { border-top: 1px solid var(--hairline); border-bottom: 1px solid var(--hairline); }
.proof__grid { display: grid; grid-template-columns: repeat(3, 1fr); }
.proof__i { padding: clamp(2rem, 5vh, 3.4rem) 0; border-left: 1px solid var(--hairline); padding-left: clamp(1rem, 2.4vw, 2rem); }
.proof__i:first-child { border-left: 0; padding-left: 0; }
.proof__n {
  font-weight: 500; font-size: var(--t-9); line-height: var(--lh-tight);
  letter-spacing: var(--ls-9); margin: 0; font-variant-numeric: tabular-nums;
  color: var(--accent);
}
.proof__l { margin: 0.8rem 0 0; font-size: var(--t-0); letter-spacing: 0.18em; text-transform: uppercase; color: var(--muted); }
@media (max-width: 759px) {
  .proof__grid { grid-template-columns: 1fr; }
  .proof__i { border-left: 0; padding-left: 0; border-top: 1px solid var(--hairline); padding: 1.6rem 0; }
  .proof__i:first-child { border-top: 0; }
}

/* ── Capability ────────────────────────────────────────────── */
.cap__grid { display: grid; gap: clamp(2rem, 4vw, 4rem); }
@media (min-width: 760px) { .cap__grid { grid-template-columns: repeat(3, 1fr); } }
.cap__i { position: relative; padding-top: 1.5rem; border-top: 1px solid var(--hairline-strong); }
.cap__i::before {
  content: ''; position: absolute; left: 0; top: -1px; width: 0; height: 1px;
  background: var(--accent-lift); transition: width 900ms var(--ease-out-expo);
}
.cap__i.is-in::before { width: 100%; }
.cap__n { font-size: 0.66rem; letter-spacing: 0.22em; color: var(--accent); margin: 0 0 1.3rem; font-variant-numeric: tabular-nums; }
.cap__t { font-weight: 500; font-size: var(--t-5); line-height: var(--lh-mid); letter-spacing: var(--ls-5); margin: 0; }
.cap__b { margin: 0.9rem 0 0; font-size: var(--t-2); line-height: var(--lh-copy); color: var(--muted); max-width: 34ch; }

/* ── Work ──────────────────────────────────────────────────── */
.work__lead { margin-bottom: clamp(1.2rem, 3vh, 2rem); }
.tile { position: relative; display: block; margin: 0; }
.tile__media { position: relative; overflow: hidden; background: var(--hairline); border-radius: 2px; aspect-ratio: 4 / 3; }
.tile__media::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, transparent 55%, rgba(10,14,12,0.30) 100%);
  opacity: 0; transition: opacity 500ms var(--ease-out-quart);
}
.tile:hover .tile__media::after { opacity: 1; }
.tile__img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transform: scale(1.035); transition: transform 900ms var(--ease-out-expo);
}
@media (hover: hover) { .tile:hover .tile__img { transform: scale(1.075); } }
/* A caption shelf tall enough for a two-line project name, so one long
   title does not push its caption block past its row neighbour's and break
   the shared baseline. The location is allowed to wrap onto its own line
   rather than squeezing the title into a third line in a narrow column,
   which is the case that would overrun the shelf. */
.tile__cap {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: 0.3rem 1rem;
  padding-top: 0.9rem; min-height: 3.4em;
}
.tile__n { flex: 1 1 11ch; }
.tile__n { margin: 0; font-weight: 500; font-size: var(--t-3); letter-spacing: var(--ls-4); line-height: 1.22; }
.tile__l { margin: 0; font-size: 0.64rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted); white-space: nowrap; flex-shrink: 0; }
.tile--lead .tile__media { aspect-ratio: 16 / 9; }
.tile--lead .tile__n { font-size: var(--t-5); }

.work__grid { display: grid; gap: clamp(1.4rem, 3vw, 2.6rem) clamp(1.2rem, 2.4vw, 2.4rem); margin-top: clamp(2.5rem, 6vh, 4.5rem); }
@media (min-width: 640px) { .work__grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) {
  /* A regular wide-narrow alternation on twelve columns. Every tile keeps
     the same 4:3 frame, and the offset that staggers the rows is a fixed
     step applied to the narrow tile, so the stagger reads as a rhythm
     rather than as three photos bottom-aligned by accident. */
  .work__grid { grid-template-columns: repeat(12, 1fr); }
  .work__grid .tile:nth-child(4n+1) { grid-column: span 7; }
  .work__grid .tile:nth-child(4n+2) { grid-column: span 5; margin-top: clamp(2rem, 5vw, 5.5rem); }
  .work__grid .tile:nth-child(4n+3) { grid-column: span 5; }
  .work__grid .tile:nth-child(4n+4) { grid-column: span 7; margin-top: clamp(2rem, 5vw, 5.5rem); }
}

/* ── Full-bleed plate ──────────────────────────────────────── */
.plate { position: relative; height: clamp(28rem, 80vh, 52rem); overflow: hidden; background: var(--ink); }
.plate__img { position: absolute; inset: -12% 0; width: 100%; height: 124%; object-fit: cover; }
/* The scrim carries a green cast so the brand colour sits on a full-bleed
   surface rather than only on small elements. */
.plate__scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgba(8,26,20,0.34) 0%, rgba(8,26,20,0.10) 42%, rgba(6,30,23,0.78) 100%);
}
.plate__body { position: absolute; inset: auto 0 0 0; padding: 0 0 clamp(2.5rem, 8vh, 5rem); }
.plate__t { margin: 0; color: #FFF; font-weight: 500; line-height: var(--lh-mid); letter-spacing: var(--ls-7); font-size: var(--t-7); max-width: 18ch; }
.plate__s { margin: 1.1rem 0 0; color: rgba(255,255,255,0.86); font-size: var(--t-2); line-height: var(--lh-copy); max-width: 46ch; }

/* ── Band. A static green field, not a scrolling word loop: it carries
   what the practice actually builds, and it is the page's largest area
   of brand colour. ── */
.band { background: var(--accent); color: #FFF; padding: clamp(3.5rem, 10vh, 7rem) 0; }
.band__head { display: flex; align-items: baseline; justify-content: space-between; gap: 1.5rem; margin-bottom: clamp(2rem, 5vh, 3.5rem); }
.band__t { margin: 0; font-weight: 500; font-size: var(--t-7); line-height: var(--lh-mid); letter-spacing: var(--ls-7); color: #FFF; }
.band__c { margin: 0; font-size: var(--t-0); letter-spacing: 0.2em; text-transform: uppercase; color: rgba(255,255,255,0.92); flex-shrink: 0; }
.band__grid { display: grid; gap: 0; border-top: 1px solid rgba(255,255,255,0.26); }
@media (min-width: 760px) { .band__grid { grid-template-columns: repeat(3, 1fr); } }
.band__i { padding: clamp(1.4rem, 3vh, 2.2rem) 0; border-bottom: 1px solid rgba(255,255,255,0.26); }
/* Stacked, the last item's rule dangles under the final line with nothing
   below it but section padding. */
.band__i:last-child { border-bottom: 0; }
@media (min-width: 760px) {
  .band__i { border-bottom: 0; border-left: 1px solid rgba(255,255,255,0.26); padding-left: clamp(1rem, 2.4vw, 2rem); padding-right: clamp(1rem, 2vw, 2rem); }
  .band__i:first-child { border-left: 0; padding-left: 0; }
}
.band__n { margin: 0 0 0.7rem; font-size: 0.66rem; letter-spacing: 0.22em; color: rgba(255,255,255,0.92); font-variant-numeric: tabular-nums; }
.band__k { margin: 0; font-weight: 500; font-size: var(--t-4); line-height: var(--lh-mid); letter-spacing: var(--ls-4); color: #FFF; }
.band__v { margin: 0.6rem 0 0; font-size: var(--t-2); line-height: var(--lh-copy); color: rgba(255,255,255,0.92); max-width: 30ch; }

/* ── Close ─────────────────────────────────────────────────── */
.close__grid { display: grid; gap: clamp(2rem, 4vw, 4rem); align-items: end; }
@media (min-width: 860px) { .close__grid { grid-template-columns: 3fr 2fr; } }
.close__t { margin: 0; font-weight: 500; font-size: var(--t-10); line-height: var(--lh-tight); letter-spacing: var(--ls-10); }
.close__b { margin: 1.5rem 0 0; font-size: var(--t-3); line-height: var(--lh-copy); color: var(--muted); max-width: 44ch; }
.btn {
  display: inline-flex; align-items: center; gap: 0.7rem;
  background: var(--accent); color: #FFF; border: 1px solid var(--accent);
  padding: 1.05rem 1.9rem; border-radius: 999px; text-decoration: none;
  font-size: 0.78rem; font-weight: 500; letter-spacing: 0.16em; text-transform: uppercase;
  box-shadow: 0 18px 38px -22px rgba(21,121,92,0.7);
  transition: transform 250ms var(--ease-out-quart), background-color 300ms var(--ease-out-quart),
              border-color 300ms ease, box-shadow 350ms var(--ease-out-quart);
}
.btn .dot { width: 6px; height: 6px; border-radius: 999px; background: #FFF;
             transition: transform 300ms var(--ease-out-quart); }
@media (hover: hover) {
  .btn:hover { background: var(--ink); border-color: var(--ink); box-shadow: 0 22px 46px -22px rgba(0,0,0,0.5); }
  .btn:hover .dot { transform: scale(1.7); }
}
/* A touch device never fires :hover, so every button on this page was inert
   under a finger. The press state carries the same colour move the pointer
   gets, on a curve slow enough to still be visible after the finger lifts. */
@media (hover: none) {
  .btn:active { transform: scale(0.965); background: var(--ink); border-color: var(--ink);
                box-shadow: 0 10px 26px -18px rgba(0,0,0,0.55); }
  .btn:active .dot { transform: scale(1.7); }
  .nav__cta:active { transform: scale(0.955); background: var(--accent); color: #FFF;
                     border-color: var(--accent); }
  .nav__cta:active .dot { background: #FFF; transform: scale(1.6); }
  .close__alt a:active, .foot__link:active { color: var(--ink); }
  .foot__link:active { color: #FFF; }
  .foot__up:active { transform: scale(0.96); color: #FFF; }
  .tile:active .tile__img { transform: scale(1.07); }
  .tile:active .tile__media::after { opacity: 1; }
}
.btn:active { transform: scale(0.975); }
.close__alt { margin: 1.15rem 0 0; font-size: var(--t-1); color: var(--muted); }
.close__alt a { color: var(--accent); text-decoration: none; border-bottom: 1px solid var(--accent-lift); padding-bottom: 1px; }
.close__alt a:hover { color: var(--ink); border-color: var(--ink); }

/* ── Footer ────────────────────────────────────────────────── */
.foot {
  background: var(--ink); color: rgba(245,243,237,0.82);
  padding: clamp(4rem, 10vh, 7rem) 0 clamp(2rem, 5vh, 3rem);
}
.foot__top {
  display: grid; gap: clamp(1.2rem, 3vw, 3.5rem);
  padding-bottom: clamp(2.5rem, 6vh, 4rem);
  border-bottom: 1px solid rgba(245,243,237,0.14);
}
@media (min-width: 900px) { .foot__top { grid-template-columns: 1.15fr 1fr; align-items: end; } }
.foot__mark {
  margin: 0; color: #FFF; font-weight: 500;
  font-size: clamp(2rem, 5.2vw, 4.2rem); letter-spacing: var(--ls-7); line-height: 1.05;
}
.foot__mark i { font-style: normal; color: var(--accent-lift); }
/* 8.2:1 on the ink ground. */
.foot__note { margin: 0; font-size: var(--t-2); line-height: var(--lh-copy); color: rgba(245,243,237,0.7); max-width: 44ch; }

.foot__cols {
  display: grid; gap: clamp(2rem, 4vw, 3rem) clamp(1.5rem, 3vw, 3rem);
  padding: clamp(2.5rem, 6vh, 4rem) 0;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 900px) { .foot__cols { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 479px) { .foot__cols { grid-template-columns: minmax(0, 1fr); gap: 2.2rem; } }
.foot__ch {
  margin: 0 0 1.1rem; font-size: var(--t-0); letter-spacing: 0.22em;
  text-transform: uppercase; color: var(--accent-lift);
}
.foot__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.62rem; }
.foot__list--plain li { font-size: var(--t-1); line-height: 1.45; color: rgba(245,243,237,0.7); }
.foot__link {
  position: relative; display: inline-flex; align-items: baseline; gap: 0.35rem;
  font-size: var(--t-1); line-height: 1.45; color: rgba(245,243,237,0.82);
  text-decoration: none; transition: color 320ms var(--ease-out-quart);
}
.foot__link::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -2px; height: 1px;
  background: var(--accent-lift); transform: scaleX(0); transform-origin: left center;
  transition: transform 520ms var(--ease-out-expo);
}
@media (hover: hover) {
  .foot__link:hover { color: #FFF; }
  .foot__link:hover::after { transform: scaleX(1); }
}
.foot__link .arr { font-size: 0.8em; color: var(--accent-lift); }
.foot__addr { margin: 0; font-size: var(--t-1); line-height: 1.75; color: rgba(245,243,237,0.7); }

.foot__socs { display: flex; gap: 0.6rem; margin-top: 1.6rem; }
.foot__soc {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.35rem; height: 2.35rem; border-radius: 999px;
  border: 1px solid rgba(245,243,237,0.22); color: rgba(245,243,237,0.82);
  transition: color 300ms ease, border-color 300ms ease, background-color 300ms ease, transform 420ms var(--ease-out-expo);
}
.foot__soc svg { width: 1.05rem; height: 1.05rem; }
@media (hover: hover) {
  .foot__soc:hover { color: var(--ink); background: var(--accent-lift); border-color: var(--accent-lift); transform: translateY(-2px); }
}
@media (hover: none) { .foot__soc:active { color: var(--ink); background: var(--accent-lift); border-color: var(--accent-lift); } }

.foot__bottom {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem 1.75rem;
  padding-top: clamp(1.5rem, 4vh, 2.2rem);
  border-top: 1px solid rgba(245,243,237,0.14);
}
/* 7.4:1 on the ink ground. */
.foot__fine { margin: 0; font-size: 0.64rem; letter-spacing: 0.14em; text-transform: uppercase; color: rgba(245,243,237,0.66); }
.foot__up {
  margin-left: auto; display: inline-flex; align-items: center; gap: 0.5rem;
  position: relative; background: none; border: 0; padding: 0.35rem 0; cursor: pointer;
  font-family: inherit; font-size: 0.64rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: rgba(245,243,237,0.66);
  transition: color 300ms ease, transform 300ms var(--ease-out-quart);
}
/* The label is 0.64rem of tracked caps, about eleven pixels of hit area.
   The target is grown with an overlay rather than padding so the button stays
   flush with the copyright line beside it. */
.foot__up::before { content: ''; position: absolute; inset: -0.7rem -0.9rem; }
.foot__up .arr { color: var(--accent-lift); transition: transform 520ms var(--ease-out-expo); }
@media (hover: hover) { .foot__up:hover { color: #FFF; } .foot__up:hover .arr { transform: translateY(-4px); } }
@media (max-width: 479px) { .foot__up { margin-left: 0; } }

/* ── Reveal primitives ─────────────────────────────────────── */
/* overflow:hidden clips at the padding edge, and these lines run at 0.92 to
   0.98 line-height, so a descender sits below the box and gets sliced: the y
   in Rayan, the g in bring, the y in early. The padding is added to the span
   rather than to the mask so the rest offset, which is a percentage of the
   span, still clears the taller box; the matching negative margin keeps the
   leading between stacked lines exactly where it was. */
.r-line { display: block; overflow: hidden; margin-bottom: -0.2em; }
.r-line > span { display: block; padding-bottom: 0.2em; transform: translateY(105%); }
.r-up { opacity: 0; transform: translateY(24px); }
.r-media { clip-path: inset(0 0 100% 0); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important; animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important; scroll-behavior: auto !important;
  }
  .r-line > span { transform: none; }
  .r-up { opacity: 1; transform: none; }
  .r-media { clip-path: none; }
  .stmt__p .w { opacity: 1; }
  .tile__img { transform: none; }
  .cap__i::before { width: 100%; }
  /* A scrubbed canvas is the animation here most likely to cause
     vestibular discomfort, so the scrub is not created at all and the
     hero settles on the completed building. */
  .hero { height: 100vh; height: 100svh; }
  .hero__beatwash { display: none; }
}

/* 404. The one page that is not the concept itself, sharing its type. */
body.is-404 { display: flex; min-height: 100svh; align-items: center; }
body.is-404 .sec { width: 100%; }

/* A viewport this short has no room for both the featured rule and the beat
   shelf, and no arrangement of the two that fits. The rule goes; the project
   is still named in the work section. */
@media (max-width: 899px) and (max-height: 299px) {
  .hero__feat { display: none; }
}
