/* ============================================================
   wahab.design
   Visual language: Apple marketing pages (apple.com product pages).
   Tokens below are measured from apple.com/ae/iphone-duo and
   apple.com/ae/macbook-neo computed styles, not approximated.
   Structure: jamesayres.com (airy hero, work grouped by company,
   sticky company label riding each group).
   ============================================================ */

/* ---------- Fonts ----------
   Two roles, deliberately different faces.

   DISPLAY is Instrument Sans, self-hosted. It carries every headline,
   the hero, section titles, chips, buttons and the wordmark. Because it
   has to win over the system face, the display stack lists it first and
   does not name SF Pro or -apple-system ahead of it: on a Mac or an
   iPhone those resolve, and the webfont would never be seen.

   TEXT stays on the system stack, SF Pro on Apple platforms, so body
   copy keeps the native rendering the rest of the visual language is
   measured against. SF Pro is not licensable for web redistribution,
   so Inter is self-hosted as its cross-platform stand-in, being the
   closest metric match among freely licensable faces.

   Both are self-hosted rather than linked from Google or Apple. A CDN
   font is a third-party request that hands every visitor's IP to
   another company, which would undo the point of choosing cookieless
   analytics for this site.

   Instrument Sans ships as the latin subset, roman only, variable on
   weight 400 to 700. The site uses 400, 600 and 700, all inside that
   axis. There is no italic because nothing sets one.
   Licence: SIL Open Font License 1.1.
--------------------------------------------------------------- */
@font-face {
  font-family: "Instrument Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/InstrumentSans-var.woff2") format("woff2");
}
@font-face {
  font-family: "Inter var";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../fonts/Inter-var.woff2") format("woff2");
}

/* ============================================================
   1. TOKENS
   ============================================================ */
:root {
  /* Type stacks */
  /* Instrument Sans leads, or the system face would win on Apple
     devices and the webfont would never render. See Fonts above. */
  --font-display: "Instrument Sans", "Inter var", -apple-system,
    BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-text: "SF Pro Text", -apple-system, BlinkMacSystemFont,
    "Inter var", "Helvetica Neue", Helvetica, Arial, sans-serif;
  /* Hero two-tone, measured: ink rgb(54,54,54), ghost rgb(210,210,210) */
  --hero-ink:   #363636;
  --hero-ghost: #d2d2d2;

  /* Colour - Apple light (measured) */
  --ink:        #1d1d1f;
  --ink-2:      #6e6e73;
  --ink-3:      #86868b;
  --page:       #ffffff;
  --surface:    #f5f5f7;
  --surface-2:  #ebebed;
  --hairline:   #d2d2d7;
  --hairline-soft: #e6e6e8;

  /* Pill language, measured from the reference recording.
     Active pill rgb(44,44,44), idle pill rgb(243,243,243). */
  --pill-idle:      #f3f3f3;
  --pill-idle-ink:  #1d1d1f;
  --pill-active:    #2c2c2c;
  --pill-active-ink:#ffffff;

  /* Accent is now reserved for inline text links only */
  --accent:         #0071e3;
  --accent-hover:   #0077ed;
  --accent-pressed: #006edb;
  --on-accent:      #ffffff;

  /* Glass material - Apple localnav (measured) */
  --glass-bg:     rgba(255, 255, 255, 0.8);
  --glass-ring:   #e6e6e8;
  --glass-blur:   blur(20px) saturate(180%);

  /* Shape - documented rule:
     anything interactive, plus the company chips = pill,
     large media = 28px, small media = 18px, focus rings = 12px.
     Nothing else. */
  --r-pill: 980px;
  --r-lg:   28px;
  --r-md:   18px;
  --r-sm:   12px;

  /* Measure */
  --w-copy:    692px;
  --w-content: 980px;
  --w-wide:    1440px;
  --gutter:    clamp(22px, 5vw, 48px);

  /* Rhythm - VISUAL_DENSITY 2, gallery spacing */
  --space-section: clamp(96px, 14vw, 200px);
  --space-group:   clamp(64px, 9vw, 128px);
  --space-block:   clamp(20px, 2.4vw, 32px);

  /* Motion - Apple marketing easing */
  --ease:      cubic-bezier(0.28, 0.11, 0.32, 1);
  --ease-soft: cubic-bezier(0.4, 0, 0.6, 1);

  --nav-h: 56px;      /* the floating pill */
  --nav-top: 12px;    /* its gap from the top of the window */
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ink:        #f5f5f7;
    --ink-2:      #a1a1a6;
    --ink-3:      #86868b;
    /* Apple genuinely uses pure #000 on dark marketing pages.
       Keeping it, because the brief asks for Apple exactly. */
    --page:       #000000;
    --surface:    #161617;
    --surface-2:  #1d1d1f;
    --hairline:   #424245;
    --hairline-soft: #2c2c2e;

    --accent:         #2997ff;
    --accent-hover:   #47a5ff;
    --accent-pressed: #1a8cff;
    --on-accent:      #ffffff;

    --glass-bg:   rgba(22, 22, 23, 0.8);
    --glass-ring: #2c2c2e;

    --pill-idle:      #1d1d1f;
    --pill-idle-ink:  #f5f5f7;
    --pill-active:    #f5f5f7;
    --pill-active-ink:#1d1d1f;
  }
}

:root[data-theme="dark"] {
  --ink: #f5f5f7;  --ink-2: #a1a1a6;  --ink-3: #86868b;
  --page: #000000; --surface: #161617; --surface-2: #1d1d1f;
  --hairline: #424245; --hairline-soft: #2c2c2e;
  --accent: #2997ff; --accent-hover: #47a5ff; --accent-pressed: #1a8cff;
  --glass-bg: rgba(22, 22, 23, 0.8); --glass-ring: #2c2c2e;
  --pill-idle: #1d1d1f; --pill-idle-ink: #f5f5f7;
  --pill-active: #f5f5f7; --pill-active-ink: #1d1d1f;
}

/* ============================================================
   2. BASE
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-top) + var(--nav-h) + 20px);
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font-family: var(--font-text);
  font-size: 17px;
  line-height: 25px;
  letter-spacing: -0.022em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

img, video { display: block; max-width: 100%; height: auto; }
figure { margin: 0; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}

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

.skip-link {
  position: absolute; top: -100px; left: 50%;
  transform: translateX(-50%);
  z-index: 100;
  background: var(--page); color: var(--ink);
  padding: 12px 22px; border-radius: var(--r-pill);
  box-shadow: 0 0 0 1px var(--hairline);
  font-size: 14px; font-weight: 500;
}
.skip-link:focus { top: 12px; text-decoration: none; }

/* ============================================================
   3. TYPE RAMP  (Apple measured values)
   ============================================================ */
.t-display {
  font-family: var(--font-display);
  font-size: clamp(40px, 7.2vw, 64px);
  line-height: 1.0625;
  font-weight: 600;
  letter-spacing: -0.009em;
  margin: 0;
}
.t-headline {
  font-family: var(--font-display);
  font-size: clamp(32px, 5vw, 48px);
  line-height: 1.0834;
  font-weight: 600;
  letter-spacing: -0.003em;
  margin: 0;
}
.t-title {
  font-family: var(--font-display);
  font-size: clamp(24px, 3.2vw, 28px);
  line-height: 1.1429;
  font-weight: 600;
  letter-spacing: 0.007em;
  margin: 0;
}
.t-intro {
  font-family: var(--font-text);
  font-size: clamp(19px, 2.2vw, 21px);
  line-height: 1.381;
  font-weight: 400;
  letter-spacing: 0.011em;
  margin: 0;
  color: var(--ink-2);
}
.t-body { font-size: 17px; line-height: 25px; letter-spacing: -0.022em; margin: 0; }
.t-caption {
  font-size: 14px; line-height: 20px; letter-spacing: -0.016em;
  color: var(--ink-2); margin: 0;
}
.t-micro {
  font-size: 12px; line-height: 16px; letter-spacing: -0.01em;
  color: var(--ink-3); margin: 0;
}

/* ============================================================
   4. LAYOUT PRIMITIVES
   ============================================================ */
.wrap      { width: 100%; max-width: var(--w-content); margin-inline: auto; padding-inline: var(--gutter); }

/* ============================================================
   5. BUTTONS  (Apple pill, measured)
   ============================================================ */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-text);
  font-size: 17px; line-height: 25px; font-weight: 400;
  letter-spacing: -0.022em;
  padding: 12px 22px;
  border-radius: var(--r-pill);
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;           /* CTA never wraps */
  transition: background-color 0.28s var(--ease-soft),
              color 0.28s var(--ease-soft),
              transform 0.18s var(--ease-soft),
              border-color 0.28s var(--ease-soft);
}
.btn:hover { text-decoration: none; }
.btn:active { transform: scale(0.97); }

.btn--primary { background: var(--pill-active); color: var(--pill-active-ink); }
.btn--primary:hover { opacity: 0.86; }

.btn--secondary { background: var(--pill-idle); color: var(--pill-idle-ink); }
.btn--secondary:hover { background: var(--surface-2); }

.btn--sm { font-size: 14px; line-height: 20px; padding: 7px 16px; letter-spacing: -0.016em; }

/* ============================================================
   6. NAV  (Apple localnav: 52px, glass, one line)
   ============================================================ */
.nav {
  position: fixed; inset: var(--nav-top) 0 auto 0;
  z-index: 50;
  display: flex; justify-content: center;
  padding-inline: var(--gutter);
  pointer-events: none;              /* only the pill itself is clickable */
  opacity: 0;
  transform: translateY(-140%);
  transition: opacity 0.4s var(--ease), transform 0.4s var(--ease);
}
.nav.is-visible { opacity: 1; transform: translateY(0); }

/* A contained pill rather than a full width bar. */
.nav__inner {
  pointer-events: auto;
  height: 56px;
  max-width: 100%;
  display: flex; align-items: center;
  gap: clamp(20px, 4vw, 44px);
  padding-inline: clamp(18px, 2.4vw, 30px);
  border-radius: var(--r-pill);
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  box-shadow: 0 0 0 1px var(--glass-ring), 0 10px 40px rgba(0, 0, 0, 0.08);
}

.nav__brand {
  font-family: var(--font-display);
  font-size: 17px; font-weight: 600; letter-spacing: -0.022em;
  color: var(--ink); text-decoration: none; white-space: nowrap;
}
.nav__brand:hover { text-decoration: none; }

.nav__links { display: flex; align-items: center; gap: 8px; }
.nav__link {
  font-size: 15px; line-height: 20px; font-weight: 500;
  letter-spacing: -0.016em;
  color: var(--pill-idle-ink);
  background: var(--pill-idle);
  text-decoration: none;
  height: 36px; padding: 0 16px;
  display: inline-flex; align-items: center;
  border-radius: var(--r-pill);
  white-space: nowrap;
  transition: background-color 0.24s var(--ease-soft), color 0.24s var(--ease-soft);
}
.nav__link:hover { background: var(--surface-2); text-decoration: none; }
/* The section you are currently in takes the dark fill. */
.nav__link[aria-current="true"] {
  background: var(--pill-active);
  color: var(--pill-active-ink);
}

/* Three pills fit on a phone once they are compacted, and they are the
   only wayfinding there is, so they stay rather than collapsing away. */
@media (max-width: 734px) {
  .nav__links { gap: 6px; }
  .nav__link { height: 32px; padding: 0 12px; font-size: 14px; }
  /* The pill is centred while the appearance toggle is pinned right, so
     on a phone they collide. The three section pills are the wayfinding
     that matters here; the name is already the largest thing in the hero,
     so it is the one to drop. */
  .nav__brand { display: none; }
  .nav__inner { gap: 0; padding-inline: 14px; height: 48px; }
}

/* ============================================================
   6b. APPEARANCE TOGGLE
   Sits above the nav in its own fixed dock, using the same
   container and gutter, so it holds the top right corner over
   the hero and lines up inside the nav row after it.
   ============================================================ */
.theme-dock {
  position: fixed; inset: var(--nav-top) 0 auto 0;
  height: var(--nav-h);
  z-index: 60;                    /* above the nav at 50 */
  display: flex; align-items: center;
  pointer-events: none;           /* only the button is clickable */
}
.theme-dock__inner {
  width: 100%;
  max-width: var(--w-wide);
  margin-inline: auto;
  padding-inline: var(--gutter);
  display: flex; justify-content: flex-end;
}

.theme-toggle {
  pointer-events: auto;
  position: relative;
  width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0;
  border: 0;
  border-radius: var(--r-pill);
  color: var(--ink);
  background: var(--glass-bg);
  box-shadow: 0 0 0 1px var(--glass-ring);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  cursor: pointer;
  transition: background-color 0.28s var(--ease-soft),
              box-shadow 0.28s var(--ease-soft),
              transform 0.18s var(--ease-soft);
}
/* The mark stays 32px to sit correctly in a 52px bar, but the touch
   target is pushed out to 44px to meet Apple's guidance. */
.theme-toggle::after {
  content: "";
  position: absolute;
  inset: -6px;
}
.theme-toggle:hover { background: var(--surface-2); }
.theme-toggle:active { transform: scale(0.94); }
.theme-toggle svg { width: 17px; height: 17px; display: block; }

/* Show the appearance the button will switch TO, never the current one. */
.theme-toggle__icon { display: none; line-height: 0; }
:root[data-theme="light"] .theme-toggle__icon[data-icon="moon"] { display: block; }
:root[data-theme="dark"]  .theme-toggle__icon[data-icon="sun"]  { display: block; }

/* With no saved choice there is no attribute, so follow the system. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) .theme-toggle__icon[data-icon="moon"] { display: block; }
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) .theme-toggle__icon[data-icon="sun"] { display: block; }
}

/* ============================================================
   7. HERO
   Type treatment and card mechanics copied from spencergabor.work.
   The rotations, vertical offsets, overlap and the velocity tilt
   coefficient are their exact values, read off the live stylesheet.
   ============================================================ */
.hero {
  min-height: 100dvh;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  padding: clamp(80px, 10vh, 96px) var(--gutter) clamp(48px, 8vh, 96px);
  text-align: center;
  container-type: inline-size;
  overflow: hidden;
}
.hero__eyebrow {
  font-family: var(--font-display);
  font-size: 14px; font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--hero-ink);
  margin: 0;
}
.hero__email {
  font-family: var(--font-display);
  font-size: 13px; font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-3);
  margin: 4px 0 0;
}
.hero__email a { color: inherit; }

.hero__line {
  font-family: var(--font-display);
  font-size: clamp(40px, 9.2cqi, 128px);
  line-height: 0.82;
  font-weight: 700;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  margin: 0;
  text-wrap: balance;
}
.hero__line--ink   { color: var(--hero-ink); margin-top: clamp(24px, 5vh, 56px); }
.hero__line--ghost { color: var(--hero-ghost); }

/* ---------- The four cards ----------
   .hero__images is pointer-events:none so the stack never blocks the
   text or the CTAs underneath it. */
.hero__images {
  display: flex;
  place-items: center;
  justify-content: center;
  pointer-events: none;
  position: relative;
  touch-action: none;
  width: 100%;
  z-index: 1;
  /* Clear the name line, then sit into the top of the role line.
     That is the relationship on the reference: the stack overlaps the
     lower display line, never the upper one. */
  margin-block: clamp(6px, 1.4vw, 20px) clamp(-12px, -1.2vw, 0px);
}
.hero__image {
  aspect-ratio: 1 / 1;
  border-radius: 1rem;
  display: grid;
  width: clamp(6rem, 100%, 14cqi);
}
.hero__image:not(:last-child) { margin-right: -6%; }

/* Every child shares one grid cell, so the shadow layer and the image
   move together. translate takes the spring offset, rotate takes the
   base angle plus the velocity tilt. */
.hero__image > * {
  border-radius: inherit;
  grid-area: 1 / 1;
  translate: calc(var(--offsetX, 0) * 1px)
             calc(var(--offsetY, 0) * 1px + var(--translateY, 0%));
}
.hero__image-media { position: relative; overflow: hidden; border-radius: inherit; }
.hero__image-media > img,
.hero__image-media > video {
  width: 100%; height: 100%; object-fit: cover; pointer-events: auto;
}

.hero__image:nth-child(1) > * { rotate: calc( 2deg - var(--velocity, 0) * 0.25deg); --translateY:  5%; }
.hero__image:nth-child(2) > * { rotate: calc(-3deg - var(--velocity, 0) * 0.25deg); --translateY: -6%; }
.hero__image:nth-child(3) > * { rotate: calc( 6deg - var(--velocity, 0) * 0.25deg); --translateY:  5%; }
.hero__image:nth-child(4) > * { rotate: calc(-2deg - var(--velocity, 0) * 0.25deg); --translateY: -4%; }

.hero__image .shadow {
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.16), 0 2px 8px rgba(0, 0, 0, 0.08);
  background: var(--surface);
}

/* The placeholder sits inside the rotating child so it tilts too. */
.hero__image .shot__ph { border-radius: inherit; }

/* Spring pop-in, their exact easing curve. Driven entirely by
   @starting-style, so the resting state is scale 1. If the browser
   does not support it, or scripting is off, the cards are simply
   there. They are never hidden waiting on something to reveal them. */
@media (prefers-reduced-motion: no-preference) {
  .hero__image {
    scale: 1;
    transition: scale 1s linear(
      0 0%, 0.5737 7.6%, 0.8382 11.87%, 0.9463 14.19%, 1.0292 16.54%,
      1.0886 18.97%, 1.1258 21.53%, 1.137 22.97%, 1.1424 24.48%,
      1.1423 26.1%, 1.1366 27.86%, 1.1165 31.01%, 1.0507 38.62%,
      1.0219 42.57%, 0.9995 46.99%, 0.9872 51.63%, 0.9842 58.77%,
      1.0011 81.26%, 1 100%) calc(0.15s + var(--delay, 0) * 0.08s);
  }
  @starting-style {
    .hero__image { scale: 0; }
  }
}

.hero__cta {
  margin-top: clamp(28px, 5vh, 48px);
  display: flex; flex-wrap: wrap; gap: 12px; justify-content: center;
}

@container (max-width: 700px) {
  .hero__line { font-size: clamp(32px, 11cqi, 64px); }
  /* The 6rem floor is far too big once the type shrinks, so the stack
     stops covering the words it is supposed to sit among. */
  .hero__image { width: clamp(3.5rem, 18cqi, 7rem); }
  /* The role line wraps to two lines on a phone, so the stack has to
     clear it rather than sit on it, or the first line disappears. */
  .hero__images { margin-block: clamp(6px, 2vw, 14px) clamp(10px, 3vw, 20px); }
}

/* ============================================================
   8. WORK GROUP  (the jamesayres sticky-company pattern)
   Pure CSS sticky: each company label rides its own group and is
   pushed out by the next group's label. No scroll listeners.
   ============================================================ */
.work { padding-bottom: var(--space-section); }

.group { position: relative; padding-bottom: var(--space-group); }

.group__label {
  position: sticky;
  top: calc(var(--nav-top) + var(--nav-h) + 12px);
  z-index: 20;
  display: flex; justify-content: center;
  pointer-events: none;                 /* never blocks the media below */
  margin-bottom: var(--space-block);
}
.chip {
  pointer-events: auto;
  display: inline-flex; align-items: center;
  font-family: var(--font-display);
  font-size: 14px; line-height: 20px; font-weight: 500;
  letter-spacing: -0.016em;
  color: var(--ink);
  padding: 7px 16px;
  border-radius: var(--r-pill);
  background: transparent;
  box-shadow: 0 0 0 1px transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  transition: background-color 0.35s var(--ease),
              box-shadow 0.35s var(--ease),
              backdrop-filter 0.35s var(--ease);
}
/* Becomes the Apple glass material only once it is actually floating */
.chip.is-stuck {
  background: var(--glass-bg);
  box-shadow: 0 0 0 1px var(--glass-ring);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
}

.group__head {
  max-width: var(--w-copy);
  margin: 0 auto var(--space-block);
  padding-inline: var(--gutter);
  text-align: center;
}
.group__head .t-intro { margin-top: 12px; }

/* ============================================================
   9. MEDIA SLOTS
   Empty, labelled export targets. Drop a file named after the slot
   into assets/media/ and site.js swaps it in automatically.
   ============================================================ */
.shot {
  position: relative;
  aspect-ratio: var(--ar, 16 / 9);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--surface);
  box-shadow: 0 0 0 1px var(--hairline-soft);
}
.shot > img, .shot > video { width: 100%; height: 100%; object-fit: cover; }

.shot--sm { border-radius: var(--r-md); }
.shot--dark { background: #000; box-shadow: none; }

/* The empty-slot treatment lives on the placeholder, not the card, so
   it vanishes completely the moment real media is dropped in. */
.shot__ph {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 6px; padding: 24px; text-align: center;
  background: var(--surface-2);
  box-shadow: inset 0 0 0 1px var(--hairline);
  border-radius: inherit;
}
.shot__name {
  font-size: 14px; line-height: 20px; font-weight: 500;
  letter-spacing: -0.016em; color: var(--ink-2);
}
.shot__spec {
  font-size: 12px; line-height: 16px; letter-spacing: -0.01em;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.shot--dark .shot__ph {
  background: #1d1d1f;
  box-shadow: inset 0 0 0 1px #3a3a3c;
}
.shot--dark .shot__name { color: rgba(245,245,247,0.72); }
.shot--dark .shot__spec { color: rgba(245,245,247,0.44); }

/* ---- Layout family A: full-bleed hero + two-up  (du) ---- */
.lay-bleed { padding-inline: var(--gutter); }
.lay-bleed .shot { max-width: var(--w-wide); margin-inline: auto; }

.lay-duo {
  max-width: var(--w-wide); margin: var(--space-block) auto 0;
  padding-inline: var(--gutter);
  display: grid; gap: var(--space-block);
  grid-template-columns: repeat(2, 1fr);
}
@media (max-width: 734px) { .lay-duo { grid-template-columns: 1fr; } }

/* ---- Layout family B: full-bleed statement card  (Tetris) ---- */
.statement {
  max-width: var(--w-wide); margin-inline: auto;
  margin-bottom: var(--space-block);
  border-radius: var(--r-lg);
  background: #000;
  color: #f5f5f7;
  min-height: clamp(360px, 52vh, 620px);
  display: grid; place-items: center;
  padding: clamp(40px, 8vw, 96px) var(--gutter);
  text-align: center;
  overflow: hidden;
}
.statement__word {
  font-family: var(--font-display);
  font-size: clamp(56px, 13vw, 160px);
  line-height: 0.95;
  font-weight: 600;
  letter-spacing: -0.015em;
  margin: 0;
  color: #f5f5f7;
}
.statement__sub { margin-top: 20px; color: rgba(245,245,247,0.72); }

/* ---- Layout family C: device triptych + system board  (Welfare) ---- */
.lay-phones {
  max-width: var(--w-content); margin-inline: auto;
  padding-inline: var(--gutter);
  display: grid; gap: clamp(16px, 3vw, 40px);
  grid-template-columns: repeat(3, 1fr);
  align-items: end;
}
@media (max-width: 734px) {
  .lay-phones {
    grid-template-columns: repeat(3, 72vw);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding-bottom: 12px;
    -webkit-overflow-scrolling: touch;
  }
  .lay-phones > * { scroll-snap-align: center; }
}

/* CSS device frame. The frame is a mockup shell; the screen inside
   is an explicit empty slot, never a drawn fake interface. */
.device {
  position: relative;
  border-radius: clamp(28px, 4.4vw, 44px);
  padding: clamp(6px, 0.9vw, 10px);
  background: linear-gradient(160deg, #3a3a3c 0%, #1d1d1f 46%, #3a3a3c 100%);
  box-shadow: 0 22px 60px rgba(0, 0, 0, 0.18), 0 2px 8px rgba(0, 0, 0, 0.1);
}
.device__screen {
  position: relative;
  aspect-ratio: 9 / 19.5;
  border-radius: clamp(22px, 3.6vw, 36px);
  overflow: hidden;
  background: var(--surface);
}
.device__screen > img, .device__screen > video { width: 100%; height: 100%; object-fit: cover; }
.device__notch {
  position: absolute; top: clamp(8px, 1.1vw, 12px); left: 50%;
  transform: translateX(-50%);
  width: 32%; height: clamp(18px, 2.4vw, 26px);
  border-radius: var(--r-pill);
  background: #1d1d1f;
  z-index: 2;
}
.device--raised { transform: translateY(clamp(-40px, -5vw, -16px)); }
@media (max-width: 734px) { .device--raised { transform: none; } }

.lay-board {
  max-width: var(--w-content); margin: var(--space-block) auto 0;
  padding-inline: var(--gutter);
}

/* ---- Layout family D: horizontal journey strip  (Hala) ---- */
.journey {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: clamp(220px, 24vw, 300px);
  gap: clamp(14px, 2vw, 24px);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding: 4px var(--gutter) 20px;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}
.journey > * { scroll-snap-align: center; }
.journey::-webkit-scrollbar { height: 6px; }
.journey::-webkit-scrollbar-thumb { background: var(--hairline); border-radius: var(--r-pill); }
.journey::-webkit-scrollbar-track { background: transparent; }

.step__label {
  margin-top: 12px;
  font-size: 14px; line-height: 20px; font-weight: 500;
  letter-spacing: -0.016em; color: var(--ink);
}
.step__note {
  display: block;                /* it is a span inside the figcaption */
  font-weight: 400;
  font-size: 14px; line-height: 20px; letter-spacing: -0.016em;
  color: var(--ink-2); margin: 2px 0 0;
}

/* ---- Layout family E: asymmetric split + wide board  (Thndr) ---- */
.lay-split {
  max-width: var(--w-content); margin-inline: auto;
  padding-inline: var(--gutter);
  display: grid;
  grid-template-columns: 5fr 7fr;
  gap: clamp(20px, 3vw, 40px);
  align-items: center;
}
.lay-split__stack { display: grid; gap: clamp(20px, 3vw, 40px); }
@media (max-width: 734px) {
  .lay-split { grid-template-columns: 1fr; }
}

/* ============================================================
   10. CONTACT
   ============================================================ */
.contact {
  padding-block: var(--space-section);
  background: var(--surface);
  text-align: center;
}
.contact__headline { margin: 0 auto; max-width: 16ch; text-wrap: balance; }
.contact__sub { margin: 20px auto 0; max-width: 40ch; }
.contact__actions {
  margin-top: 36px;
  display: flex; flex-wrap: wrap; gap: 12px; justify-content: center;
}

/* ============================================================
   11. FOOTER  (Apple globalfooter)
   ============================================================ */
.footer {
  background: var(--surface);
  color: var(--ink-3);
  padding-block: 28px 44px;
  box-shadow: inset 0 1px 0 var(--hairline-soft);
}
.footer__inner {
  max-width: var(--w-wide); margin-inline: auto; padding-inline: var(--gutter);
  display: flex; flex-wrap: wrap; gap: 12px 28px;
  align-items: center; justify-content: space-between;
}
.footer__links { display: flex; flex-wrap: wrap; gap: 20px; }
.footer a { color: var(--ink-2); font-size: 12px; line-height: 16px; letter-spacing: -0.01em; }
.footer a:hover { color: var(--ink); }

/* ============================================================
   12. SCROLL REVEAL
   MOTION_INTENSITY 5. Structural reveal only - the richer
   micro-animation pass is deliberately deferred.
   Driven by IntersectionObserver, never a scroll listener.
   ============================================================ */
/* Scoped to .js so that with scripting off nothing is ever hidden.
   The class is set by an inline script in <head>. */
@media (prefers-reduced-motion: no-preference) {
  .js .reveal {
    opacity: 0;
    transform: translateY(24px);
    transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
    will-change: opacity, transform;
  }
  .js .reveal.is-in { opacity: 1; transform: none; will-change: auto; }
}

/* ============================================================
   13. REDUCED TRANSPARENCY FALLBACK
   backdrop-filter support is uneven; keep contrast without it.
   ============================================================ */
@media (prefers-reduced-transparency: reduce) {
  .nav, .chip.is-stuck, .theme-toggle {
    background: var(--page);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .nav { background: var(--page); }
  .chip.is-stuck { background: var(--page); }
  .theme-toggle { background: var(--page); }
}


/* ============================================================
   14. ABOUT
   Portrait beside the bio. Layout family: portrait split, used
   exactly once on the page.
   ============================================================ */
.about { padding-block: var(--space-section) 0; }
.about__grid {
  max-width: var(--w-content); margin-inline: auto;
  padding-inline: var(--gutter);
  display: grid; grid-template-columns: 5fr 7fr;
  gap: clamp(24px, 4vw, 56px);
  align-items: start;
  margin-top: var(--space-block);
}
.about__portrait {
  aspect-ratio: 4 / 5;
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--surface);
  position: relative;
}
.about__portrait > img { width: 100%; height: 100%; object-fit: cover; }
.about__bio > p + p { margin-top: 1em; }
.about__bio p { color: var(--ink-2); }
@media (max-width: 734px) {
  .about__grid { grid-template-columns: 1fr; }
  .about__portrait { max-width: 320px; }
}

/* ============================================================
   15. CAPABILITIES
   A tag set, not a list. Layout family: pill cloud, used once.
   ============================================================ */
.caps { padding-block: var(--space-section) 0; }
.caps__grid {
  max-width: var(--w-content); margin-inline: auto;
  padding-inline: var(--gutter);
  margin-top: var(--space-block);
  display: flex; flex-wrap: wrap; gap: 10px;
}
.cap {
  display: inline-flex; align-items: center;
  height: 44px; padding: 0 22px;
  border-radius: var(--r-pill);
  background: var(--pill-idle);
  color: var(--pill-idle-ink);
  font-size: 17px; letter-spacing: -0.022em;
}

/* ============================================================
   16. APPROACH
   Numbered principles. Layout family: numbered two-column rows.
   ============================================================ */
.approach { padding-block: var(--space-section) 0; }
.approach__list {
  max-width: var(--w-content); margin-inline: auto;
  padding-inline: var(--gutter);
  margin-top: var(--space-block);
  display: grid; gap: clamp(28px, 4vw, 48px);
  grid-template-columns: repeat(2, 1fr);
  counter-reset: approach;
}
.approach__item { counter-increment: approach; }
.approach__item::before {
  content: counter(approach);
  display: block;
  font-family: var(--font-display);
  font-size: 15px; font-weight: 600;
  color: var(--ink-3);
  margin-bottom: 10px;
}
.approach__item h3 {
  font-family: var(--font-display);
  font-size: clamp(21px, 2.6vw, 24px); line-height: 1.2;
  font-weight: 600; letter-spacing: -0.003em;
  margin: 0 0 8px;
}
.approach__item p { color: var(--ink-2); margin: 0; }
@media (max-width: 734px) { .approach__list { grid-template-columns: 1fr; } }

/* ============================================================
   17. FAQ
   Native details/summary, so it opens without JS and is keyboard
   operable for free. Layout family: disclosure list.
   ============================================================ */
.faq { padding-block: var(--space-section) clamp(56px, 8vw, 96px); }
.faq__list {
  max-width: var(--w-copy); margin-inline: auto;
  padding-inline: var(--gutter);
  margin-top: var(--space-block);
}
.faq__item { border-bottom: 1px solid var(--hairline-soft); }
.faq__item summary {
  list-style: none; cursor: pointer;
  padding: 22px 40px 22px 0;
  position: relative;
  font-size: 17px; font-weight: 500; letter-spacing: -0.022em;
  color: var(--ink);
}
.faq__item summary::-webkit-details-marker { display: none; }
.faq__item summary::after {
  content: "";
  position: absolute; right: 6px; top: 50%;
  width: 9px; height: 9px;
  margin-top: -6px;
  border-right: 1.8px solid var(--ink-3);
  border-bottom: 1.8px solid var(--ink-3);
  transform: rotate(45deg);
  transition: transform 0.28s var(--ease);
}
.faq__item[open] summary::after { transform: rotate(-135deg); margin-top: -2px; }
.faq__answer { padding: 0 0 22px; color: var(--ink-2); margin: 0; }

/* ============================================================
   18. SCROLL CUE
   A small circle with a down arrow, the Mixpanel / Siena pattern.
   It lives inside the hero and scrolls away with it, so nothing
   has to watch the scroll position to hide it.
   ============================================================ */
.hero__scroll {
  margin-top: clamp(28px, 5vh, 52px);
  width: 46px; height: 46px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-pill);
  background: var(--pill-idle);
  color: var(--pill-idle-ink);
  flex: none;
  transition: background-color 0.24s var(--ease-soft),
              transform 0.24s var(--ease-soft);
}
.hero__scroll:hover { background: var(--surface-2); text-decoration: none; }
.hero__scroll:active { transform: scale(0.94); }
.hero__scroll svg { width: 18px; height: 18px; display: block; }

@media (prefers-reduced-motion: no-preference) {
  .hero__scroll svg { animation: cue-bob 1.9s var(--ease-soft) infinite; }
  @keyframes cue-bob {
    0%, 62%, 100% { transform: translateY(0); }
    28%           { transform: translateY(4px); }
  }
}

/* ============================================================
   19. COMPANY CHIP, EXPANDED
   Tapping the sticky chip opens the project detail, the way the
   jamesayres chip does. Measured from theirs: 320px wide, 32px
   radius, rgba(255,255,255,.8) over blur(24px), 16/24 text with
   labels at 50% ink.
   ============================================================ */
.chip-wrap { position: relative; pointer-events: auto; }
.chip { cursor: pointer; border: 0; font: inherit; }

.chip__panel {
  position: absolute;
  top: 0; left: 50%;
  width: min(320px, calc(100vw - var(--gutter) * 2));
  transform: translateX(-50%) scale(0.94);
  transform-origin: top center;
  border-radius: 32px;
  background: var(--glass-bg);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  backdrop-filter: blur(24px) saturate(180%);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.24),
              0 12px 48px rgba(0, 0, 0, 0.08),
              0 48px 240px rgba(0, 0, 0, 0.06);
  padding: 58px 26px 26px;
  text-align: left;
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease),
              visibility 0s linear 0.3s;
}
.chip-wrap.is-open .chip__panel {
  opacity: 1;
  transform: translateX(-50%) scale(1);
  pointer-events: auto;
  visibility: visible;
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease),
              visibility 0s;
}
/* The chip itself gets out of the way once the card is open. */
.chip-wrap.is-open .chip { opacity: 0; pointer-events: none; }

.chip__close {
  position: absolute; top: 14px; left: 50%;
  transform: translateX(-50%);
  width: 34px; height: 34px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; padding: 0;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--ink-2);
  cursor: pointer;
  transition: background-color 0.2s var(--ease-soft);
}
.chip__close:hover { background: var(--hairline); color: var(--ink); }
.chip__close svg { width: 15px; height: 15px; display: block; }

.chip__desc {
  margin: 0;
  font-size: 16px; line-height: 24px; letter-spacing: -0.01em;
  color: var(--ink);
}
.chip__meta { margin: 22px 0 0; }
.chip__meta dt {
  font-size: 16px; line-height: 24px; letter-spacing: -0.01em;
  color: var(--ink-3);
  margin-top: 18px;
}
.chip__meta dt:first-child { margin-top: 0; }
.chip__meta dd {
  margin: 0;
  font-size: 16px; line-height: 24px; letter-spacing: -0.01em;
  color: var(--ink);
}

@media (prefers-reduced-transparency: reduce) {
  .chip__panel { background: var(--page); -webkit-backdrop-filter: none; backdrop-filter: none; }
}

/* ============================================================
   20. TESTIMONIALS
   Layout family: bordered quote cards in a calm grid, after the
   Plain section on Mobbin. Distinct from the Approach rows below
   it, which are bare numbered text with no container.
   ============================================================ */
.tstm { padding-block: var(--space-section) 0; }

.tstm__grid {
  /* Wider than the reading column: at 980px each card's measure drops to
     360px, which pushed short quotes to seven lines. */
  max-width: 1180px; margin-inline: auto;
  padding-inline: var(--gutter);
  margin-top: var(--space-block);
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(16px, 2.2vw, 28px);
}

.tstm__card {
  display: flex; flex-direction: column;
  gap: 22px;
  padding: clamp(24px, 3vw, 34px);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: 0 0 0 1px var(--hairline-soft);
}

.tstm__quote {
  margin: 0;
  font-size: 17px; line-height: 26px; letter-spacing: -0.022em;
  color: var(--ink);
  /* The cards sit side by side, so the quote block pushes the
     attribution to a shared baseline at the bottom of each card. */
  flex: 1;
}

.tstm__person {
  display: flex; align-items: center; gap: 12px;
}

/* Initials rather than a photo. Using someone's LinkedIn picture
   without asking is not ours to do, and a generated face for a real
   person would be worse. */
.tstm__avatar {
  flex: none;
  width: 40px; height: 40px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-pill);
  background: var(--pill-active);
  color: var(--pill-active-ink);
  font-family: var(--font-display);
  font-size: 14px; font-weight: 600; letter-spacing: 0.02em;
}

.tstm__who { display: flex; flex-direction: column; min-width: 0; }
.tstm__name {
  font-size: 15px; line-height: 20px; font-weight: 500;
  letter-spacing: -0.016em; color: var(--ink);
}
.tstm__role {
  font-size: 14px; line-height: 19px; letter-spacing: -0.016em;
  color: var(--ink-3);
}

@media (max-width: 734px) {
  .tstm__grid { grid-template-columns: 1fr; }
}

/* ============================================================
   21. COMING SOON
   The holding page reuses the hero wholesale. Without the card
   stack between them the two display lines nearly touch at 0.82
   leading, so they get a little air here and only here.
   ============================================================ */
.hero--soon .hero__cta { margin-top: clamp(36px, 6vh, 60px); }

/* ============================================================
   22. COMING SOON BACKGROUND
   A light gradient flow in the sreedesigns register: pale blue on
   white with soft diagonal light rays drifting across it.

   Built in CSS rather than shipped as video on purpose. A 1080p
   background loop is several megabytes, decodes on every visit,
   and almost never loops seamlessly. This is under a kilobyte,
   loops perfectly by definition, and stays sharp at any size.
   Only transform and opacity animate, so it stays on the GPU.
   ============================================================ */
.soon-bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  background:
    radial-gradient(120% 90% at 80% 6%,  #cfe1fa 0%, rgba(207,225,250,0) 58%),
    radial-gradient(110% 85% at 10% 94%, #dbe9fb 0%, rgba(219,233,251,0) 60%),
    linear-gradient(168deg, #ffffff 0%, #dbe8fa 48%, #f4f8fe 100%);
}

/* Two ray sheets at slightly different angles and speeds. Crossing
   them is what stops the motion reading as a single sliding band. */
.soon-bg::before,
.soon-bg::after {
  content: "";
  position: absolute;
  top: -70%; left: -70%;
  width: 240%; height: 240%;
  will-change: transform;
}

.soon-bg::before {
  background: repeating-linear-gradient(
    64deg,
    rgba(255, 255, 255, 0)    0px,
    rgba(255, 255, 255, 1)    150px,
    rgba(255, 255, 255, 0)    420px,
    rgba(122, 166, 224, 0.5)  700px,
    rgba(255, 255, 255, 0)    1080px
  );
  filter: blur(26px);
  opacity: 1;
}

.soon-bg::after {
  background: repeating-linear-gradient(
    56deg,
    rgba(255, 255, 255, 0)    0px,
    rgba(255, 255, 255, 0.9)  260px,
    rgba(160, 197, 236, 0.4)  560px,
    rgba(255, 255, 255, 0)    920px
  );
  filter: blur(44px);
  opacity: 0.9;
}

@media (prefers-reduced-motion: no-preference) {
  .soon-bg::before { animation: rays-a 26s linear infinite; }
  .soon-bg::after  { animation: rays-b 38s linear infinite; }
}

/* Translating along the ray angle means the pattern repeats onto
   itself, so the loop has no seam. */
@keyframes rays-a {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-473px, -970px, 0); }
}
@keyframes rays-b {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(515px, 763px, 0); }
}

/* The holding page is locked to light: the artwork is a pale
   gradient, and dark ink on it is the only readable combination. */
.page-soon { background: transparent; }

/* ---------- Full bleed on iOS ----------

   .soon-bg is position:fixed, and a fixed element only ever covers the
   viewport. It cannot paint the canvas: the strip behind the status
   bar, the band under Safari's toolbar, and any overscroll. Those take
   the ROOT element's background, and with body transparent and html
   carrying none, the canvas fell back to white. That is the pale band
   at the top and bottom of the page on iPhone.

   So the root repeats the same stack. background-attachment: fixed
   anchors it to the viewport, which is what makes it line up with
   .soon-bg instead of stretching over the document and going white at
   the top again. .soon-bg keeps its own copy, so if a browser drops
   the fixed attachment the viewport still renders correctly and only
   the safe areas degrade.

   color-scheme is declared because this page is locked light. Without
   it a phone in dark mode can paint the canvas dark, which reads as
   the same bleed failure from the other direction. */
html.soon-root {
  background:
    radial-gradient(120% 90% at 80% 6%,  #cfe1fa 0%, rgba(207,225,250,0) 58%),
    radial-gradient(110% 85% at 10% 94%, #dbe9fb 0%, rgba(219,233,251,0) 60%),
    linear-gradient(168deg, #ffffff 0%, #dbe8fa 48%, #f4f8fe 100%);
  background-attachment: fixed;
  color-scheme: light;
}
.page-soon .hero { background: transparent; }

/* Lifts the centre so the display type and the facts keep their
   contrast wherever the rays happen to drift. */
.soon-bg__scrim {
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: radial-gradient(75% 55% at 50% 38%,
    rgba(255, 255, 255, 0.82) 0%,
    rgba(255, 255, 255, 0.45) 45%,
    rgba(255, 255, 255, 0) 78%);
}


/* ---------- Coming soon: background and capabilities ---------- */
.soon-facts {
  max-width: 760px;
  margin-inline: auto;
  padding: 0 var(--gutter) clamp(72px, 12vw, 130px);
}
.soon-facts__list { list-style: none; margin: 0; padding: 0; }
.soon-facts__list li {
  display: grid;
  grid-template-columns: 148px 1fr;
  gap: 20px;
  padding: 20px 0;
  border-top: 1px solid rgba(29, 29, 31, 0.1);
}
.soon-facts__k {
  font-size: 14px; line-height: 24px; font-weight: 500;
  letter-spacing: -0.016em;
  color: var(--ink-3);
}
.soon-facts__v {
  font-size: 17px; line-height: 26px; letter-spacing: -0.022em;
  color: var(--ink);
}
@media (max-width: 734px) {
  .soon-facts__list li { grid-template-columns: 1fr; gap: 4px; }
}

/* The holding page scrolls, so the hero should not eat a whole
   screen before the facts below it are reachable. */
.hero--soon {
  /* Deliberately short of a full screen. The first couple of facts show
     below the statement, which is what tells people there is more. */
  min-height: 78dvh;
  /* Symmetric, or justify-content centre lands the block off centre. */
  padding-block: clamp(72px, 9vh, 104px);
}

/* Pull the list up a little so a row is clearly cut by the fold rather
   than sitting just past it. */
.page-soon .soon-facts { padding-top: clamp(8px, 2vh, 24px); }
.footer--soon {
  background: transparent;
  box-shadow: inset 0 1px 0 rgba(29, 29, 31, 0.08);
  /* Clears the home indicator and Safari's toolbar. */
  padding-bottom: calc(44px + env(safe-area-inset-bottom, 0px));
}


/* ---------- Coming soon: centred wordmark ---------- */
.soon-bar {
  position: fixed;
  /* Clears the notch. Flat 12px put the wordmark under the clock once
     the page started bleeding into the status bar. */
  top: calc(var(--nav-top) + env(safe-area-inset-top, 0px));
  right: 0; bottom: auto; left: 0;
  z-index: 5;
  display: flex;
  justify-content: center;
  pointer-events: none;
}
.soon-bar__mark {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.022em;
  color: var(--hero-ink);
  line-height: 40px;
}


/* ============================================================
   14. COMING SOON  ·  progressive blur, build-in, white Resume
   ============================================================ */

/* ---------- Progressive blur beneath the wordmark ----------

   One blurred strip with a soft mask gives a hard seam where the mask
   ends, because the blur is uniform right up to the cut. Stacking
   layers fixes that: each one blurs twice as hard as the last and is
   masked to a band slightly lower than the last, so the strength ramps
   instead of stopping. The bands overlap on purpose, which is what
   removes the banding between them.

   The masks run upward, so the strongest layer covers the top edge and
   the ramp tapers to nothing at the bottom. Running them the other way
   leaves a blurred band hanging over sharp content instead of melting
   into it.

   Sits under the bar (z-index 4 against the bar's 5) so the wordmark
   stays sharp while everything scrolling beneath it dissolves. */
.soon-blur {
  position: fixed;
  inset: 0 0 auto 0;
  /* Grows by the inset so the ramp still starts at the physical top
     edge rather than below the status bar. */
  height: calc(132px + env(safe-area-inset-top, 0px));
  z-index: 4;
  pointer-events: none;
  /* Hidden over the top of the page, where there is nothing underneath
     it to blur and the strip would just look like haze. */
  opacity: 0;
  transition: opacity 0.5s var(--ease-soft);
}
.soon-blur.is-on { opacity: 1; }

.soon-blur__layer {
  position: absolute;
  inset: 0;
}
.soon-blur__layer:nth-child(1) {
  -webkit-backdrop-filter: blur(1px);  backdrop-filter: blur(1px);
  -webkit-mask-image: linear-gradient(to top, #000 0%, #000 12%, transparent 30%);
          mask-image: linear-gradient(to top, #000 0%, #000 12%, transparent 30%);
}
.soon-blur__layer:nth-child(2) {
  -webkit-backdrop-filter: blur(2px);  backdrop-filter: blur(2px);
  -webkit-mask-image: linear-gradient(to top, transparent 6%, #000 24%, #000 36%, transparent 52%);
          mask-image: linear-gradient(to top, transparent 6%, #000 24%, #000 36%, transparent 52%);
}
.soon-blur__layer:nth-child(3) {
  -webkit-backdrop-filter: blur(4px);  backdrop-filter: blur(4px);
  -webkit-mask-image: linear-gradient(to top, transparent 24%, #000 42%, #000 56%, transparent 72%);
          mask-image: linear-gradient(to top, transparent 24%, #000 42%, #000 56%, transparent 72%);
}
.soon-blur__layer:nth-child(4) {
  -webkit-backdrop-filter: blur(8px);  backdrop-filter: blur(8px);
  -webkit-mask-image: linear-gradient(to top, transparent 42%, #000 62%, #000 76%, transparent 90%);
          mask-image: linear-gradient(to top, transparent 42%, #000 62%, #000 76%, transparent 90%);
}
.soon-blur__layer:nth-child(5) {
  -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
  -webkit-mask-image: linear-gradient(to top, transparent 62%, #000 82%, #000 100%);
          mask-image: linear-gradient(to top, transparent 62%, #000 82%, #000 100%);
}

/* Firefox has no backdrop-filter on this path. Rather than ship a bare
   rectangle, fall back to the tint alone, which reads as the same soft
   top edge without the blur. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .soon-blur__layer { background: transparent; }
  .soon-blur {
    background: linear-gradient(to bottom,
      rgba(255, 255, 255, 0.92) 0%,
      rgba(255, 255, 255, 0.55) 55%,
      rgba(255, 255, 255, 0) 100%);
  }
}

/* ---------- Build in on scroll ----------

   Apple's own values, read off apple.com/mac: the start state is
   opacity 0 with translateY(30px), it is staggered across siblings, and
   it falls back to fully visible when scripting is off. The timing is
   mine; theirs is tweened in JavaScript, so there is no duration in the
   stylesheet to copy.

   Gated on .js for the same reason Apple gates on html.no-js. Without
   it the start state would be the resting state and the facts would
   never appear at all. */
.js .soon-facts__list li {
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.8s var(--ease), transform 0.8s var(--ease);
}
.js .soon-facts__list li.is-in {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .js .soon-facts__list li { opacity: 1; transform: none; transition: none; }
  .soon-blur { transition: none; }
}

/* ---------- White Resume button on the holding page ----------
   The pale gradient washes out the grey pill, so on this page only the
   secondary button goes white with a hairline to hold its edge. The
   portfolio keeps the grey. */
.page-soon .btn--secondary {
  background: #fff;
  color: var(--ink);
  border-color: rgba(29, 29, 31, 0.1);
  box-shadow: 0 1px 3px rgba(29, 29, 31, 0.06);
}
.page-soon .btn--secondary:hover { background: #fff; border-color: rgba(29, 29, 31, 0.22); }

/* Zero-size marker at the top of the document. Absolute rather than
   fixed, so it scrolls away and the observer fires. */
.soon-top {
  position: absolute;
  top: 0; left: 0;
  width: 1px; height: 1px;
  pointer-events: none;
}
