

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

h1, h2, h3, p, figure { margin: 0; }

/* ─── film grain ─────────────────────────────────────────────────────── */
.grain {
  position: fixed;
  inset: -60px;
  z-index: 90;
  pointer-events: none;
  opacity: .05;
  mix-blend-mode: overlay;
  background-size: 200px 200px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)' opacity='.9'/%3E%3C/svg%3E");
  animation: grain-boil 1.1s steps(1, end) infinite;
  will-change: transform;
}
@keyframes grain-boil {
  0%   { transform: translate3d(0, 0, 0); }
  20%  { transform: translate3d(-7px, 4px, 0); }
  40%  { transform: translate3d(5px, -9px, 0); }
  60%  { transform: translate3d(-11px, -4px, 0); }
  80%  { transform: translate3d(9px, 7px, 0); }
  100% { transform: translate3d(0, 0, 0); }
}

/* ─── custom cursor ──────────────────────────────────────────────────── */
.cursor { position: fixed; inset: 0; z-index: 120; pointer-events: none; }
.cursor[hidden] { display: none; }

.cursor__dot, .cursor__ring-hold {
  position: absolute; top: 0; left: 0;
  will-change: transform;
}
.cursor__dot, .cursor__ring {
  opacity: 0;
  transition: opacity .3s ease;
}
.cursor.is-on .cursor__dot { opacity: 1; }

.cursor__dot {
  width: 5px; height: 5px;
  margin: -2.5px 0 0 -2.5px;
  border-radius: 50%;
  background: #dff0ff;
  box-shadow: 0 0 6px rgba(120, 200, 255, .9), 0 0 16px rgba(59, 134, 255, .45);
}

/* The ring is a lens: it is looked THROUGH, so the blur stays under a pixel and
   the highlights do the work of the outline the border used to draw. The holder
   carries position (JS), this carries scale and shape (CSS), so the morph never
   fights the follow. */
.cursor__ring {
  position: absolute; top: 0; left: 0;
  width: 44px; height: 44px;
  margin: -22px 0 0 -22px;
  border-radius: 50%;
  background:
    radial-gradient(circle at 33% 28%, rgba(206, 232, 255, .16), rgba(206, 232, 255, .03) 46%, transparent 70%),
    radial-gradient(circle at 70% 78%, rgba(160, 210, 255, .06), transparent 55%);
  backdrop-filter: blur(.75px) saturate(1.2) brightness(1.06) contrast(1.03);
  -webkit-backdrop-filter: blur(.75px) saturate(1.2) brightness(1.06) contrast(1.03);
  box-shadow:
    inset 0 0 0 1px rgba(150, 200, 255, .42),
    inset 0 1px 3px rgba(210, 236, 255, .34),
    inset 0 -3px 8px rgba(0, 0, 0, .2),
    0 3px 14px rgba(0, 0, 0, .3);
  transform: scale(.78);
  transition: opacity .3s ease,
              transform .4s cubic-bezier(.34, 1.45, .5, 1),
              box-shadow .4s ease,
              border-radius .7s cubic-bezier(.34, 1.3, .45, 1);
}
.cursor.is-on .cursor__ring { opacity: 1; transform: scale(1); }
.cursor.is-on.is-hot .cursor__ring {
  transform: scale(1.24);
  box-shadow:
    inset 0 0 0 1px rgba(160, 220, 255, .72),
    inset 0 1px 4px rgba(215, 240, 255, .46),
    inset 0 -4px 10px rgba(0, 0, 0, .22),
    0 4px 18px rgba(0, 0, 0, .34);
}
/* Held down, the lens stops being a circle and starts being a drop. */
.cursor.is-on.is-press .cursor__ring { animation: cursorBlob 2.1s ease-in-out infinite; }
@keyframes cursorBlob {
  0%, 100% { border-radius: 34% 66% 62% 38% / 60% 36% 64% 40%; transform: scale(1.14) rotate(0deg); }
  33%      { border-radius: 64% 36% 40% 60% / 38% 62% 38% 62%; transform: scale(1.08) rotate(8deg); }
  66%      { border-radius: 42% 58% 66% 34% / 64% 40% 60% 36%; transform: scale(1.16) rotate(-6deg); }
}
html.has-cursor,
html.has-cursor * { cursor: none; }

/* ─── sections & scenes ──────────────────────────────────────────────── */
.section {
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  overflow: hidden;
  isolation: isolate;
  touch-action: pan-y pinch-zoom;
}

/* ─── the pin ─────────────────────────────────────────────────────────────
   Everything a section draws lives inside this. In the full-bleed layout it is
   sticky and one screen tall, so the composition holds still while the section
   scrolls past it: which is what gives the morph a runway to be scrubbed
   along. In portrait it is `display: contents` and the section's own stacked
   layout is exactly what it always was. */
/* Absolute rather than `height: 100%`: the section is sized by `min-height`,
   which is not a definite height, so a percentage would resolve to `auto` and
   the pin would collapse to nothing: taking the absolutely-positioned scene
   and copy down with it. */
.section__pin {
  position: absolute;
  inset: 0;
}

.section__inner {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
}
.section__inner > * { pointer-events: auto; }

/* ═══ HERO ═══════════════════════════════════════════════════════════ */
.hero__title {
  font-family: var(--font-display);
  font-weight: 200;
  /* --h1-fit is a corrective scale JS applies only when the artwork leaves too
     little room between the nav and the top of the V. */
  font-size: calc(clamp(1.28rem, min(3.25vw, 5.05vh), 3.2rem) * var(--h1-fit, 1));
  line-height: 1.14;
  letter-spacing: -.012em;
  color: #f2f8ff;
  text-wrap: balance;
}
/* padding + matching negative margin gives the reveal mask room for the glow
   without changing where the line actually sits */
.hero__title .line {
  display: block;
  overflow: hidden;
  padding: .14em 0 .1em;
  margin: -.14em 0 -.06em;
}
.hero__title .line__i { display: block; }
.hero__title .line:last-child .line__i {
  background: linear-gradient(98deg, #ffffff 0%, #e4f1ff 48%, #ffffff 96%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* ─── CTA ────────────────────────────────────────────────────────────── */
.cta {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 14px;
  margin-top: 2.2em;
  padding: 15px 26px;
  font-size: 11px;
  font-weight: 400;
  letter-spacing: .26em;
  text-transform: uppercase;
  color: var(--ice);
  border: 1px solid var(--hair);
  border-radius: 2px;
  background:
    linear-gradient(180deg, rgba(28, 68, 150, .16), rgba(10, 24, 56, .10));
  overflow: hidden;
  cursor: pointer;
  transition: border-color .5s var(--ease-out), color .5s var(--ease-out),
              box-shadow .6s var(--ease-out), background-color .5s var(--ease-out);
}
.cta::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 180% at 50% 130%, rgba(70, 160, 255, .40), transparent 62%);
  opacity: 0;
  transition: opacity .6s var(--ease-out);
}
.cta__t { position: relative; }
.cta__arrow {
  position: relative;
  width: 22px; height: 1px;
  background: currentColor;
  opacity: .55;
  transition: width .55s var(--ease-out), opacity .55s var(--ease-out);
}
.cta__arrow::after {
  content: '';
  position: absolute;
  right: 0; top: -2.5px;
  width: 6px; height: 6px;
  border-top: 1px solid currentColor;
  border-right: 1px solid currentColor;
  transform: rotate(45deg);
}
.cta:hover, .cta:focus-visible {
  border-color: rgba(120, 195, 255, .55);
  box-shadow: 0 0 0 1px rgba(80, 165, 255, .10), 0 14px 46px -18px rgba(50, 140, 255, .70);
}
.cta:hover::before, .cta:focus-visible::before { opacity: 1; }
.cta:hover .cta__arrow, .cta:focus-visible .cta__arrow { width: 30px; opacity: .95; }
.cta[disabled] { opacity: .45; pointer-events: none; }

/* ─── footer ─────────────────────────────────────────────────────────── */
/* The footer is styled once, in styles/hero.css, and its `border-top` moved
   there with the rest. It used to be declared in both sheets: hero.css loads
   later and won every shared property, so this file's padding, its background
   (a blue horizon glow that therefore never painted) and both type rules
   were dead. One declaration was not shared and so stayed live: a
   `max-width: 1440px` on `.footer__inner` that hero.css had no reason to
   reset. Against `--content: 1600px` it pulled the last screen of the page
   out of line with every section above it, measured at 1920, the footer's
   brand started at x=240 and the section content at x=160. */

/* ─── reveal choreography ────────────────────────────────────────────── */
@media (scripting: enabled) {
  /* Copy waiting to be revealed is invisible, but it was still a target: the
     panel's slots and both call-to-action buttons could be clicked, and
     tabbed to: while they were still blurred out at opacity 0, so a press
     meant for the artwork opened a file picker from nothing. A reveal fires
     once and never reverses, so this simply holds until the copy is there. */
  html.motion-ready .reveal {
    opacity: 0;
    transform: translateY(16px);
    filter: blur(7px);
    pointer-events: none;
    transition:
      opacity   .95s var(--ease-out) calc(var(--i, 0) * 85ms),
      transform 1.15s var(--ease-out) calc(var(--i, 0) * 85ms),
      filter    .95s var(--ease-out) calc(var(--i, 0) * 85ms);
  }
  html.motion-ready .hero__title .line__i {
    transform: translateY(105%);
    transition: transform 1.25s var(--ease-out) calc(var(--i, 0) * 110ms + 120ms);
  }
  html.motion-ready .is-revealed .hero__title .line__i,
  html.motion-ready .hero__title.is-revealed .line__i { transform: none; }
  html.motion-ready .reveal.is-revealed { opacity: 1; transform: none; filter: none; pointer-events: auto; }
}

/* ═══════════════ PORTRAIT / NARROW: artwork becomes a band ═══════════ */
@media (max-width: 900px), (max-height: 639px), (max-aspect-ratio: 1/1) {
  :root { --gutter: clamp(18px, 6vw, 46px); }

  /* The stage is a full-bleed idea: in portrait the scenes are in-flow plates
     stacked with the copy, so the pin dissolves and each section paints its
     own canvas again, exactly as before. */
  .section__pin { display: contents; }

  .section {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: clamp(22px, 4.4vh, 46px);
    padding: calc(env(safe-area-inset-top) + 82px) var(--gutter) clamp(54px, 9vh, 96px);
    height: auto;
  }

  .section__inner { position: relative; inset: auto; order: 1; }

  /* hero */
  .hero__title { font-size: clamp(1.32rem, 6.6vw, 2.5rem); }
  .section--hero { justify-content: center; }

}

/* very short landscape: keep the hero copy from crowding the crown */
@media (min-aspect-ratio: 2/1) {
  .hero__title { font-size: calc(clamp(1.05rem, 4.1vh, 2rem) * var(--h1-fit, 1)); }
}

/* ═══════════════════ REDUCED MOTION ═════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .18s !important;
    scroll-behavior: auto !important;
  }

  .grain { animation: none; }
  @keyframes rm-fade { from { opacity: 0; } to { opacity: 1; } }

  .cursor { display: none !important; }
  html.has-cursor, html.has-cursor * { cursor: auto; }

  @media (scripting: enabled) {
    html.motion-ready .reveal { transform: none; filter: none; transition: opacity .3s linear; }
    html.motion-ready .hero__title .line__i { transform: none; transition: none; }
  }
}

/* ═══════════════════ HIGH CONTRAST / FORCED COLORS ══════════════════ */
@media (forced-colors: active) {
  .grain,
  .cursor { display: none; }
  /* The same pair the reduced-motion block above carries, and for the same
     reason: hiding the drawn cursor is only half an instruction. Without this
     line `has-cursor` keeps `cursor: none` on every element while the thing
     that replaced the pointer is `display: none`, and the reader is left with
     no pointer at all. `initCursor` refuses to build one under forced colours
     now, so this is the case where the theme is switched on mid-visit. */
  html.has-cursor, html.has-cursor * { cursor: auto; }
  .cta { border: 1px solid CanvasText; }

  /* Every section paints its ground as a gradient, which is a
     `background-image`: and forced colours replaces `color` and
     `background-color` but leaves background images exactly where they are.
     Under a LIGHT contrast theme (High Contrast White, Desert, Aquatic) the
     text is forced to a dark CanvasText while these grounds stay near-black,
     so the whole editorial half of the page, and the page's one outline
     button, were dark on dark. The grounds come off; Canvas shows through. */
  .capabilities, .studio, .contact,
  .build, .lattice-stage::before,
  .btn-outline { background-image: none; }
  .btn-outline { border: 1px solid ButtonText; }
}

/* ─── print ──────────────────────────────────────────────────────────────
   The page had no print rules at all, and three things went wrong at once:
   the travel boxes are measured in `svh`, which in paged media resolves
   against the page area and emitted some two thousand near-blank sheets;
   backgrounds are off by default, so `--text-1` printed near-white on white;
   and every display heading is `background-clip: text` with a transparent
   fill, so the headings printed as nothing whatsoever. What is wanted from a
   print is the document: the words, in order, in ink. */
@media print {
  /* First, because a running transition outranks even an `!important` author
     declaration: it sits above them in the cascade. Without this the reveal
     rules below were correct and still lost: switching to print media started
     an opacity transition from 0, and the sheet was rendered part-way through
     it. Measured: the contact address printed at opacity 0. */
  *, *::before, *::after {
    transition: none !important;
    animation: none !important;
  }
  html, body { background: #fff; color: #000; }
  .wave-canvas, .swarm-vignette, .grain, .cursor, .hero-mark, .closing-wave,
  .site-header, .mobile-menu, .skip-link, .chapter-scroll,
  .lattice-object, .lattice-caption { display: none !important; }

  /* Nothing is pinned, and nothing is a screen tall. */
  .section, .lattice-travel, .lattice-stage {
    height: auto !important;
    min-height: 0 !important;
    overflow: visible !important;
  }
  .section__pin, .lattice-stage {
    position: static !important;
    height: auto !important;
  }
  .section__inner, .chapter-signals { position: static !important; }

  /* Lit metal does not print. The headings become ink. */
  .display, .display--bright, .display--metal, .display--cyan,
  .studio-heading, .hero__title .line:last-child .line__i {
    background: none !important;
    -webkit-text-fill-color: #000 !important;
    color: #000 !important;
  }
  /* The reveal choreography holds copy at zero opacity until it is scrolled
     to, and `(scripting: enabled)` is true while printing, so everything the
     reader had not reached printed blank. */
  .reveal, .work-cta > * {
    opacity: 1 !important;
    filter: none !important;
    transform: none !important;
  }
  a { color: #000; text-decoration: underline; }
}
