:root {
  --ink: #0d0f11;
  --paper: #f4f2ee;
  --paper-dim: #e6e2db;
  --sand: #c8bba6;
  --sea: #2f6f74;
  --muted: #8a8681;
  --line: rgba(13, 15, 17, .12);
  --hero-vh: 100vh;
  --ease: cubic-bezier(.22, 1, .36, 1);
  /* emons' own curve for the slots, labels and cards — worth matching, the feel
     of the expansion is most of what makes those controls read as considered. */
  --slot: cubic-bezier(.3, 0, .2, 1);
  --pad: clamp(20px, 4vw, 64px);
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }
body {
  background: var(--paper);
  color: var(--ink);
  font: 400 17px/1.55 "Inter", -apple-system, "Segoe UI", system-ui, sans-serif;
  font-feature-settings: "ss01", "cv05";
  overflow-x: clip;
}
/* The width/height attributes are a presentational hint that sets CSS height, and an
   explicit height beats aspect-ratio — without this every figure renders 1520px tall. */
img { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }

/* Lenis wants the html element to opt out of native smooth scrolling. */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }

.kicker {
  font-size: 12px; letter-spacing: .14em; text-transform: uppercase;
  color: var(--muted); margin: 0 0 18px;
}
.display {
  font-weight: 500; letter-spacing: -.025em; line-height: 1.06;
  font-size: clamp(30px, 4.6vw, 62px); margin: 0;
}
.body-lg { font-size: clamp(16px, 1.35vw, 19px); line-height: 1.6; color: #3a3d40; max-width: 58ch; }

/* ── header ─────────────────────────────────────────────────────────── */
.site-header {
  position: fixed; inset: 0 0 auto; z-index: 50;
  display: flex; align-items: center; justify-content: space-between;
  padding: 22px var(--pad);
  color: #fff; pointer-events: none;
  text-shadow: 0 1px 24px rgba(0,0,0,.45);
}
.site-header::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(to bottom, rgba(8,10,12,.55), rgba(8,10,12,0));
  transition: opacity .35s;
}
/* Past the hero the header runs over alternating light and dark sections, so it takes
   its colour from whatever is behind it instead of staying white on a black bar. */
.site-header.is-past::before { opacity: 0; }
.site-header.is-past {
  background: rgba(244,242,238,.78); color: var(--ink); text-shadow: none;
  backdrop-filter: saturate(1.2) blur(14px); -webkit-backdrop-filter: saturate(1.2) blur(14px);
  border-bottom: 1px solid var(--line);
}
.site-header.is-past.on-dark {
  background: rgba(13,15,17,.72); color: var(--paper);
  border-bottom-color: rgba(244,242,238,.14);
}
.site-header > * { pointer-events: auto; }
.brand { font-weight: 600; letter-spacing: .2em; font-size: 13px; }
.brand span { opacity: .55; margin-left: 10px; letter-spacing: .16em; }
/* On a 320px screen the three controls in the header already added up to 322px with the old
   label, and "WhatsApp" is nineteen wider. The project name is the one thing here the page
   repeats on every screen, so it is what gives way. */
@media (max-width: 380px) { .brand span { display: none; } }
.site-nav { display: flex; gap: 26px; font-size: 13px; letter-spacing: .04em; }
.site-nav a { opacity: .75; transition: opacity .25s; }
.site-nav a:hover { opacity: 1; }
/* The call to action used to exist only at the very bottom of the page, which
   is the one place a reader has already decided to leave. It rides in the
   header now and takes its colour from whatever the header is over. */
.head-cta {
  font-size: 12.5px; letter-spacing: .05em; padding: 9px 18px; border-radius: 999px;
  background: var(--sand); color: #0b0d0e; white-space: nowrap;
  transition: background .3s, color .3s;
}
.head-cta:hover { background: #fff; }
.site-header.is-past.on-dark .head-cta { background: var(--sand); }
@media (max-width: 480px) { .head-cta { padding: 8px 13px; font-size: 12px; } }

.lang { display: flex; gap: 2px; font-size: 12px; letter-spacing: .08em; }
.lang button {
  background: none; border: 0; color: inherit; cursor: pointer;
  padding: 4px 7px; opacity: .45; font: inherit; text-transform: uppercase;
  transition: opacity .25s;
}
.lang button[aria-pressed="true"], .lang button:hover { opacity: 1; }
@media (max-width: 780px) { .site-nav { display: none; } }

/* ── hero ───────────────────────────────────────────────────────────── */
/* The hero used to be 420vh, then 260vh, of scroll distance to walk through five stages.
   It costs none now: one gesture is one stage and the page below stays locked until the
   last, which is what makes emons feel like a film rather than a long page. */
.hero { position: relative; height: 100svh; padding: 0; }
.hero__pin {
  position: relative; height: 100svh; overflow: hidden;
  background: var(--ink);
}
.hero__media { position: absolute; inset: 0; }
/* Without a transition the stages snapped from one to the next. */
.hero__media figure {
  position: absolute; inset: 0; margin: 0; opacity: 0;
  transition: opacity .85s var(--ease);
}
.hero__media figure.is-active { opacity: 1; }
.hero__media img, .hero__media video {
  width: 100%; height: 100%; object-fit: cover;
  transform: scale(1.06); transform-origin: 50% 55%;
}
/* The clip sits over its own still and only appears once it is actually
   running, so a slow download shows the frame instead of black. */
.hero__media video {
  position: absolute; inset: 0;
  opacity: 0; transition: opacity .5s linear;
}
.hero__media video.is-ready { opacity: 1; }
/* The film covers the stills entirely once it is running; they stay as its poster.
   Everything above it needs its own z-index, not just a later place in the markup: a
   positioned element with a positive z-index paints above every z-index:auto sibling
   whatever the DOM order, and without this the film hides the scrim, the copy and the
   stage rail outright. The same trap ate the text for the whole 2.5s of every bridge. */
.hero__film { z-index: 2; }

/* The panels are a deck: the outgoing one leaves upward while the incoming one arrives
   from below, both a full height in the same 0.9s. The stack clips them, and its height
   is set from JS off the tallest panel because three languages disagree about that. */
.hero__stack { position: relative; overflow: hidden; }
.hero__panel { position: absolute; inset: 0; margin: 0; will-change: transform; }

/* Nothing rubber-bands while the wheel is being taken over. */
html.hero-locked, html.hero-locked body { overscroll-behavior: none; }
.hero__scrim {
  position: absolute; inset: 0; z-index: 3;
  background:
    linear-gradient(to top, rgba(8,10,12,.82) 0%, rgba(8,10,12,.28) 38%, rgba(8,10,12,0) 62%),
    linear-gradient(to right, rgba(8,10,12,.5) 0%, rgba(8,10,12,0) 55%);
}
/* The copy layer sits above the hotspots and fills the whole pin, so left as it was it
   swallowed every click aimed at a plus. It passes events through and hands them back
   only where something is actually there to click or select. */
.hero__inner { pointer-events: none; }
.hero__kicker, .hero__stack, .hero__tabs, .hero__scroll { pointer-events: auto; }
.hero__inner {
  position: relative; z-index: 5; height: 100%; display: flex; flex-direction: column;
  justify-content: flex-end; padding: 0 var(--pad) clamp(28px, 6vh, 56px);
  color: #fff;
}
.hero__kicker {
  position: absolute; top: clamp(88px, 14vh, 140px); left: var(--pad);
  font-size: 12px; letter-spacing: .14em; text-transform: uppercase; opacity: .62;
}
.hero__stack, .hero__panel { max-width: 46ch; }
.hero__n {
  font-variant-numeric: tabular-nums; font-size: 12px; letter-spacing: .2em;
  opacity: .6; display: block; margin-bottom: 14px;
}
.hero__title {
  font-size: clamp(38px, 6.4vw, 92px); font-weight: 500; letter-spacing: -.03em;
  line-height: .98; margin: 0 0 12px;
}
.hero__lead { font-size: clamp(17px, 1.7vw, 23px); line-height: 1.35; margin: 0 0 10px; }
.hero__body { font-size: 15px; line-height: 1.6; opacity: .74; margin: 0; max-width: 52ch; }
.hero__facts { display: flex; gap: 34px; margin: 22px 0 0; padding: 0; list-style: none; }
.hero__facts b { display: block; font-size: clamp(18px, 1.8vw, 26px); font-weight: 500; letter-spacing: -.02em; }
.hero__facts span { font-size: 11.5px; letter-spacing: .08em; text-transform: uppercase; opacity: .55; }
/* Each line is masked so it can slide up from behind its own edge. */
.line-mask { overflow: hidden; display: block; }

/* ── stage rail ───────────────────────────────────────────────────────
   emons' bottom-left stage buttons: a number, an icon in a slot, and a label that
   unrolls only on the active one. The label animates through a grid track going
   0fr to 1fr, which is the one honest way to transition a width to auto. */
/* A compact group at the bottom left, not a full-width bar: stretched across the page
   the rail reads as a progress meter, which is a different promise. */
.hero__tabs { display: flex; gap: 0; width: fit-content; margin-top: clamp(22px, 4vh, 42px); }
.hero__tab {
  flex: 0 0 auto; min-width: 96px; background: none; border: 0; color: #fff;
  cursor: pointer; text-align: left; padding: 0; font: inherit;
}
.hero__tab-row { display: flex; align-items: center; gap: 10px; padding: 0 14px 13px 0; }
.hero__tab em {
  font-style: normal; font-size: 11px; letter-spacing: .08em; opacity: .4;
  font-variant-numeric: tabular-nums; transition: opacity .4s;
}
.hero__tab-slot {
  width: 34px; height: 34px; flex: 0 0 34px; display: grid; place-items: center;
  border-radius: 9px; background: rgba(255,255,255,.10); color: #fff;
  transition: background-color .5s var(--slot), color .5s var(--slot);
}
.hero__tab-slot svg { width: 19px; height: 19px; }
.hero__tab-label {
  display: grid; grid-template-columns: 0fr;
  transition: grid-template-columns .5s var(--slot);
  font-size: 12.5px; letter-spacing: .05em;
}
.hero__tab-label > span { overflow: hidden; white-space: nowrap; }
.hero__tab:hover .hero__tab-slot { background: rgba(255,255,255,.24); }
.hero__tab[aria-current="true"] em { opacity: 1; }
.hero__tab[aria-current="true"] .hero__tab-slot { background: #fff; color: var(--ink); }
.hero__tab[aria-current="true"] .hero__tab-label { grid-template-columns: 1fr; }
/* the rail itself: a hairline under every button, with a dot that fills on the active one */
.hero__tab-rail { display: block; position: relative; height: 1px; background: rgba(255,255,255,.18); }
.hero__tab-rail i {
  position: absolute; left: 0; top: -2.5px; width: 6px; height: 6px; border-radius: 50%;
  background: rgba(255,255,255,.28); transition: background-color .4s, transform .4s var(--slot);
}
.hero__tab[aria-current="true"] .hero__tab-rail i { background: #fff; transform: scale(1.4); }
@media (max-width: 700px) {
  /* Five buttons at 96px each is 480px on a 390px screen, and .hero__pin's overflow:hidden
     was silently eating the fifth one — the last stage was unreachable from the rail and
     nothing complained, because clipped is not the same as scrolling. They share the width
     now, and each carries its number: an unlabelled glyph is not a control. */
  .hero__tabs { width: 100%; }
  .hero__tab { flex: 1 1 0; min-width: 0; }
  .hero__tab-row { flex-direction: column; align-items: center; gap: 6px; padding: 0 0 11px; }
  .hero__tab em { display: block; font-size: 10px; letter-spacing: .1em; }
  .hero__tab-label { display: none; }   /* the stage title above already names it */

  /* The hero stays full-bleed on a phone, the way emons keeps it. What has to give is the
     copy: five elements of it covered half the picture. Three lines — number, title,
     lead — and the paragraph and the figures wait for a wider screen. */
  .hero__body, .hero__facts { display: none; }
  .hero__lead { font-size: 16.5px; margin-bottom: 0; opacity: .82; }
  .hero__title { margin-bottom: 4px; }
  .hero__kicker { display: none; }
  /* Sit the copy right on top of the rail instead of floating a third of the way up the
     picture, where the subject of every frame happens to be. */
  .hero__inner { padding-bottom: 16px; }
  .hero__tabs { margin-top: 14px; }
  /* And lift the crop so the building is above the text rather than behind it: cover
     centres the frame by default, which puts the subject exactly where the words go. */
  .hero__media img, .hero__media video { object-position: 50% 34%; }
  /* With less text sitting on it the scrim can lift, and more of the frame comes back. */
  .hero__scrim {
    background:
      linear-gradient(to top, rgba(8,10,12,.78) 0%, rgba(8,10,12,.14) 28%, rgba(8,10,12,0) 46%);
  }
}

/* ── hotspots ─────────────────────────────────────────────────────────
   The plus opens a card in place, rotating 135 degrees into a close button as it
   goes. The card grows on a 0fr/1fr row rather than a max-height, so no guessed
   height can clip the longest translation. */
/* ── turntable ──────────────────────────────────────────── */
.spin__stage {
  position: relative; margin: clamp(28px, 5vh, 52px) auto 0;
  width: min(1100px, 92vw); cursor: grab; touch-action: pan-y;
  user-select: none; -webkit-user-select: none;
}
.spin__stage.is-held { cursor: grabbing; }
.spin__img {
  display: block; width: 100%; height: auto; border-radius: 14px;
  pointer-events: none;   /* the drag belongs to the stage, not to the picture */
}
.spin__hint {
  position: absolute; left: 50%; bottom: 18px; transform: translateX(-50%);
  margin: 0; padding: 7px 15px; border-radius: 999px;
  background: rgba(10,12,14,.55); backdrop-filter: blur(7px);
  font-size: 11.5px; letter-spacing: .14em; text-transform: uppercase; color: #fff;
  opacity: .85; transition: opacity .4s var(--ease);
  pointer-events: none;
}
/* The hint has done its job the moment the hand lands on it. */
.spin__stage.is-held .spin__hint { opacity: 0; }

/* Loading runs along the bottom edge and leaves once everything is in. */
.spin__bar {
  position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: rgba(255,255,255,.12); border-radius: 0 0 14px 14px; overflow: hidden;
  transition: opacity .5s var(--ease);
}
.spin__bar i { display: block; height: 100%; background: #fff; transform-origin: 0 50%; transform: scaleX(0); }
.spin__bar.is-done { opacity: 0; }

/* ── location ─────────────────────────────────────────────────────────
   The map is drawn, not embedded. A tile service would pull in a third party, go
   stale the day the tiles change, and tell a buyer nothing the address bar cannot.
   What sells a headland is how little else is on it, and that is a drawing job. */
/* Anchor targets land under the fixed bar without this, so #place opens on a headline
   sliced in half by the header. */
[id] { scroll-margin-top: 84px; }

.place__grid {
  display: grid; grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: clamp(32px, 5vw, 76px); align-items: center;
}
.place__modes { display: flex; gap: 6px; margin: clamp(22px, 3vh, 34px) 0 18px; }
.place__modes button {
  border: 1px solid rgba(255,255,255,.2); background: none; color: inherit;
  border-radius: 999px; padding: 8px 16px; font: inherit; font-size: 12.5px;
  letter-spacing: .05em; cursor: pointer;
  transition: background-color .35s var(--slot), border-color .35s var(--slot), color .35s var(--slot);
}
.place__modes button:hover { border-color: rgba(255,255,255,.5); }
.place__modes button[aria-pressed="true"] { background: #fff; border-color: #fff; color: var(--ink); }

.place__list { list-style: none; margin: 0; padding: 0; }
.place__list li {
  display: flex; align-items: baseline; gap: 14px;
  padding: 11px 0; border-top: 1px solid rgba(255,255,255,.1);
  transition: color .3s, border-color .3s;
}
.place__list li span { flex: 1; font-size: 14.5px; }
/* Tabular figures so the times sit in a column instead of shuffling with each digit. */
.place__list li b { font-weight: 500; font-variant-numeric: tabular-nums; opacity: .6; }
.place__list li.is-lit { border-color: rgba(255,255,255,.5); }
.place__list li.is-lit b { opacity: 1; }
.place__note { margin-top: 16px; font-size: 12px; letter-spacing: .04em; opacity: .4; }

/* Clip to the same rounded box as the picture, or a pin near a corner hangs outside it. */
.place__map { position: relative; margin: 0; border-radius: 14px; overflow: hidden; }
.place__map img {
  display: block; width: 100%; height: auto; border-radius: 14px;
  border: 1px solid rgba(255,255,255,.09);
}
.place__pins { position: absolute; inset: 0; }

/* Pins over the photograph. Places inside the frame get a dot; places outside it get a
   chevron on the border pointing the way, because a dot for the airport dropped somewhere
   in the field would be a lie the eye believes. */
.place__pin {
  position: absolute; display: flex; align-items: center; gap: 8px;
  transform: translate(-50%, -50%); white-space: nowrap;
  transition: opacity .3s;
}
.place__pin i {
  width: 8px; height: 8px; flex: 0 0 8px; border-radius: 50%;
  background: rgba(255,255,255,.75); box-shadow: 0 0 0 3px rgba(0,0,0,.28);
  transition: background-color .3s, transform .3s var(--slot);
}
.place__pin span {
  font-size: 12.5px; letter-spacing: .03em; color: rgba(255,255,255,.8);
  text-shadow: 0 1px 6px rgba(0,0,0,.85); transition: color .3s;
}
.place__pin.is-edge i {
  width: 0; height: 0; border-radius: 0; background: none; box-shadow: none;
  border: 5px solid transparent;
}
/* Each edge marker sits against its own border and points outward, so the label always
   falls back onto the picture instead of off it. */
.place__pin.is-right { flex-direction: row-reverse; transform: translateY(-50%); }
.place__pin.is-right i { border-left-color: rgba(255,255,255,.8); margin-left: 5px; }
.place__pin.is-top { flex-direction: column; gap: 4px; transform: translateX(-50%); }
.place__pin.is-top i { border-bottom-color: rgba(255,255,255,.8); }
.place__pin.is-lit i { background: #fff; transform: scale(1.4); }
.place__pin.is-lit.is-edge i { transform: scale(1.35); }
.place__pin.is-lit.is-top i { border-bottom-color: #fff; }
.place__pin.is-lit.is-right i { border-left-color: #fff; }
.place__pin.is-lit span { color: #fff; }
/* Somewhere you cannot walk to stops competing while the walking times are showing. */
.place__pin.is-off { opacity: .45; }

/* Where the building is. The ring breathes so the eye finds it before it reads anything. */
.place__here {
  position: absolute; left: 49%; top: 66%; transform: translate(-50%, -50%);
  display: flex; flex-direction: column; align-items: center; gap: 7px; pointer-events: none;
}
.place__here i {
  width: 13px; height: 13px; border-radius: 50%; background: #fff;
  box-shadow: 0 0 0 6px rgba(255,255,255,.18), 0 0 0 14px rgba(255,255,255,.08);
  animation: place-pulse 3.2s var(--ease) infinite;
}
.place__here span {
  font-size: 12px; letter-spacing: .2em; font-weight: 500; color: #fff;
  text-shadow: 0 1px 8px rgba(0,0,0,.9);
}
@keyframes place-pulse {
  0%, 100% { box-shadow: 0 0 0 6px rgba(255,255,255,.18), 0 0 0 14px rgba(255,255,255,.08); }
  50%      { box-shadow: 0 0 0 9px rgba(255,255,255,.12), 0 0 0 22px rgba(255,255,255,.03); }
}
@media (prefers-reduced-motion: reduce) { .place__here i { animation: none; } }

@media (max-width: 900px) {
  .place__grid { grid-template-columns: 1fr; }
  /* At phone width the labels cover more of the picture than they explain. */
  .place__pin span { display: none; }
}

.hero__spots { position: absolute; inset: 0; z-index: 4; pointer-events: none; }
.hero__spot {
  position: absolute; width: min(300px, 62vw);
  /* Centre the BUTTON on the point, not the block. Centring the block moved the whole
     thing half a card upward the moment one opened, and the marker left its subject. */
  margin: -17px 0 0 -17px;
  opacity: 0; pointer-events: none; transition: opacity .5s var(--ease);
}
.hero__spot.is-on { opacity: 1; pointer-events: auto; }
/* Glass rather than a white dot: solid white read as a blemish on the frame. The ring
   and the blur keep it findable over both the pale cliff and the dark sea. */
.hero__spot-btn {
  width: 34px; height: 34px; padding: 0; cursor: pointer;
  display: grid; place-items: center; color: #fff;
  background: rgba(255,255,255,.14);
  border: 1px solid rgba(255,255,255,.55);
  backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
  border-radius: 999px;
  box-shadow: 0 4px 14px rgba(0,0,0,.22);
  transition: border-radius .5s var(--slot), background-color .5s var(--slot),
              border-color .5s var(--slot), color .5s var(--slot);
}
.hero__spot-btn:hover { background: rgba(255,255,255,.9); border-color: #fff; color: var(--ink); }
.hero__spot.is-open .hero__spot-btn { background: rgba(255,255,255,.9); border-color: #fff; color: var(--ink); }
.hero__spot-btn svg { width: 16px; height: 16px; transition: transform .5s var(--slot); }
.hero__spot.is-open .hero__spot-btn { border-radius: 10px; }
.hero__spot.is-open .hero__spot-btn svg { transform: rotate(135deg); }
.hero__spot-card {
  display: grid; grid-template-rows: 0fr; margin-top: 8px;
  background: rgba(10,12,14,.88); border-radius: 10px; color: #fff;
  box-shadow: 0 12px 30px rgba(0,0,0,.28);
  transition: grid-template-rows .5s var(--slot);
}
.hero__spot.is-open .hero__spot-card { grid-template-rows: 1fr; }
.hero__spot-card > div { overflow: hidden; }
.hero__spot-card h3 {
  margin: 0; padding: 14px 16px 0; font-size: 12px; letter-spacing: .12em;
  text-transform: uppercase; font-weight: 500;
}
.hero__spot-card p {
  margin: 7px 0 0; padding: 0 16px 15px; font-size: 13.5px; line-height: 1.5; opacity: .78;
}
/* Below the phone breakpoint the frame is too small to point at anything usefully, and
   the cards would cover the copy they are meant to annotate. */
@media (max-width: 700px) { .hero__spots { display: none; } }

.hero__scroll {
  position: absolute; right: var(--pad); bottom: calc(clamp(28px, 6vh, 56px) + 74px);
  font-size: 11px; letter-spacing: .16em; text-transform: uppercase; opacity: .5;
  display: flex; align-items: center; gap: 10px;
}
.hero__scroll em { display: block; width: 26px; height: 1px; background: currentColor; }
@media (max-width: 700px) { .hero__scroll { display: none; } }

/* ── generic sections ───────────────────────────────────────────────── */
section { padding: clamp(90px, 13vh, 170px) var(--pad); }
.wrap { max-width: 1360px; margin: 0 auto; }
.split { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(30px, 5vw, 80px); align-items: center; }
@media (max-width: 900px) { .split { grid-template-columns: 1fr; } }

.manifest { background: var(--paper); }
.manifest .display { max-width: 20ch; }
.manifest .body-lg { margin-top: 26px; }

.dark { background: var(--ink); color: var(--paper); }
.dark .body-lg { color: rgba(244,242,238,.72); }
.dark .kicker { color: rgba(244,242,238,.5); }

.figure-full { margin: 0; }
.figure-full img { width: 100%; border-radius: 4px; }
.figure-full figcaption {
  margin-top: 14px; font-size: 12.5px; letter-spacing: .04em; color: var(--muted);
}
.dark .figure-full figcaption { color: rgba(244,242,238,.5); }

.point-list { list-style: none; margin: 34px 0 0; padding: 0; }
.point-list li {
  display: grid; grid-template-columns: 20ch 1fr; gap: 18px;
  padding: 16px 0; border-top: 1px solid rgba(244,242,238,.16); font-size: 14.5px;
}
.point-list b { font-weight: 500; }
.point-list span { color: rgba(244,242,238,.6); }
@media (max-width: 640px) { .point-list li { grid-template-columns: 1fr; gap: 4px; } }

.units { width: 100%; border-collapse: collapse; margin-top: 34px; font-size: 14.5px; }
.units th {
  text-align: left; font-weight: 400; font-size: 11.5px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted); padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
}
.units td { padding: 15px 0; border-bottom: 1px solid var(--line); }
.units td:first-child { font-weight: 500; width: 3ch; }
.units td:nth-child(3) { font-variant-numeric: tabular-nums; }
.units td:last-child { color: var(--muted); }
.units tbody tr { transition: background-color .35s var(--ease); }
.units tbody tr:hover { background: rgba(13,15,17,.035); }
.units .u-key { transition: transform .4s var(--slot), color .35s; transform-origin: left center; }
.units tbody tr:hover .u-key { transform: scale(1.3); color: var(--sea); }
/* The bar is the home against the largest one, so the row reads as a size
   before anyone parses the number. */
.u-bar { width: 22%; }
.u-bar i { display: block; height: 3px; border-radius: 2px; background: var(--sea); opacity: .55; transform: scaleX(0); transform-origin: left; }
@media (max-width: 760px) { .u-bar { display: none; } }

/* ── amenities: an accordion, not a grid ─────────────────────────────
   Six slots side by side, the one under the pointer opens. The label stays
   horizontal in both states — a name turned on its side in a narrow slot is
   the one thing here that reads as a trick rather than a decision. */
.acc {
  display: flex; gap: 6px; height: clamp(300px, 46vh, 460px);
  margin-top: clamp(36px, 6vh, 64px);
}
.acc__i {
  position: relative; flex: 1 1 0; min-width: 0; overflow: hidden; border-radius: 4px;
  cursor: pointer; transition: flex-grow .75s var(--slot);
}
.acc__i img { width: 100%; height: 100%; object-fit: cover; }
.acc__i::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(6,8,9,.86) 0%, rgba(6,8,9,.1) 55%);
}
.acc__i.on { flex-grow: 4.6; }
.acc__i:focus-visible { outline: 2px solid var(--sand); outline-offset: -2px; }
.acc__cap { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; padding: 16px 16px 18px; color: #f4f2ee; }
.acc__cap h3 {
  margin: 0; font-weight: 500; font-size: 13.5px; line-height: 1.25; letter-spacing: -.005em;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
  transition: font-size .5s var(--slot);
}
.acc__i.on .acc__cap { padding: 20px 22px 22px; }
.acc__i.on .acc__cap h3 { font-size: 19px; -webkit-line-clamp: 1; }
.acc__cap p {
  margin: 0; font-size: 14px; line-height: 1.5; color: rgba(244,242,238,.74); max-width: 46ch;
  max-height: 0; opacity: 0; overflow: hidden;
  transition: max-height .6s var(--slot), opacity .4s, margin .6s var(--slot);
}
.acc__i.on .acc__cap p { max-height: 84px; opacity: 1; margin-top: 9px; }

/* Six columns cannot exist on a phone — 390px over six is 65px a slot, which is
   narrower than the word in it. The same control on its side is the only form
   that fits, and a landscape still sits in a wide band far better anyway. */
@media (max-width: 760px) {
  .acc { flex-direction: column; height: auto; gap: 5px; }
  .acc__i { flex: none; height: 86px; transition: height .7s var(--slot); }
  .acc__i.on { flex: none; height: clamp(220px, 32vh, 320px); }
  .acc__i::after { background: linear-gradient(to right, rgba(6,8,9,.9) 0%, rgba(6,8,9,.34) 50%, rgba(6,8,9,.05) 100%); }
  .acc__cap { top: 0; display: flex; flex-direction: column; justify-content: center; padding: 0 20px; }
  .acc__cap h3, .acc__i.on .acc__cap h3 { font-size: 16px; -webkit-line-clamp: 2; }
  .acc__i.on .acc__cap { padding: 0 20px; }
  .acc__cap p { max-width: none; }
}

.facts { display: flex; gap: clamp(26px, 4vw, 60px); margin: 30px 0 0; padding: 0; list-style: none; flex-wrap: wrap; }
.facts b { display: block; font-size: clamp(22px, 2.4vw, 34px); font-weight: 500; letter-spacing: -.025em; }
.facts span { font-size: 11.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.dark .facts span { color: rgba(244,242,238,.5); }

/* replaced by the closing block below */
.closing h2 {
  font-size: clamp(56px, 13vw, 180px); font-weight: 500; letter-spacing: -.045em;
  margin: 0 0 18px; line-height: .9;
}
.closing p { margin: 0 auto; max-width: 44ch; color: rgba(244,242,238,.72); }
.btn {
  position: relative; display: inline-flex; align-items: center; gap: 11px; overflow: hidden;
  isolation: isolate; margin-top: 38px; padding: 15px 30px;
  border: 1px solid rgba(244,242,238,.35); border-radius: 999px;
  font-size: 13.5px; letter-spacing: .06em; transition: color .35s, border-color .35s;
}
/* The fill arrives from the left rather than switching on, and the arrow moves
   with it — the only piece of ornament on the page, so it may as well be made. */
.btn::before {
  content: ""; position: absolute; inset: 0; z-index: -1; background: var(--paper);
  transform: translateX(-101%); transition: transform .62s var(--slot);
}
.btn:hover { color: var(--ink); border-color: var(--paper); }
.btn:hover::before { transform: none; }
.btn svg { transition: transform .5s var(--slot); }
.btn:hover svg { transform: translateX(5px); }


/* ── the homes ───────────────────────────────────────────────────────
   Full width rather than another text-beside-picture split: the page already
   carries six of those. One row of four tabs, one large plan, the table under
   it. The plans are one model relit four times, so nothing drifts between them. */
.homes__tabs {
  display: flex; gap: 4px; margin: clamp(30px, 5vh, 52px) 0 0;
  border-bottom: 1px solid var(--line);
}
.homes__tab {
  flex: 1 1 0; min-width: 0; background: none; border: 0; border-bottom: 2px solid transparent;
  font: inherit; color: var(--muted); cursor: pointer; padding: 13px 6px 15px; text-align: left;
  transition: color .3s, border-color .3s;
}
.homes__tab b { display: block; font-size: clamp(18px, 2vw, 22px); font-weight: 500; letter-spacing: -.02em; }
.homes__tab span { font-size: 12.5px; }
.homes__tab[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--sea); }
.homes__tab:hover { color: var(--ink); }
.homes__plan {
  position: relative; margin-top: clamp(20px, 3vh, 32px);
  border-radius: 4px; overflow: hidden; background: var(--paper-dim);
}
.homes__plan img { width: 100%; }
.homes__plan img:not(.is-on) { display: none; }
@media (max-width: 620px) {
  .homes__tab { padding: 11px 4px 13px; }
  .homes__tab span { font-size: 11.5px; }
}

/* ── the closing block ───────────────────────────────────────────────
   A wordmark centred on black is the cheapest possible last screen. The final
   frame carries the numbers and the single call to action; the four answers a
   buyer always wants sit under it, and the legal note goes last and small. */
/* A footer sits on the bottom edge; the section's usual 170px tail would leave the
   page ending in empty ground again. */
.closing { text-align: left; padding-bottom: clamp(30px, 4.5vh, 56px); }
.closing__frame {
  position: relative; margin: 0; border-radius: 4px; overflow: hidden;
  min-height: min(76vh, 640px); display: flex; align-items: flex-end;
}
.closing__frame img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.closing__frame::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(6,8,9,.92) 8%, rgba(6,8,9,.34) 58%, rgba(6,8,9,.1) 100%);
}
.closing__in { position: relative; z-index: 1; width: 100%; padding: clamp(24px, 3.4vw, 52px); }
.closing__in .kicker { color: rgba(244,242,238,.62); margin-bottom: 14px; }
.closing h2 {
  font-size: clamp(28px, 4.2vw, 54px); font-weight: 500; letter-spacing: -.04em;
  line-height: 1.05; margin: 0 0 22px; max-width: 16ch;
}
.closing__row { display: flex; align-items: flex-end; justify-content: space-between; gap: 28px; flex-wrap: wrap; }
.closing__facts { display: flex; gap: clamp(18px, 2.6vw, 44px); list-style: none; margin: 0; padding: 0; flex-wrap: wrap; }
.closing__facts b { display: block; font-size: clamp(18px, 2vw, 27px); font-weight: 500; letter-spacing: -.025em; }
.closing__facts span { font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: rgba(244,242,238,.55); }
/* The pitch sits over the button, not inside it: a label that wraps onto three lines is a
   broken button, and the sentence is the part that has to be read first anyway. */
.closing__ask { display: flex; flex-direction: column; align-items: flex-end; gap: 12px; }
.closing__pitch {
  margin: 0; max-width: 26ch; text-align: right;
  font-size: clamp(15px, 1.35vw, 18px); line-height: 1.35; color: rgba(244,242,238,.9);
}
@media (max-width: 720px) {
  .closing__ask { align-items: flex-start; width: 100%; }
  .closing__pitch { text-align: left; max-width: 34ch; }
}
.closing .btn { margin-top: 0; background: var(--sand); border-color: var(--sand); color: var(--ink); }
.closing .btn::before { background: #fff; }
.closing .btn:hover { color: var(--ink); border-color: #fff; }

.closing__cols {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 26px;
  padding-top: clamp(26px, 4vw, 46px); font-size: 14.5px;
}
@media (max-width: 820px) { .closing__cols { grid-template-columns: 1fr 1fr; } }
@media (max-width: 460px) { .closing__cols { grid-template-columns: 1fr; } }
.closing__cols h4 {
  margin: 0 0 6px; font-size: 11px; letter-spacing: .12em; text-transform: uppercase;
  color: rgba(244,242,238,.45); font-weight: 400;
}
.closing__cols p { margin: 0; color: rgba(244,242,238,.8); line-height: 1.55; max-width: none; text-align: left; }
.closing__rule { height: 1px; background: rgba(244,242,238,.14); margin-top: clamp(26px, 4vw, 46px); }

/* ── the colophon and the bar under it ───────────────────────────────
   Every figure in this table comes from the build (nine sources, 1065 frames,
   96 turntable files); if the film is rebuilt with a different part list, the
   text here is wrong and has to move with it. */
.colo {
  display: grid; grid-template-columns: 1.35fr 1fr; gap: clamp(26px, 4vw, 64px);
  padding: clamp(28px, 3.6vw, 48px) 0 clamp(20px, 2.6vw, 30px); align-items: start; text-align: left;
}
@media (max-width: 860px) { .colo { grid-template-columns: 1fr; gap: 30px; } }
.colo h4 {
  margin: 0 0 16px; font-size: 11px; letter-spacing: .12em; text-transform: uppercase;
  color: rgba(244,242,238,.45); font-weight: 400;
}
.colo__list { margin: 0; display: grid; grid-template-columns: minmax(88px, 128px) 1fr; }
.colo__list dt {
  font-size: 12.5px; letter-spacing: .06em; text-transform: uppercase;
  color: rgba(244,242,238,.45); padding: 10px 0; border-top: 1px solid rgba(244,242,238,.09);
}
.colo__list dd {
  margin: 0; padding: 10px 0; font-size: 15px; color: rgba(244,242,238,.82);
  border-top: 1px solid rgba(244,242,238,.09);
}
.colo__list dd b { font-weight: 500; color: var(--paper); }
.colo__list dd a {
  color: var(--paper); text-decoration: none;
  border-bottom: 1px solid rgba(200,187,166,.5); transition: border-color .25s;
}
.colo__list dd a:hover { border-bottom-color: var(--sand); }
.foot-nav { display: flex; flex-direction: column; gap: 11px; font-size: 15px; }
.foot-nav a { color: rgba(244,242,238,.62); text-decoration: none; transition: color .25s; }
.foot-nav a:hover { color: var(--paper); }
/* on a phone the 128px label column snaps every value onto three lines, so the
   label goes above its row and the sections lie flat */
@media (max-width: 560px) {
  .colo__list { grid-template-columns: 1fr; }
  .colo__list dt { padding: 12px 0 0; }
  .colo__list dd { padding: 2px 0 12px; border-top: 0; }
  .foot-nav { flex-direction: row; flex-wrap: wrap; gap: 18px; font-size: 14px; }
}

.foot-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap;
  border-top: 1px solid rgba(244,242,238,.14);
  padding: clamp(16px, 2vw, 22px) 0 clamp(18px, 2.4vw, 26px); text-align: left;
}
.foot-bar__left { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; }
.foot-bar__right { display: flex; align-items: center; gap: clamp(14px, 2vw, 24px); }
.foot-mark { font-size: 13px; letter-spacing: .2em; font-weight: 500; }
.foot-line { font-size: 12.5px; color: rgba(244,242,238,.42); }
.foot-by { font-size: 12.5px; color: rgba(244,242,238,.42); }
.foot-by a {
  color: var(--paper); text-decoration: none;
  border-bottom: 1px solid rgba(200,187,166,.5); transition: border-color .25s;
}
.foot-by a:hover { border-bottom-color: var(--sand); }
.up {
  display: inline-flex; align-items: center; gap: 8px; font-size: 13px;
  color: rgba(244,242,238,.62); text-decoration: none; transition: color .25s;
}
.up:hover { color: var(--paper); }
.up i { font-style: normal; transition: transform .4s var(--ease); }
.up:hover i { transform: translateY(-3px); }

/* ── the ground under each section is its own picture ────────────────
   Not the file — the LQIP thumbnail that already ships with every image. A
   24-pixel placeholder blown up and blurred is exactly the colour field wanted
   here, and it costs no request, no decode and nothing to repaint. */
main > section:not(.hero) { position: relative; overflow: hidden; }
.ground { position: absolute; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }
.ground i {
  position: absolute; inset: -12%; display: block;
  background-size: cover; background-position: center;
  filter: blur(30px) saturate(1.45);
}
.ground::after { content: ""; position: absolute; inset: 0; background: rgba(244,242,238,.85); }
.dark .ground::after { background: rgba(9,13,15,.855); }
main > section > .wrap, main > section > .spin__stage { position: relative; z-index: 1; }

/* ── sections ride over one another instead of butting up ────────────
   The sticky offset is set from JS: a section taller than the window has to be
   allowed to reach its own bottom before it pins, or its last screenful is
   covered before it is ever seen. Each section also gets an explicit z-index —
   without one the incoming section's ground paints under the outgoing
   section's text, the same trap that once hid the entire hero.
   Off on a phone: there a section is three screens tall, the overlap never
   reads, and the ground alone does the work. */
@media (min-width: 761px) {
  main > section.stack { position: sticky; }
  main > section.stack + .stack { box-shadow: 0 -30px 64px rgba(0, 0, 0, .34); }
}

/* Reveal-on-scroll: everything starts nudged down, GSAP puts it back. */
.reveal { opacity: 0; transform: translateY(22px); }
/* Two blocks arrive differently. A picture is uncovered by a wipe while it
   drifts back to its own size; a statement assembles line by line, each rising
   from behind the one above. Both stay opaque — the nudge-and-fade is exactly
   what they are replacing. The clip sits on the image, never on the element the
   sweep measures, or the thing meant to reveal it can no longer see it. */
.reveal.wipe, .reveal.lines { opacity: 1; transform: none; }
.reveal.wipe img { clip-path: inset(0 100% 0 0); transform: scale(1.1); }
.ln { display: block; overflow: hidden; }
.ln > i { display: block; font-style: normal; transform: translateY(108%); }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; }
  .reveal.wipe img { clip-path: none; transform: none; }
  .ln > i { transform: none; }
  .u-bar i { transform: scaleX(var(--k, 1)); }
  .hero__media img { transform: none; }
  .hero__media video { display: none; }
}
