/* Palette sampled from zsdproperties.com computed styles */
:root {
  --navy:  #122231;
  --navy-2:#1b3145;
  --gold:  #ac8755;   /* brand gold — button fills, rules, borders */
  --cream: #f5efe2;

  /* Brand gold measures ~5:1 on flat navy, but these labels sit over
     photography, where it drops well below that and disappears. This lighter
     gold holds ~7.8:1 and is used for every piece of gold TEXT. The brand
     value stays on solid fills, where contrast is not in question. */
  --gold-text: #d9b276;

  /* Two-tier type. Cinzel carries the journey stage only — it's Trajan-derived,
     effectively all-caps (its lowercase are small capitals) and sets wide, so it
     wants POSITIVE tracking. Syne carries section heads below the fold to stay
     typographically loyal to the zsdproperties.com lead page, which sets its H1
     in Syne 800 at NEGATIVE tracking. Do not swap their tracking values. */
  --font-display: Cinzel, "Times New Roman", serif;   /* wordmark + journey headlines */
  --font-head: Syne, system-ui, sans-serif;           /* section heads, matches lead page */
  --font-ui: "Space Grotesk", system-ui, sans-serif;  /* body, chips, buttons */

  /* Scroll distance the whole exterior→interior journey consumes. */
  /* Trimmed from 460vh. The stage is sticky for this entire distance, so an
     over-long runway reads as the page being stuck rather than as a slow reveal. */
  --journey-runway: 340vh;
}

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

body {
  margin: 0;
  background: var(--navy);
  color: var(--cream);
  font-family: var(--font-ui);
  -webkit-font-smoothing: antialiased;
}

/* Lenis needs these or the virtual scroll fights the native one */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }

/* ---------- intro curtain ---------- */
/* Only exists when JS is running — see the .js gate in <head>. Without JS
   there is nothing to remove it, so it must never render. */
#intro { display: none; }

.js #intro {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  position: fixed; inset: 0; z-index: 9999;
  background: var(--navy);
  overflow: hidden;
  /* Blocks interaction while up, unlike the reference — this page has a CTA
     under it and a click landing on a hidden button would be a real bug. */
  transition: opacity 1s var(--ease-out, cubic-bezier(.22,.61,.36,1));
}
.js #intro::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(80% 70% at 50% 45%, rgba(172,135,85,.10) 0%, transparent 65%);
}
/* Faded out and no longer interactive; JS removes it from the DOM after. */
#intro.is-done { opacity: 0; pointer-events: none; }

/* Gold rays behind the mark. `screen` keys the black plate out, same as the
   atmosphere canvases, so no alpha channel is needed. Scaling and fading in
   under the letters gives the curtain something to open into. */
.intro__sunburst {
  position: absolute; left: 50%; top: 46%;
  width: min(150vw, 1500px); height: auto;
  transform: translate(-50%, -50%) scale(.82);
  opacity: 0; mix-blend-mode: screen; pointer-events: none;
  transition: opacity 2.2s ease, transform 3.4s cubic-bezier(.22,.61,.36,1);
}
.intro__sunburst.is-on { opacity: .55; transform: translate(-50%, -50%) scale(1); }

.intro__brand { position: relative; z-index: 1; text-align: center; padding: 0 1.5rem; }

.intro__logo {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(3.2rem, 11vw, 8rem);
  /* Tracking is applied as symmetric margins on the letters, NOT letter-spacing.
     letter-spacing appends a trailing space after the last letter that centring
     counts as content, pushing the glyphs off-centre — and the usual negative
     margin-right fix misbehaves here because this element is what sizes the flex
     container, so the correction feeds back into the width it is correcting
     against. Equal margins either side of every letter are symmetric by
     construction, so the mark centres exactly at any font size. */
  letter-spacing: 0;
  color: var(--cream); line-height: 1;
}
.intro__logo .ltr {
  margin: 0 .25em;   /* half the intended .5em tracking, on each side */
  display: inline-block; opacity: 0;
  transform: translateY(18px) scale(1.08);
  transition: opacity .9s var(--ease-out, cubic-bezier(.22,.61,.36,1)),
              transform 1s var(--ease-out, cubic-bezier(.22,.61,.36,1));
}
.intro__logo .ltr.is-on { opacity: 1; transform: translateY(0) scale(1); }

.intro__line {
  display: block; height: 1px; width: 0; opacity: 0;
  margin: 1.6rem auto 0;
  background: linear-gradient(90deg, transparent 0%, rgba(217,178,118,.3) 15%,
              var(--gold-text) 50%, rgba(217,178,118,.3) 85%, transparent 100%);
  transition: width 1.2s var(--ease-out, cubic-bezier(.22,.61,.36,1)), opacity .6s ease;
}
.intro__line.is-on { width: min(520px, 68vw); opacity: 1; }

.intro__tagline {
  margin: 1.35rem 0 0;
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.35rem, 2.6vw, 1.9rem);
  letter-spacing: .46em; margin-right: -.46em;
  color: var(--gold-text);
  opacity: 0; transform: translateY(8px);
  transition: opacity 1s var(--ease-out, cubic-bezier(.22,.61,.36,1)),
              transform 1s var(--ease-out, cubic-bezier(.22,.61,.36,1));
}
.intro__tagline.is-on { opacity: 1; transform: translateY(0); }

/* Push-to-start. The curtain waits here indefinitely until this is pressed. */
.intro__enter {
  margin-top: 2.6rem;
  display: inline-flex; align-items: center; gap: .8rem;
  padding: 1.05rem 2.8rem;
  background: none; border: 1px solid rgba(217, 178, 118, .55);
  border-radius: 999px; cursor: pointer;
  font-family: var(--font-ui); font-weight: 600;
  font-size: clamp(.92rem, 2.4vw, 1.05rem); letter-spacing: .22em; text-transform: uppercase;
  color: var(--gold-text);
  opacity: 0; transform: translateY(10px); pointer-events: none;
  transition: opacity .9s var(--ease-out, cubic-bezier(.22,.61,.36,1)),
              transform .9s var(--ease-out, cubic-bezier(.22,.61,.36,1)),
              background-color .3s ease, color .3s ease, border-color .3s ease;
}
/* Only clickable once revealed, so an early click can't skip the sequence. */
.intro__enter.is-on { opacity: 1; transform: translateY(0); pointer-events: auto; }
.intro__enter:hover, .intro__enter:focus-visible {
  background: var(--gold-text); border-color: var(--gold-text); color: var(--navy);
}
.intro__enter:focus-visible { outline: 2px solid var(--cream); outline-offset: 4px; }

/* Nothing to reveal if motion is off — show the page immediately. */
@media (prefers-reduced-motion: reduce) {
  .js #intro { display: none; }
}

/* ---------- cursor comet ---------- */
/* Above everything except the intro curtain, and never interactive — the whole
   page has to stay clickable straight through it. */
.comet {
  position: fixed; inset: 0; z-index: 9998;
  width: 100%; height: 100%;
  pointer-events: none;
}

/* ---------- nav ---------- */
.nav {
  position: fixed; inset: 0 0 auto 0; z-index: 50;
  display: flex; align-items: center; justify-content: space-between;
  padding: 1.75rem 2rem;
  /* No blend mode here — every section is dark, and `difference` inverted the
     mark against copy scrolling under it and rendered it garbled. */
  color: var(--cream);
}
/* Scrim behind the nav. The nav already sits above everything in z-order, but
   bright content passing underneath (the expanding wordmark, the lit kitchen
   plate) still washes the mark and hamburger out. This keeps them readable
   whatever scrolls past, without darkening the page. */
.nav::before {
  content: ""; position: absolute; inset: -1px 0 auto 0; height: 150%;
  z-index: -1; pointer-events: none;
  background: linear-gradient(180deg, rgba(18,34,49,.72) 0%, rgba(18,34,49,.42) 45%, transparent 100%);
}
.nav__mark {
  font-family: var(--font-display); font-weight: 700; font-size: clamp(1.25rem, 3.4vw, 1.6rem);
  letter-spacing: .16em; color: var(--cream); text-decoration: none;
}
.nav__toggle {
  display: grid; gap: 6px; width: 44px; padding: 10px 0;
  background: none; border: 0; cursor: pointer;
  position: relative; z-index: 2;   /* stays clickable above the open panel */
}
.nav__toggle span {
  height: 2px; background: var(--cream); display: block;
  transition: transform .35s cubic-bezier(.22,.61,.36,1), opacity .25s ease;
  transform-origin: center;
}
/* Bars cross into an X while the panel is open. */
.nav__toggle[aria-expanded="true"] span:first-child { transform: translateY(4px) rotate(45deg); }
.nav__toggle[aria-expanded="true"] span:last-child  { transform: translateY(-4px) rotate(-45deg); }

/* ---------- menu panel ---------- */
.menu {
  position: fixed; inset: 0; z-index: 45;
  display: grid; place-items: center;
  /* Solid, not translucent. At .97 the hero headline still read straight
     through and competed with the menu items. */
  background: var(--navy);
  opacity: 0; visibility: hidden;
  transition: opacity .4s ease, visibility .4s;
}
.menu.is-open { opacity: 1; visibility: visible; }
/* `hidden` is removed by JS on first open; until then the panel is fully out of
   the accessibility tree rather than merely transparent. */
.menu[hidden] { display: none; }

.menu__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.6rem; text-align: center; }
.menu__link {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(2.4rem, 8vw, 4.5rem);
  letter-spacing: .04em; color: var(--cream); text-decoration: none;
  transition: color .25s ease;
}
.menu__link:hover, .menu__link:focus-visible { color: var(--gold-text); }
.menu__link--sm {
  font-family: var(--font-ui); font-weight: 500;
  font-size: clamp(1.1rem, 2.4vw, 1.4rem);
  letter-spacing: .06em; color: var(--gold-text);
}

/* ---------- the journey ---------- */
.journey { position: relative; height: var(--journey-runway); }

/* Sticky, not a GSAP pin. Using both makes GSAP swap this to `fixed` and
   fight the sticky offset, and the stage slides away mid-scrub. */
.journey__stage {
  position: sticky; top: 0;
  /* vh is the LARGE viewport height on mobile — it assumes the address bar is
     hidden, so anything bottom-anchored sits under the chrome on first paint
     and the stage visibly resizes as the bar hides. svh is the small (worst
     case) height; vh stays as the fallback for browsers without svh. */
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  background: var(--navy);
}

.layer { position: absolute; inset: 0; }
.scene { position: absolute; inset: 0; }

/* ---------- scene 1: exterior ---------- */
.scene--exterior { z-index: 10; }

/* Cloudscape plate. The generated sky is warmer than the brand navy, so it's
   pushed back under a navy wash rather than colour-graded in the encoder. */
.hero__sky {
  background-image:
    linear-gradient(180deg, rgba(18,34,49,.55) 0%, rgba(18,34,49,.35) 40%, var(--navy) 100%),
    url("../assets/sky.webp");
  background-size: cover;
  background-position: center 35%;
}

/* Cutout is cropped to its alpha bbox — a ~5.8:1 strip — so it's sized by
   width and parked low, reading as the house rising out of the mist. */
.hero__house { z-index: 1; display: grid; place-items: end center; }
.hero__house img {
  width: min(118vw, 1500px);
  height: auto;
  margin-bottom: 6vh;
  transform-origin: 50% 100%;
}

.hero__logo {
  /* Above the atmosphere (z 30), below the copy (z 40). Inside .scene--exterior
     it sat under the smoke and the letters washed out. */
  z-index: 36;
  /* Upper third, not dead centre — the headline owns the lower half and the
     two collide on short viewports otherwise. */
  display: grid; align-content: start; justify-content: center; gap: .1em;
  /* Must clear the nav strip BEFORE scaling. With the origin at the layer's top
     edge, a glyph at y maps to y*scale — so a small top padding starts the mark
     inside the nav band and scaling keeps it there. 4.5vh left only ~3px of
     clearance at full scale. */
  /* A px floor as well as the vh: on a short viewport 6vh is only ~40px, which
     puts the mark back inside the nav band once it scales. The floor keeps the
     clearance real regardless of viewport height. */
  padding-top: max(6vh, 76px);
  text-align: center;
  line-height: 1;
  pointer-events: none;
  /* This is a full-viewport layer, so the default origin is the viewport
     CENTRE — scaling up drags the mark upward and sweeps it straight through
     the nav, making the ZSD mark and hamburger unreadable. Anchoring the origin
     to the top edge makes it expand downward, away from the nav, into space the
     headline has already vacated by then. */
  transform-origin: 50% 0%;
}
/* Drawn wordmark. Outlines first, then flooded solid cream so the finished mark
   is identical to the intro logo — same colour, same weight, one identity.
   The tight-cropped SVG reads much larger than the HTML text it replaced at the
   same nominal width, hence the reduced size to clear the headline block. */
.wordmark {
  width: min(58vw, 740px); height: auto; display: block; overflow: visible;
  /* Dark halo under the glyphs so the outline holds its edge where bright
     smoke drifts behind it — this is what stops the letters dissolving. */
  filter: drop-shadow(0 2px 10px rgba(9, 18, 27, .85))
          drop-shadow(0 0 28px rgba(9, 18, 27, .55));
}
.wordmark__stroke {
  stroke: rgba(252, 249, 243, .96);
  stroke-width: 4;
  /* Keeps the hairline identical at every viewport — without this the stroke
     scales with the viewBox and goes chunky on wide screens. */
  vector-effect: non-scaling-stroke;
  stroke-linejoin: round;
  /* Same cream as the intro logo. Filled from first paint — the mark arrives
     complete and the scroll timeline drains it. Also the correct resting state
     for reduced-motion users, who never run the timeline at all. */
  fill: var(--cream);
  fill-opacity: 1;
}

.hero__logo-sub {
  display: block; margin-top: .38em;
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.5rem, 3.8vw, 2.9rem);
  letter-spacing: .3em; margin-right: -.3em;
  color: var(--gold-text);
  text-shadow: 0 2px 12px rgba(9, 18, 27, .9);
}

.hero__sunburst {
  z-index: 2; pointer-events: none;
  width: 100%; height: 100%; object-fit: cover;
  opacity: .5; mix-blend-mode: screen;
  transform-origin: 50% 46%;
}

/* ---------- scene 2: interior ---------- */
/* Starts hidden; the timeline cross-fades it up over the exterior. */
.scene--interior { z-index: 20; opacity: 0; }
.interior__plate { width: 100%; height: 100%; }
/* Warms the plate toward the brand gold and keeps the copy legible over it. */
.interior__grade {
  background:
    radial-gradient(120% 90% at 50% 40%, rgba(172,135,85,.16) 0%, transparent 65%),
    linear-gradient(180deg, rgba(18,34,49,.30) 0%, rgba(18,34,49,.16) 45%, rgba(18,34,49,.86) 100%);
}

/* ---------- atmosphere: spans BOTH scenes ---------- */
/* Sits above both scenes and outside either, so the mist carries straight
   through the cut instead of fading out with the exterior. */
.atmos {
  z-index: 30; width: 100%; height: 100%;
  pointer-events: none;
  mix-blend-mode: screen;
}
/* Bright light on black, so `screen` does the keying and the frames need no
   alpha channel. Kept well under 1 or the smoke buries the scene. */
.atmos--smoke { opacity: .24; }
.atmos--dust  { opacity: .42; }

.journey__vignette {
  z-index: 35; pointer-events: none;
  background: linear-gradient(180deg, rgba(18,34,49,0) 62%, var(--navy) 100%);
}

/* ---------- copy ---------- */
/* Both copy blocks occupy the same box in the same stage, so whichever comes
   later in the DOM sits over the other — and `opacity: 0` does NOT stop an
   element being hit-tested. The faded-out interior block was swallowing clicks
   on the exterior CTA underneath it. Nothing here takes pointer input except
   the buttons, so the container opts out entirely and links opt back in. */
.journey__content {
  position: absolute; z-index: 40; inset: auto 0 0 0;
  display: grid; justify-items: center; gap: 1.15rem;
  padding: 0 1.5rem 9vh;
  text-align: center;
  pointer-events: none;
}
.journey__content a { pointer-events: auto; }
.journey__content--interior { opacity: 0; }

.eyebrow {
  margin: 0; color: var(--gold-text);
  font-size: clamp(.95rem, 1.5vw, 1.05rem); font-weight: 600;
  /* Wide tracking costs legibility at small sizes; eased off with the bump. */
  letter-spacing: .2em; text-transform: uppercase;
}
.display {
  margin: 0; font-family: var(--font-display); font-weight: 700;
  font-size: clamp(2rem, 6.4vw, 4.8rem); line-height: 1.06; letter-spacing: .015em;
}
/* overflow-hidden box + inner pushed below it = the line-reveal rig */
.line { display: block; overflow: hidden; }
.line__inner { display: block; will-change: transform; }

.sub {
  margin: 0; color: rgba(245,239,226,.92);
  font-size: clamp(1.2rem, 2.4vw, 1.5rem); line-height: 1.5;
  max-width: 40ch;
}

/* Lead-page spec: Space Grotesk 600 / 12px / .08em tracking, gold hairline box */
.chips {
  display: flex; flex-wrap: wrap; justify-content: center; gap: .5rem;
  margin: .35rem 0 0; padding: 0; list-style: none;
}
/* Solid gold gradient rather than an outline. Cream-on-transparent sat over
   photography and the contrast collapsed wherever the plate was bright; navy
   on gold holds ~6.3:1 no matter what is behind it. */
.chips li {
  padding: .72rem 1.2rem;
  border: 0; border-radius: 999px;
  background: linear-gradient(145deg, #e3c089 0%, #c49a5e 48%, #a87f4d 100%);
  font-family: var(--font-ui); font-weight: 700;
  font-size: clamp(.95rem, 1.4vw, 1.02rem);
  letter-spacing: .05em; text-transform: uppercase;
  color: var(--navy);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.35),
              0 2px 10px rgba(9,18,27,.35);
}

.btn {
  margin-top: .6rem;
  display: inline-flex; align-items: center; gap: .6rem;
  padding: 1.15rem 2.4rem;
  background: var(--gold); color: var(--navy);
  font-family: var(--font-ui); font-weight: 600; font-size: clamp(1rem, 2.6vw, 1.1rem);
  text-decoration: none;
  border-radius: 999px;
  transition: transform .25s ease, filter .25s ease;
}
.btn:hover { transform: translateY(-2px); filter: brightness(1.07); }
.btn--lg { padding: clamp(1.1rem, 3.4vw, 1.35rem) clamp(1.9rem, 7vw, 3rem); font-size: clamp(1.05rem, 3vw, 1.25rem); }

.frame-corner {
  position: absolute; z-index: 45; width: 74px; height: 74px;
  border: 1px solid var(--gold); opacity: .55; pointer-events: none;
}
.frame-corner--tl { top: 22px; left: 22px;  border-right: 0; border-bottom: 0; }
.frame-corner--tr { top: 22px; right: 22px; border-left: 0;  border-bottom: 0; }
.frame-corner--bl { bottom: 22px; left: 22px;  border-right: 0; border-top: 0; }
.frame-corner--br { bottom: 22px; right: 22px; border-left: 0;  border-top: 0; }

/* ---------- scrubbed line reveal ---------- */
.why { padding: 22vh 6vw; max-width: 1100px; margin: 0 auto; }
.why__eyebrow {
  color: var(--gold-text); font-size: clamp(.95rem, 1.5vw, 1.05rem); font-weight: 600;
  letter-spacing: .2em; text-transform: uppercase; margin: 0 0 1.5rem;
}
/* Syne below the fold — matches the lead page, and its negative tracking is
   the opposite of what Cinzel wants above. */
.why__copy {
  display: grid; gap: .1em;
  font-family: var(--font-head); font-weight: 800;
  font-size: clamp(1.6rem, 4.6vw, 3.4rem); line-height: 1.18; letter-spacing: -.01em;
}
.reveal { position: relative; display: block; width: fit-content; }
/* Mask sits over the line and retracts on scroll — text "inks in" rather than fading. */
.reveal__mask {
  position: absolute; inset: -6% -1% -6% 0;
  background: var(--navy); opacity: .82;
  transform-origin: right center;
  will-change: transform;
}

/* ---------- closing section ---------- */
/* Same pattern as .journey — sticky stage in a taller runway so the plate has
   scroll distance to scrub against instead of being a static backdrop. */
.close { position: relative; height: 230vh; }
.close__stage {
  position: sticky; top: 0;
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  background: var(--navy);
}
.close__plate { width: 100%; height: 100%; opacity: 0; }
.close__grade {
  background:
    radial-gradient(115% 85% at 50% 45%, rgba(172,135,85,.18) 0%, transparent 62%),
    linear-gradient(180deg, var(--navy) 0%, rgba(18,34,49,.55) 30%, rgba(18,34,49,.92) 100%);
}
/* Atmosphere repeats here so the mist carries into the close. */
.atmos--close { opacity: 0; }

.close__content {
  position: absolute; z-index: 40; inset: 0;
  display: grid; place-content: center; justify-items: center;
  gap: 1.1rem; padding: 0 1.5rem; text-align: center;
}
.close__content h2 {
  font-family: var(--font-head); font-weight: 800; letter-spacing: -.01em;
  font-size: clamp(2rem, 5.4vw, 3.6rem); margin: 0;
}
.close__tel {
  color: rgba(245,239,226,.85); font-size: clamp(1rem, 2.6vw, 1.1rem); text-decoration: none;
  border-bottom: 1px solid rgba(245,239,226,.25); padding-bottom: 2px;
}
.close__tel:hover { color: var(--cream); }

@media (prefers-reduced-motion: reduce) {
  .line__inner { transform: none !important; }
  .reveal__mask { display: none; }
  .scene--interior, .journey__content--interior { opacity: 1; }
}

/* ============================================================
   MOBILE  (<= 430px)
   The layout is fluid everywhere, so this block only handles what
   fluidity cannot: crop focal points, badge stacking, and spacing
   that is proportionally too generous once the viewport is narrow.
   ============================================================ */
@media (max-width: 430px) {

  /* --- 1. crop focal points --------------------------------
     `cover` on a portrait viewport crops hard from the sides. Both
     plates are re-aimed so the subject survives the crop instead of
     defaulting to dead centre. (The canvas sequences are aimed in JS —
     see `focal` in frameSequence.) */
  .hero__sky {
    background-position: 62% 28%;   /* keeps the lit cloud break in frame */
    background-size: cover;
  }
  .hero__sunburst {
    object-position: 50% 40%;       /* rays radiate from behind the mark */
  }
  .hero__house img {
    width: 165vw;                   /* the house is a 5.8:1 strip; let it bleed */
    margin-bottom: 3vh;
  }

  /* --- 3. feature badges: one per row ----------------------
     At 2-up they compress and break mid-phrase. Stacked full-width
     they stay legible and become comfortable tap targets. */
  /* Stacking costs ~110px of height versus the 2-up wrap, so the padding
     inside each badge is trimmed to pay some of it back. */
  .chips {
    flex-direction: column;
    align-items: stretch;
    gap: .45rem;
    width: 100%;
    max-width: 18rem;
    margin-inline: auto;
  }
  .chips li {
    padding: .68rem 1rem;
    text-align: center;
  }

  /* --- 4. proportional spacing ------------------------------ */
  .nav { padding: 1.05rem 1.15rem; }
  /* The nav is ~55px tall here; 2.4vh (20px) started the mark inside it, so
     scaling overlapped the ZSD mark and the hamburger. */
  .hero__logo { padding-top: 9vh; }
  .wordmark { width: 74vw; }
  .journey__content { padding: 0 1.15rem 4.5vh; gap: .8rem; }
  .close__content { padding: 0 1.15rem; gap: .85rem; }
  .why { padding: 13vh 1.35rem; }
  .frame-corner { width: 42px; height: 42px; }
  .frame-corner--tl, .frame-corner--tr { top: 14px; }
  .frame-corner--bl, .frame-corner--br { bottom: 14px; }
  .frame-corner--tl, .frame-corner--bl { left: 14px; }
  .frame-corner--tr, .frame-corner--br { right: 14px; }
  .intro__enter { margin-top: 1.9rem; padding: .95rem 2.2rem; }
  .intro__line.is-on { width: 74vw; }
  .btn { padding: 1rem 1.9rem; }
}

/* Short viewports (landscape phones) run out of height, not width. */
@media (max-height: 560px) {
  .hero__logo { padding-top: 1.5vh; }
  .wordmark { width: min(38vw, 420px); }
  .journey__content { padding-bottom: 3vh; gap: .6rem; }
  .chips { display: none; }   /* first thing to go when height is scarce */
}
