/* ==========================================================================
   Zeelio marketing — header + "looking up" hero
   The camera points at the sky from the edge of a training ground: clouds
   behind, stickers drifting in the air, the ground furniture close to the
   lens and out of focus. Fraunces is held back for the arrival headline.
   Scoped to the redesigned surfaces only; legacy sections keep css/main.css.
   ========================================================================== */

:root {
  --topbar-h: 62px;
}

html {
  /* Clear the fixed bar, plus a little air. */
  scroll-padding-top: calc(var(--topbar-h) + var(--z-space-4));
}

/* ----------------------------------------------------------------- Topbar */

/* On large screens the header is transparent over the sky hero. */
.topbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--z-space-4);
  height: var(--topbar-h);
  padding: 0 clamp(var(--z-space-4), 3vw, var(--z-space-10));
  background-color: transparent;
  background-image: none;
  transition:
    background-color var(--z-settle) var(--z-ease),
    box-shadow var(--z-settle) var(--z-ease);
}

/* Off the hero the sky is gone and the bar gains structure over scrolled content. */
.topbar.header-scrolled {
  padding: 0 clamp(var(--z-space-4), 3vw, var(--z-space-10));
  background-image: none;
  background-color: rgba(250, 247, 242, 0.86);
  backdrop-filter: blur(14px) saturate(1.2);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  box-shadow: 0 1px 0 rgba(30, 24, 20, 0.07), 0 6px 18px rgba(20, 34, 54, 0.06);
}

@media (max-width: 640px) {
  /* Solid canvas on phone keeps the header and browser chrome continuous. */
  .topbar {
    background-color: var(--z-canvas);
  }

  .topbar.header-scrolled {
    background-color: var(--z-canvas);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}

.topbar__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--z-space-2);
  text-decoration: none;
  color: var(--z-ink-strong);
}

.topbar__badge {
  display: block;
  width: 30px;
  height: 30px;
}

/* The wordmark is Fraunces, at the proportions the Logo component in webv2
   uses for its `lockup` variant (badge 32 / word 21 at sm). Live text, not an
   image — same as the component, and it stays crisp and selectable. */
.topbar__word {
  font-family: var(--z-font-display);
  font-size: 1.3125rem;
  font-weight: 400;
  letter-spacing: -0.005em;
  line-height: 1;
}

/* Deliberately not a filled pill. There is one loud action on this page and it
   is the gold button in the hero; the way back into the app is a door, not a
   pitch, so it stays an outline. */
.topbar__login {
  display: inline-flex;
  align-items: center;
  height: var(--z-control-standard);
  padding: 0 var(--z-space-5);
  border: 1px solid var(--z-shell);
  border-radius: var(--z-radius-pill);
  background-color: var(--z-shell);
  font-family: var(--z-font-body);
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1;
  color: var(--z-shell-ink);
  text-decoration: none;
  white-space: nowrap;
  transition:
    background-color var(--z-quick) var(--z-ease),
    border-color var(--z-quick) var(--z-ease),
    color var(--z-quick) var(--z-ease),
    transform var(--z-quick) var(--z-ease);
}

.topbar__login:hover {
  background-color: var(--z-ink-strong);
  border-color: var(--z-ink-strong);
  color: #ffffff;
}

.topbar__login:active {
  transform: translateY(1px);
}

.topbar__login:focus-visible {
  outline: 2px solid var(--z-primary);
  outline-offset: 3px;
}

/* ---------------------------------------------------------------- Buttons */

.z-btn {
  --z-btn-height: var(--z-control-touch);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--z-space-2);
  height: var(--z-btn-height);
  padding: 0 var(--z-space-5);
  border: 1px solid transparent;
  border-radius: var(--z-radius-pill);
  font-family: var(--z-font-body);
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition:
    background-color var(--z-quick) var(--z-ease),
    border-color var(--z-quick) var(--z-ease),
    color var(--z-quick) var(--z-ease),
    box-shadow var(--z-quick) var(--z-ease),
    transform var(--z-quick) var(--z-ease);
}

.z-btn:focus-visible {
  outline: 2px solid var(--z-primary);
  outline-offset: 3px;
}

.z-btn--primary {
  background-color: var(--z-primary);
  color: var(--z-primary-foreground);
  box-shadow: var(--z-elev-1);
}

.z-btn--primary:hover {
  background-color: var(--z-primary-hover);
  box-shadow: var(--z-elev-2);
}

.z-btn--primary:active {
  transform: translateY(1px);
  box-shadow: var(--z-elev-1);
}

/* -------------------------------------------------------------- Sky hero */

/* The stickers stay sharp; prepared clouds and masts carry their softness
   in the artwork rather than runtime filters. */
.sky {
  /* The racks are the frame the headline sits inside, so the mast's own width
     is the unit the composition is measured in: its height, how far below the
     fold it is planted, and how far up from the bottom the copy sits all come
     off this one number. Change it and the framing holds. */
  --mast-w: clamp(150px, 21vw, 330px);
  position: relative;
  isolation: isolate;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  /* Crop side entrances while allowing the bottom frame over the cream seam. */
  overflow: visible;
  clip-path: inset(0 0 -140px);
  z-index: 2;
  background-color: #6499cf;
}

.sky__backdrop {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.sky__gradient {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(58% 46% at 8% 2%, rgba(253, 240, 214, 0.55), rgba(253, 240, 214, 0) 68%),
    radial-gradient(50% 40% at 94% 18%, rgba(255, 246, 228, 0.34), rgba(255, 246, 228, 0) 72%),
    linear-gradient(180deg,
      #3f78bd 0%,
      #5b93cb 18%,
      #82b1db 40%,
      #aac8e2 60%,
      #cfdce0 78%,
      #e9e7db 91%,
      var(--z-canvas) 100%);
}

.sky__clouds {
  position: absolute;
  inset: 0;
  background: url('../assets/images/clouds.webp') center / cover no-repeat;
}

/* A brighter patch of sky behind the copy — legibility that reads as weather. */
.sky__glow {
  position: absolute;
  inset: 0;
  background: radial-gradient(42% 34% at 50% 54%,
      rgba(255, 253, 246, 0.72),
      rgba(255, 253, 246, 0.28) 52%,
      rgba(255, 253, 246, 0) 78%);
}

/* ------------------------------------------------------------- Foreground */

.sky__foreground {
  position: absolute;
  inset: 0;
  z-index: 3;
  overflow: hidden;
  pointer-events: none;
}

/* Softness and lamp bloom are baked into the shared mast artwork. */
.upshot {
  position: absolute;
  bottom: calc(var(--mast-w) * -0.18);
  width: var(--mast-w);
  aspect-ratio: 360 / 640;
  opacity: 0.86;
}

.upshot img {
  position: absolute;
  left: -5.5556%;
  width: 133.3333%;
  max-width: none;
  height: 100%;
}

.upshot--left { left: -3%; }
.upshot--right { right: -3%; transform: scaleX(-1); }

/* Lights only look bright against something darker, so the sky gives a little
   as they come up: the top and the corners deepen while the middle — where the
   copy is — is left alone and then lifted by the copy's own warm backlight. It
   sits low in the stack so it deepens the sky and the clouds and nothing else;
   the racks above it are what gain by it. */
.sky__dusk {
  position: absolute;
  inset: 0;
  z-index: 1;
  opacity: 0;
  background: radial-gradient(112% 82% at 50% 60%,
      rgba(255, 255, 255, 0) 30%,
      rgba(84, 112, 154, 0.34) 100%);
  mix-blend-mode: multiply;
  pointer-events: none;
}

.sky__dusk { opacity: 1; }

/* What the racks throw into the rest of the frame. Screen rather than a plain
   overlay, so it adds light instead of laying a film over the sky — and it
   sits above the masts while leaving the live copy clear. */
.sky__flare {
  position: absolute;
  inset: 0;
  z-index: 4;
  opacity: 0;
  background:
    radial-gradient(38% 34% at 11% 61%, rgba(255, 220, 158, 0.62), rgba(255, 220, 158, 0) 70%),
    radial-gradient(38% 34% at 89% 61%, rgba(255, 220, 158, 0.62), rgba(255, 220, 158, 0) 70%),
    radial-gradient(86% 40% at 50% 104%, rgba(255, 228, 176, 0.5), rgba(255, 228, 176, 0) 74%);
  mix-blend-mode: screen;
  pointer-events: none;
}

.sky__flare { opacity: 1; }

/* The haze that hands the hero over to the cream page, laid over the
   foreground so the ground furniture dissolves into it. */
.sky__horizon {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 4;
  height: 19%;
  background: linear-gradient(180deg,
      rgba(246, 242, 235, 0) 0%,
      rgba(246, 242, 235, 0.2) 48%,
      rgba(246, 242, 235, 0.72) 82%,
      var(--z-canvas) 100%);
  pointer-events: none;
}

/* -------------------------------------------------- The copy, once it is lit */

/* Nothing in the copy reacts to the lamps. A glow only reads as light when the
   thing glowing is brighter than what surrounds it, and this is near-black ink
   on a pale sky — a warm shadow behind the glyphs came out as a smudge clinging
   to every letter, and a pool behind the block came out as a stain on the air.
   The button is the one thing here that is bright enough to catch light, so it
   is the only thing that does. */

.sky__copy {
  position: relative;
}

/* The halo behind the button rides the wrapper, not the button, so it is free
   to spill past the pill without fighting the sheen's clip. */
.sky__actions {
  position: relative;
}

.sky__actions::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 22rem;
  max-width: 128%;
  aspect-ratio: 2.4;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(closest-side,
      rgba(255, 201, 116, 0.66),
      rgba(255, 201, 116, 0) 72%);
  opacity: 0;
  pointer-events: none;
}

.sky__actions::before {
  opacity: 1;
}

/* ---------------------------------------------------------- Sticker field */

/* The field shares a grid with the copy, so text wrapping naturally changes
   the available sky. No DOM measurement or image-readiness gate is needed. */
.sky__field {
  --sticker-unit: clamp(56px, 7vw, 100px);
  position: relative;
  min-height: 180px;
  width: 100%;
  z-index: 2;
  pointer-events: none;
}

/* A fixed bouquet: depth comes from scale and atmospheric opacity. */
.sticker {
  --size: calc(var(--sticker-unit) * var(--depth));
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--size);
  margin: calc(var(--size) / -2) 0 0 calc(var(--size) / -2);
  opacity: var(--haze);
  animation: sticker-float var(--duration) ease-in-out infinite;
}
.sticker img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  transform: rotate(var(--rest));
}
@keyframes sticker-float {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(1px, -3px); }
}
@media (max-width: 640px), (hover: none) and (pointer: coarse) {
  .sticker { left: var(--mx); top: var(--my); }
}
.sky.is-armed:not(.is-flowing) .sticker,
.sky.is-armed:not(.is-flowing) .sticker img,
.sky.is-paused .sticker,
.sky.is-paused .sticker img { animation-play-state: paused; }
.sky.is-armed:not(.is-flowing) .sticker img { visibility: hidden; }
.sky.is-armed:not(.is-flowing) .sticker.is-launched img { visibility: visible; }

/* ------------------------------------------------------------------ Copy */

/* Bottom-anchored, not centred. The masts hang off the bottom edge too, so
   pinning the copy to the same edge is what makes the framing survive a change
   of window height: the headline and the two racks move together. */
.sky__inner {
  position: relative;
  z-index: 5;
  display: grid;
  grid-template-rows: minmax(180px, 1fr) auto;
  gap: var(--z-space-6);
  width: 100%;
  min-width: 0;
  margin: 0 auto;
  padding: calc(var(--topbar-h) + var(--z-space-10)) var(--z-space-6)
    calc(var(--mast-w) * 0.42);
}

.sky__copy {
  position: relative;
  width: 100%;
  max-width: 46rem;
  margin: 0 auto;
  z-index: 1;
  text-align: center;
  font-family: var(--z-font-body);
  color: var(--z-ink);
}

/* The hero lockup. The bar carries the badge, so this one takes the bare glyph
   over the wordmark — the same two pieces the Logo component composes, stacked
   rather than set in a row so the identity reads as a mark and the headline
   below it keeps the full width of the frame. Fraunces, because that is what
   the brand wordmark is; the size gap between it and the headline is what stops
   the two serif lines reading as one sentence. */
.sky__logo {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--z-space-3);
  margin: 0 0 var(--z-space-5);
}

.sky__mark {
  display: block;
  width: clamp(52px, 5.4vw, 78px);
  height: auto;
  aspect-ratio: 320 / 243;
}

.sky__word {
  font-family: var(--z-font-display);
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  font-weight: 400;
  letter-spacing: -0.005em;
  line-height: 1;
  color: var(--z-ink-strong);
}

/* Launch stage goes above the headline, the way it does on every other
   software hero: it says what this is before the line says what it does.
   Not a link, so it never competes with the one CTA below. */
.sky__badge {
  display: inline-flex;
  align-items: center;
  gap: var(--z-space-2);
  height: 30px;
  margin: 0 0 var(--z-space-5);
  padding: 0 var(--z-space-4) 0 var(--z-space-3);
  background-color: rgba(255, 255, 255, 0.6);
  border: 1px solid rgba(255, 255, 255, 0.72);
  border-radius: var(--z-radius-pill);
  box-shadow: 0 2px 10px rgba(20, 34, 54, 0.1);
  backdrop-filter: blur(10px) saturate(1.15);
  -webkit-backdrop-filter: blur(10px) saturate(1.15);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.002em;
  line-height: 1;
  color: var(--z-ink-strong);
}

/* The live dot: a slow ring, not a blink. */
.sky__pulse {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background-color: var(--z-primary);
  animation: pilot-pulse 2.8s var(--z-ease) infinite;
}

@keyframes pilot-pulse {
  0% {
    box-shadow: 0 0 0 0 rgba(12, 110, 158, 0.42);
  }

  70%,
  100% {
    box-shadow: 0 0 0 7px rgba(12, 110, 158, 0);
  }
}

.sky__title {
  font-family: var(--z-font-display);
  font-weight: 600;
  font-size: clamp(2.25rem, 5.4vw, 4rem);
  line-height: 1.04;
  letter-spacing: -0.022em;
  color: var(--z-ink-strong);
  text-wrap: balance;
  margin: 0;
}

.sky__actions {
  display: flex;
  justify-content: center;
  margin-top: var(--z-space-8);
}

/* Alone on the page, so it can afford a little more presence — and the warm
   value is what separates it from a blue sky, which the action blue cannot do.
   Gold is an honor signal in the system and not an interaction colour, so this
   stays a hero one-off: no .z-btn--accent, nothing for the rest of the site to
   pick up. Espresso ink on accent is 7.3:1. */
.sky__cta {
  --z-btn-height: 52px;
  padding: 0 var(--z-space-8);
  font-size: 1.0625rem;
  background-color: var(--z-accent);
  color: var(--z-shell);
  box-shadow: 0 10px 24px rgba(74, 52, 12, 0.3);
}

.sky__cta:hover {
  /* One step down from accent; the system has no hover token for it. */
  background-color: #c08f2c;
  box-shadow: 0 14px 30px rgba(74, 52, 12, 0.36);
}

.sky__cta:active {
  transform: translateY(1px);
  box-shadow: 0 6px 14px rgba(74, 52, 12, 0.28);
}

.sky__cta:focus-visible {
  outline-color: var(--z-shell);
}

/* ------------------------------------------------- Looking down: the ground */

/* The hero's horizon dissolves into cream, so this section starts in the same
   cream and there is no seam to cross — the page just arrives on the ground.
   The photograph is the argument here, and it happens to be shot from directly
   above, which makes it the exact answer to a hero that spends its whole height
   looking up. */
.ground {
  position: relative;
  z-index: 1;
  background-color: var(--z-canvas);
  padding: clamp(var(--z-space-16), 9vw, 128px) var(--z-space-6);
  font-family: var(--z-font-body);
}

/* Photograph on one side, the whole argument on the other. The heading lives in
   this column rather than above the pair because a statement floating over a
   short paragraph and a tall picture leaves a hole in the middle of the page —
   beside the print it has something to stand next to. */
.ground__inner {
  display: grid;
  grid-template-columns: 1.06fr 1fr;
  align-items: center;
  gap: clamp(var(--z-space-10), 6vw, 92px);
  max-width: 1120px;
  margin: 0 auto;
}

/* A print, not a background. The hero's whole vocabulary is die-cut objects
   with a shadow under them, lying on a surface; a photograph gets to be one
   too, with a mount around it and a hair of rotation so it reads as placed
   rather than as inserted. It also stops doing the thing it was doing before,
   which was sitting at a third of its opacity underneath body copy — where the
   photo was unreadable and so, very nearly, was the text. */
.ground__print {
  position: relative;
  margin: 0;
  padding: clamp(10px, 1.1vw, 16px);
  background-color: var(--z-surface);
  border-radius: var(--z-radius-md);
  box-shadow: var(--z-elev-3);
  transform: rotate(-1.8deg);
}

.ground__photo {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: calc(var(--z-radius-md) - 8px);
}

/* Hanken, not Fraunces. The display face is an honor signal in this system and
   ADR-0003 puts section headings outside it by name — the hero's one line is
   where it gets spent. The emphasis is carried by value instead: the first
   clause sits back in muted ink and the second lands in full strength, so the
   sentence keeps its beat without spending a colour on it. */
.ground__title {
  margin: 0 0 var(--z-space-6);
  font-size: clamp(1.875rem, 3.4vw, 2.75rem);
  font-weight: 700;
  letter-spacing: -0.028em;
  line-height: 1.1;
  color: var(--z-muted);
  text-wrap: balance;
}

.ground__lands {
  color: var(--z-ink-strong);
}

.ground__text {
  display: flex;
  flex-direction: column;
  gap: var(--z-space-5);
  font-size: clamp(1.0625rem, 1.15vw, 1.1875rem);
  line-height: 1.65;
  color: var(--z-ink);
  text-wrap: pretty;
}

.ground__text p {
  margin: 0;
}

@media (max-width: 900px) {
  .ground__inner {
    grid-template-columns: 1fr;
    gap: clamp(var(--z-space-12), 9vw, 72px);
    max-width: 40rem;
  }

  /* Stacked, the print comes second: the sentence has to land before the
     picture illustrates it, or the picture is just a picture. */
  .ground__print {
    order: 2;
  }
}

@media (max-width: 640px) {
  .ground {
    padding-left: var(--z-space-5);
    padding-right: var(--z-space-5);
  }

  /* The tilt costs width the frame does not have here, and a shadow this size
     under a full-width print reads as a mistake rather than as an object. */
  .ground__print {
    transform: none;
    box-shadow: var(--z-elev-2);
  }
}

/* ----------------------------------------------- Looking up again, indoors */

/* The hero is a sky at midday; this is the same camera angle indoors with the
   lights on. It is deliberately almost nothing — a few beams across the top,
   grain in the air, six lamps — because a ceiling drawn literally, in
   perspective, with trusses and hoops and hanging gear, reads as a runway. The
   room is carried by darkness and light, and there is nothing in the
   foreground at all. */
.hall {
  --dof: 16px;
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  align-items: center;
  min-height: 92vh;
  padding: clamp(var(--z-space-16), 12vh, 152px) var(--z-space-6);
  background:
    radial-gradient(96% 74% at 50% 34%, #241b15 0%, #180f0b 52%, #0f0a08 100%),
    var(--z-shell);
  color: var(--z-shell-ink);
  font-family: var(--z-font-body);
}

.hall__scene {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

/* Air, not a surface. The same fractal noise the hero uses for cloud, turned
   down until it is only the grain you get pointing a camera into a dark room. */
.hall__grain {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0.14;
  background: url('../assets/images/grain.webp') center / cover no-repeat;
  mix-blend-mode: screen;
}

/* The structure. Four bands, thicker and further apart as they come toward you,
   fading out before they reach the copy — enough to say "there is a roof up
   there" and not one line more. Blurred hard, because it is nowhere near the
   focal plane and because soft is what makes it read as depth rather than as a
   set of stripes. */
.hall__beams {
  position: absolute;
  inset: -8% -6% auto;
  height: 66%;
  background: linear-gradient(179deg,
      transparent 0 4%,
      #0a0705 4% 8.5%,
      transparent 8.5% 17%,
      #0a0705 17% 23%,
      transparent 23% 34%,
      #0a0705 34% 41.5%,
      transparent 41.5% 55%,
      #0a0705 55% 64%,
      transparent 64% 100%);
  opacity: 1;
  filter: blur(calc(0.62 * var(--dof)));
  -webkit-mask-image: linear-gradient(180deg, #000 0%, rgba(0, 0, 0, 0.82) 44%, transparent 96%);
  mask-image: linear-gradient(180deg, #000 0%, rgba(0, 0, 0, 0.82) 44%, transparent 96%);
}

/* --------------------------------------------------------------- The lamps */

.hall__lamps {
  position: absolute;
  inset: 0;
}

/* Three static gradients carry the bloom, amber core and white centre. */
.hall__lamp {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: calc(var(--s) * 46vmin);
  aspect-ratio: 1.75;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(closest-side,
      rgba(255, 233, 194, 0.5),
      rgba(255, 210, 146, 0.15) 44%,
      rgba(255, 196, 120, 0) 74%);
  opacity: 1;
}

.hall__lamp::before,
.hall__lamp::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 24%;
  aspect-ratio: 2.4;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  filter: blur(calc(0.16 * var(--dof)));
}

.hall__lamp::before {
  background: radial-gradient(closest-side, #ffb257, rgba(214, 122, 40, 0) 78%);
}

.hall__lamp::after {
  background: radial-gradient(closest-side, rgba(255, 252, 244, 0.75), rgba(255, 231, 186, 0) 76%);
  opacity: 1;
}

/* What the lamps put into the air of the room, over everything. */
.hall__spill {
  position: absolute;
  inset: 0;
  z-index: 2;
  opacity: 1;
  background:
    radial-gradient(38% 44% at 16% 40%, rgba(255, 206, 140, 0.28), rgba(255, 206, 140, 0) 76%),
    radial-gradient(38% 44% at 84% 40%, rgba(255, 206, 140, 0.28), rgba(255, 206, 140, 0) 76%);
  mix-blend-mode: screen;
  pointer-events: none;
}

/* --------------------------------------------------------------- The copy */

/* Cream on espresso the whole way through. The room gets brighter but the copy
   never changes value: a lighting effect does not get to spend contrast on the
   words, and this way it is readable before the lights find it — which is most
   of the point of arriving in the dark. */
.hall__inner {
  position: relative;
  z-index: 3;
  width: 100%;
  max-width: 46rem;
  margin: 0 auto;
  text-align: center;
}

.hall__eyebrow {
  margin: 0 0 var(--z-space-5);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(245, 237, 225, 0.6);
}

.hall__title {
  margin: 0 0 var(--z-space-8);
  font-size: clamp(1.875rem, 4vw, 3rem);
  font-weight: 700;
  letter-spacing: -0.028em;
  line-height: 1.12;
  color: #fbf4e9;
  text-wrap: balance;
}

.hall__text {
  margin: 0 auto var(--z-space-5);
  max-width: 36rem;
  font-size: clamp(1.0625rem, 1.15vw, 1.1875rem);
  line-height: 1.65;
  color: rgba(245, 237, 225, 0.78);
  text-wrap: pretty;
}

.hall__text:last-child {
  margin-bottom: 0;
}

@media (max-width: 640px) {
  .hall {
    /* Blur is measured in screen pixels, so a smaller scene needs a smaller
       circle of confusion to keep the same amount of structure. */
    --dof: 10px;
    min-height: 88vh;
    padding-left: var(--z-space-5);
    padding-right: var(--z-space-5);
  }

  .hall__lamp {
    width: calc(var(--s) * 62vmin);
  }
}

/* ------------------------------------------------------------ Responsive */

/* Short laptop windows: keep the whole composition above the fold. */
@media (min-width: 641px) and (max-height: 780px) {
  /* A short window does not make the masts shorter, so it has to: otherwise the
     racks reach past the headline and there is no frame left, only a wall. */
  .sky {
    --mast-w: clamp(132px, 15.5vw, 236px);
  }

  .sky__field {
    --sticker-unit: clamp(52px, 6vw, 88px);
  }

  .sky__inner {
    padding-top: calc(var(--topbar-h) + var(--z-space-5));
    padding-bottom: calc(var(--mast-w) * 0.5);
  }

  .sky__logo {
    gap: var(--z-space-2);
    margin-bottom: var(--z-space-4);
  }

  .sky__mark {
    width: clamp(44px, 4.4vw, 60px);
  }

  .sky__badge {
    margin-bottom: var(--z-space-4);
  }

  .sky__actions {
    margin-top: var(--z-space-6);
  }
}

@media (max-width: 900px) {
  .sky__field {
    --sticker-unit: clamp(60px, 11.5vw, 104px);
  }

  /* A portrait tablet is a narrow frame, so the racks have to claim more of
     its width than a vw share alone would give them. */
  .sky {
    --mast-w: clamp(170px, 26vw, 300px);
  }

  /* The headline runs closer to the frame edge here, so the racks step out. */
  .upshot--left {
    left: -10%;
  }

  .upshot--right {
    right: -10%;
  }

  .sky__copy {
    max-width: 34rem;
  }
}

@media (max-width: 640px) {
  .sky {
    /* Big enough that the pole clears the top of the headline, which is what
       makes one post read as a floodlight rather than as a fence. It is most of
       the screen width, so the copy has to hold its own ground — see the veil
       under .sky__copy below. */
    --mast-w: clamp(200px, 68vw, 320px);
  }

  .sky__inner {
    /* Reserve room above floating browser controls as well as the safe area. */
    padding: calc(var(--topbar-h) + var(--z-space-6)) var(--z-space-5)
      calc(var(--mast-w) * 0.42 + var(--z-space-16) + env(safe-area-inset-bottom, 0px));
  }

  .sky__logo { display: none; }

  /* The phone composition keeps six legible stickers. */
  .sky__field {
    --sticker-unit: clamp(44px, 13vw, 72px);
  }

  .sky__glow {
    background: radial-gradient(72% 30% at 50% 52%,
        rgba(255, 253, 246, 0.78),
        rgba(255, 253, 246, 0.3) 54%,
        rgba(255, 253, 246, 0) 80%);
  }

  /* One post on a phone, and it leans into the frame from the left rather than
     standing up out of the bottom. Photographed from underneath and off to one
     side, a floodlight does lean — so the tilt is the more honest reading of
     "looking up", and it buys the composition the thing a phone has least of:
     the pole crosses a corner instead of a whole column, and the rack arrives
     high, above the headline, instead of behind it. */
  .upshot--right {
    display: none;
  }

  .sky__actions {
    margin-top: var(--z-space-6);
  }

  .sky__cta {
    --z-btn-height: 50px;
    width: 100%;
    max-width: 20rem;
  }
}

/* The bouquet fits the narrower air on touch screens.
   The copy anchors the phone mast directly, even with JS disabled. */
@media (max-width: 640px), (hover: none) and (pointer: coarse) {
  .sky__inner {
    grid-template-rows: minmax(180px, 1fr) auto;
    padding-top: calc(var(--topbar-h) + var(--z-space-6));
  }

  .sky__field { --sticker-unit: clamp(60px, 16vw, 90px); }

  .sky__foreground { display: none; }
  .sky__copy::before,
  .sky__copy::after {
    content: '';
    position: absolute;
    z-index: -1;
    width: calc(var(--mast-w) * 1.3333);
    aspect-ratio: 480 / 640;
    left: calc(-50vw + 50% - var(--mast-w) * 0.28);
    bottom: calc(100% - var(--mast-w) * 0.54);
    background: url('../assets/images/floodlight.webp') center / contain no-repeat;
    transform: rotate(22deg);
    transform-origin: 0 100%;
    opacity: 0.6;
    mask-image: linear-gradient(#000 40%, transparent 90%);
    pointer-events: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .sticker, .sticker img { animation: none; }
  .sticker { transform: none; }
  .sky__pulse { animation: none; }
  .sky__actions::before { transition: none; }
  .z-btn, .z-btn:active { transform: none; }
}

/* A one-time launch hands each sticker back to its CSS float. Prepared cold
   and lit artwork crossfade; only opacity changes across the large washes. */
.sky.is-armed:not(.is-lit) .upshot__lit,
.sky.is-armed:not(.is-lit) .sky__copy::after,
.sky.is-armed:not(.is-lit) .sky__dusk,
.sky.is-armed:not(.is-lit) .sky__flare,
.sky.is-armed:not(.is-lit) .sky__actions::before { opacity: 0; }

.sky.is-lit .upshot__lit { animation: lamps-on 2400ms linear both; }
.sky.is-lit .upshot--right .upshot__lit { animation-delay: 240ms; }
.sky.is-lit .sky__dusk,
.sky.is-lit .sky__flare { animation: light-fill 2600ms ease-out both; }
.sky.is-lit .sky__actions::before { animation: light-fill 1600ms ease-out 700ms both; }
.sky__cta { position: relative; overflow: hidden; }
.sky__cta::after {
  content: '';
  position: absolute;
  inset: -60% auto -60% 0;
  width: 42%;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,0.6), transparent);
  transform: skewX(-18deg) translateX(-130%);
  pointer-events: none;
}
.sky.is-lit .sky__cta::after { animation: cta-sheen 950ms ease-out 1150ms both; }
@keyframes lamps-on {
  0% { opacity: 0; }
  8% { opacity: 0.55; }
  14% { opacity: 0.2; }
  30% { opacity: 0.55; }
  100% { opacity: 1; }
}
@keyframes light-fill { from { opacity: 0; } to { opacity: 1; } }
@keyframes cta-sheen {
  from { transform: skewX(-18deg) translateX(-130%); }
  to { transform: skewX(-18deg) translateX(350%); }
}
@media (max-width: 640px), (hover: none) and (pointer: coarse) {
  .sky__copy::before { background-image: url('../assets/images/floodlight-cold.webp'); }
  .sky.is-lit .sky__copy::after { animation: light-fill 2400ms ease-out both; }
}
@media (prefers-reduced-motion: reduce) {
  .sky.is-lit .upshot__lit, .sky.is-lit .sky__copy::after,
  .sky.is-lit .sky__dusk, .sky.is-lit .sky__flare,
  .sky.is-lit .sky__actions::before, .sky.is-lit .sky__cta::after { animation: none; }
}

.hall.is-waiting .hall__lamp, .hall.is-waiting .hall__spill { opacity: 0; }
.hall.is-lit .hall__lamp { animation: light-fill 2600ms ease-out both; }
.hall.is-lit .hall__spill { animation: light-fill 3200ms ease-out both; }
@media (prefers-reduced-motion: reduce) {
  .hall.is-waiting .hall__lamp, .hall.is-waiting .hall__spill { opacity: 1; }
  .hall.is-lit .hall__lamp, .hall.is-lit .hall__spill { animation: none; }
}

/* Short phone windows give the copy more room rather than squeezing the CTA
   against the browser controls. Tiny screens can still scroll naturally. */
@media (max-width: 640px) and (max-height: 740px) {
  .sky__inner { grid-template-rows: minmax(140px, 1fr) auto; }
  .sky__field { min-height: 140px; }
}

@media (max-width: 640px) {
  .sky__copy::before, .sky__copy::after {
    bottom: calc(100% - var(--mast-w) * 0.54 + var(--z-space-8));
    mask-image: linear-gradient(#000 30%, transparent 65%);
  }
}

/* Phone postcard: an open center, a visible lamp post, and a loose sticker
   border. Positions are deliberately irregular; no runtime randomization. */
@media (max-width: 640px) {
  .sky__inner {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-height: 100svh;
    padding: calc(var(--topbar-h) + 210px) 0
      calc(130px + env(safe-area-inset-bottom, 0px));
  }

  .sky__copy { width: 78%; }
  .sky__title { font-size: clamp(2rem, 8.5vw, 2.5rem); }
  .sky__copy::before,
  .sky__copy::after {
    width: 240px;
    left: calc(50% - 150px);
    top: -255px;
    bottom: auto;
    transform: rotate(12deg);
    opacity: 0.8;
    mask-image: linear-gradient(#000 55%, transparent 78%);
  }

}

/* The same edge frame spans every viewport. */
.sky__field {
  position: absolute;
  inset: calc(var(--topbar-h) + var(--z-space-5)) 0 0;
  width: 100%;
  min-height: 0;
  z-index: 2;
}
.sticker {
  --size: calc(var(--frame-size) * var(--frame-scale, 1));
  left: var(--frame-x);
  top: var(--frame-y);
  opacity: var(--frame-haze, var(--haze));
  z-index: var(--frame-z, 2);
  filter: var(--frame-shadow, none);
}
.sticker img { transform: rotate(var(--frame-turn)); }

@media (max-width: 640px), (max-height: 500px) {
  /* Phone edge frame: 14 side stickers and 4 bottom stickers */
  .sticker:nth-child(1) { --frame-x: 3%; --frame-y: 8%; --frame-size: 60px; --frame-turn: -24deg; }
  .sticker:nth-child(2) { --frame-x: 98%; --frame-y: 12%; --frame-size: 76px; --frame-turn: 18deg; }
  .sticker:nth-child(3) { --frame-x: 1%; --frame-y: 24%; --frame-size: 74px; --frame-turn: 25deg; }
  .sticker:nth-child(4) { --frame-x: 96%; --frame-y: 30%; --frame-size: 46px; --frame-turn: -16deg; }
  .sticker:nth-child(5) { --frame-x: 2%; --frame-y: 42%; --frame-size: 52px; --frame-turn: -20deg; }
  .sticker:nth-child(6) { --frame-x: 101%; --frame-y: 47%; --frame-size: 68px; --frame-turn: 28deg; }
  .sticker:nth-child(7) { --frame-x: -2%; --frame-y: 59%; --frame-size: 72px; --frame-turn: -15deg; }
  .sticker:nth-child(8) { --frame-x: 97%; --frame-y: 64%; --frame-size: 54px; --frame-turn: -24deg; }
  .sticker:nth-child(9) { --frame-x: 2%; --frame-y: 76%; --frame-size: 60px; --frame-turn: 18deg; }
  .sticker:nth-child(10) { --frame-x: 98%; --frame-y: 81%; --frame-size: 44px; --frame-turn: 16deg; }
  .sticker:nth-child(11) { --frame-x: 0%; --frame-y: 91%; --frame-size: 62px; --frame-turn: -28deg; }
  .sticker:nth-child(12) { --frame-x: 20%; --frame-y: 97%; --frame-size: 56px; --frame-turn: -18deg; }
  .sticker:nth-child(13) { --frame-x: 41%; --frame-y: 94%; --frame-size: 70px; --frame-turn: 22deg; }
  .sticker:nth-child(14) { --frame-x: 64%; --frame-y: 98%; --frame-size: 54px; --frame-turn: -12deg; }
  .sticker:nth-child(15) { --frame-x: 85%; --frame-y: 95%; --frame-size: 66px; --frame-turn: 24deg; }
  .sticker:nth-child(16) { --frame-x: 99%; --frame-y: 21%; --frame-size: 44px; --frame-turn: -16deg; }
  .sticker:nth-child(17) { --frame-x: 0%; --frame-y: 68%; --frame-size: 44px; --frame-turn: 18deg; }
  .sticker:nth-child(18) { --frame-x: 100%; --frame-y: 91%; --frame-size: 44px; --frame-turn: -12deg; }
}

@media (min-width: 641px) and (min-height: 501px) {
  .sky__inner {
    --frame-scale: 1.1;
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-height: 100svh;
    padding: calc(var(--topbar-h) + var(--z-space-16)) var(--z-space-16) 140px;
  }

  /* Bottom bridge frame across larger screens: layered depth ribbon */
  .sticker:nth-child(1) {
    --frame-x: 5%; --frame-y: 88%; --frame-size: 48px; --frame-turn: -16deg;
    --frame-haze: 0.72; --frame-z: 1; --frame-shadow: drop-shadow(0 4px 10px rgba(20, 34, 54, 0.12));
  }
  .sticker:nth-child(2) {
    --frame-x: 11%; --frame-y: 94%; --frame-size: 72px; --frame-turn: 12deg;
    --frame-haze: 1; --frame-z: 3; --frame-shadow: drop-shadow(0 10px 22px rgba(20, 34, 54, 0.22));
  }
  .sticker:nth-child(3) {
    --frame-x: 18%; --frame-y: 89%; --frame-size: 60px; --frame-turn: -8deg;
    --frame-haze: 0.88; --frame-z: 2; --frame-shadow: drop-shadow(0 6px 16px rgba(20, 34, 54, 0.16));
  }
  .sticker:nth-child(4) {
    --frame-x: 24%; --frame-y: 95%; --frame-size: 46px; --frame-turn: 18deg;
    --frame-haze: 0.70; --frame-z: 1; --frame-shadow: drop-shadow(0 4px 10px rgba(20, 34, 54, 0.12));
  }
  .sticker:nth-child(5) {
    --frame-x: 30%; --frame-y: 91%; --frame-size: 74px; --frame-turn: -14deg;
    --frame-haze: 1; --frame-z: 3; --frame-shadow: drop-shadow(0 10px 22px rgba(20, 34, 54, 0.22));
  }
  .sticker:nth-child(6) {
    --frame-x: 37%; --frame-y: 96%; --frame-size: 62px; --frame-turn: 14deg;
    --frame-haze: 0.86; --frame-z: 2; --frame-shadow: drop-shadow(0 6px 16px rgba(20, 34, 54, 0.16));
  }
  .sticker:nth-child(7) {
    --frame-x: 44%; --frame-y: 90%; --frame-size: 48px; --frame-turn: -10deg;
    --frame-haze: 0.72; --frame-z: 1; --frame-shadow: drop-shadow(0 4px 10px rgba(20, 34, 54, 0.12));
  }
  .sticker:nth-child(8) {
    --frame-x: 50%; --frame-y: 96%; --frame-size: 76px; --frame-turn: 8deg;
    --frame-haze: 1; --frame-z: 3; --frame-shadow: drop-shadow(0 10px 22px rgba(20, 34, 54, 0.22));
  }
  .sticker:nth-child(9) {
    --frame-x: 57%; --frame-y: 91%; --frame-size: 46px; --frame-turn: -12deg;
    --frame-haze: 0.70; --frame-z: 1; --frame-shadow: drop-shadow(0 4px 10px rgba(20, 34, 54, 0.12));
  }
  .sticker:nth-child(10) {
    --frame-x: 63%; --frame-y: 95%; --frame-size: 60px; --frame-turn: 16deg;
    --frame-haze: 0.86; --frame-z: 2; --frame-shadow: drop-shadow(0 6px 16px rgba(20, 34, 54, 0.16));
  }
  .sticker:nth-child(11) {
    --frame-x: 70%; --frame-y: 90%; --frame-size: 72px; --frame-turn: -12deg;
    --frame-haze: 1; --frame-z: 3; --frame-shadow: drop-shadow(0 10px 22px rgba(20, 34, 54, 0.22));
  }
  .sticker:nth-child(12) {
    --frame-x: 76%; --frame-y: 95%; --frame-size: 48px; --frame-turn: 20deg;
    --frame-haze: 0.72; --frame-z: 1; --frame-shadow: drop-shadow(0 4px 10px rgba(20, 34, 54, 0.12));
  }
  .sticker:nth-child(13) {
    --frame-x: 83%; --frame-y: 89%; --frame-size: 62px; --frame-turn: -10deg;
    --frame-haze: 0.88; --frame-z: 2; --frame-shadow: drop-shadow(0 6px 16px rgba(20, 34, 54, 0.16));
  }
  .sticker:nth-child(14) {
    --frame-x: 89%; --frame-y: 95%; --frame-size: 70px; --frame-turn: 14deg;
    --frame-haze: 1; --frame-z: 3; --frame-shadow: drop-shadow(0 10px 22px rgba(20, 34, 54, 0.22));
  }
  .sticker:nth-child(15) {
    --frame-x: 95%; --frame-y: 91%; --frame-size: 58px; --frame-turn: -16deg;
    --frame-haze: 0.86; --frame-z: 2; --frame-shadow: drop-shadow(0 6px 16px rgba(20, 34, 54, 0.16));
  }
  .sticker:nth-child(16) {
    --frame-x: 24%; --frame-y: 82%; --frame-size: 48px; --frame-turn: -14deg;
    --frame-haze: 0.88; --frame-z: 2; --frame-shadow: drop-shadow(0 6px 16px rgba(20, 34, 54, 0.16));
  }
  .sticker:nth-child(17) {
    --frame-x: 54%; --frame-y: 82%; --frame-size: 48px; --frame-turn: 12deg;
    --frame-haze: 0.88; --frame-z: 2; --frame-shadow: drop-shadow(0 6px 16px rgba(20, 34, 54, 0.16));
  }
  .sticker:nth-child(18) {
    --frame-x: 85%; --frame-y: 82%; --frame-size: 48px; --frame-turn: -10deg;
    --frame-haze: 0.88; --frame-z: 2; --frame-shadow: drop-shadow(0 6px 16px rgba(20, 34, 54, 0.16));
  }
}

@media (min-width: 1024px) {
  .sky__inner { --frame-scale: 1.35; }
}

@media (max-width: 360px) {
  .sticker { --size: calc(var(--frame-size) * 0.8); }
}

/* Arrival: clear words first, then let the cream fog lift from the scenery.
   Only the small copy elements blur; the full-screen reveal uses opacity. */
.sky.is-armed { background-color: var(--z-canvas); }
.sky.is-armed .sky__copy > * {
  animation: copy-sharpen 700ms ease-out both;
}
.sky.is-armed .sky__copy > .sky__intro { animation-delay: 90ms; }
.sky.is-armed .sky__copy > .sky__actions { animation-delay: 160ms; }
.sky.is-armed .sky__backdrop,
.sky.is-armed .sky__foreground {
  animation: fog-clear 950ms ease-out 750ms both;
}
.sky.is-lit .sky__actions::before { animation-delay: 2400ms; }
.sky.is-lit .sky__cta::after { animation-delay: 2850ms; }
@keyframes copy-sharpen {
  from { opacity: 0; filter: blur(8px); transform: translateY(6px); }
  to { opacity: 1; filter: blur(0); transform: translateY(0); }
}
@keyframes fog-clear { from { opacity: 0; } to { opacity: 1; } }
@media (max-width: 640px), (hover: none) and (pointer: coarse) {
  .sky.is-armed .sky__copy::before {
    animation: fog-clear 950ms ease-out 750ms both;
  }
}
/* A resize settles the scene, and reduced motion always shows the whole page. */
.sky.is-flowing .sky__copy > *,
.sky.is-flowing .sky__backdrop,
.sky.is-flowing .sky__foreground,
.sky.is-flowing .sky__copy::before { animation: none; }
@media (prefers-reduced-motion: reduce) {
  .sky.is-armed .sky__copy > *,
  .sky.is-armed .sky__backdrop,
  .sky.is-armed .sky__foreground,
  .sky.is-armed .sky__copy::before { animation: none; }
}

/* Clip horizontal decoration without clipping the hero/ground overlap. */
#main-content { overflow-x: clip; }
