/* =============================================================================
   MissionAlarm — hero.css
   The stylesheet for the scroll-driven 3D hero, in both of the arrangements
   hero.js supports.

   Requires tokens.css first. Every colour here is a var(--token) from the app's
   own design system, so the site and the app are the same object.

   -----------------------------------------------------------------------------
   IT LOADS ITSELF IF IT HAS TO

   The sentinel below lets hero.js detect whether this file is on the page. If
   index.html did not link it, hero.js injects it next to itself. So this file
   is safe to link explicitly and safe to leave unlinked; it just must not be
   linked twice.

   Nothing in here is required for the page to be readable. Everything is either
   scoped to the 3D mode or to the standalone [data-hero] contract, so a page
   that never runs JavaScript is unaffected by all of it.
   ============================================================================= */

:root {
  --hero-css-loaded: 1;
}

/* =============================================================================
   1. Shared — the canvas and its scrim, in either arrangement
   ============================================================================= */

/* Never let the render eat a scroll gesture. */
.hero__canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--duration-card-reveal) var(--ease-spring);
}

/* Set by hero.js the moment the renderer starts, so the space fades in without
   waiting on the first screenshot to decode. */
[data-hero-live] .hero__canvas {
  opacity: 1;
}

/* The page's own zero-JS backdrop fades out when hero.js takes over, so the
   render brings a vignette of its own: it keeps the headline over the middle of
   the frame legible and stops the phone hitting a hard edge. */
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  opacity: 0.82;
  background:
    radial-gradient(52% 58% at 50% 52%, var(--surface-base) 0%, transparent 78%),
    radial-gradient(122% 100% at 50% 46%, transparent 46%, var(--surface-base) 100%),
    linear-gradient(to bottom, var(--surface-base) 0%, transparent 22%),
    linear-gradient(to top, var(--surface-base) 0%, transparent 32%);
}

/* Too narrow to hold the page's copy and a full-size phone at once. hero.js
   already steps the camera back; this takes the render down to a texture so the
   words stay the loudest thing on the screen. Declared after the rule above so
   it wins when both attributes are present. */
[data-hero-cramped] .hero__canvas {
  opacity: 0.4;
}

/* =============================================================================
   2. Standalone hero — the [data-hero] contract
   Used when hero.js is handed a whole hero section rather than a background
   layer. It builds the track, the sticky stage and the captions itself; the
   section only has to ship the static stacked-image fallback below.

     <section class="hero" data-hero data-hero-base="assets/screens/">
       <div class="hero__fallback">
         <div class="hero__shots">
           <figure class="hero__shot" data-hero-shot="AL-01">
             <div class="hero__shot-frame">
               <img src="assets/screens/AL-01.png" width="462" height="780" alt="...">
             </div>
             <figcaption>
               <span class="hero__shot-label">Lock screen</span>
               <span class="hero__shot-text">...</span>
             </figcaption>
           </figure>
           ... AF-01, MI-01, MI-09, HM-01, SK-01 ...
         </div>
       </div>
     </section>

   The fallback is the DEFAULT. hero.js sets data-hero-mode="3d" only once it
   has a working renderer, and that single attribute swaps the layouts. With no
   JavaScript the section stays exactly as authored.
   ============================================================================= */

[data-hero] {
  --hero-progress: 0;
  --hero-track-height: 520vh;
  --hero-shot-aspect: 360 / 780;

  position: relative;
  isolation: isolate;
  background-color: var(--surface-base);
  color: var(--text-primary);
  counter-reset: hero-shot;
}

/* -----------------------------------------------------------------------------
   2.1 The static fallback — the zero-JavaScript page
   -------------------------------------------------------------------------- */

.hero__fallback {
  position: relative;
  z-index: 1;
  padding: var(--space-xxxl) var(--space-gutter) var(--space-huge);
}

/* The six figures live in .hero__shots. Anything else the page puts inside
   .hero__fallback, a heading or a lede, stays out of the grid. */
.hero__shots {
  display: grid;
  gap: var(--space-xxl) var(--space-xl);
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  max-width: 1120px;
  margin-inline: auto;
  padding: 0;
  list-style: none;
}

.hero__shot {
  counter-increment: hero-shot;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  margin: 0;
  min-width: 0;
  /* A phone rendered 360px wide on a desktop is a poster, not a screenshot.
     Cap it so six of them stay a readable sequence rather than a scroll. */
  width: 100%;
  max-width: 320px;
  margin-inline: auto;
}

/* Each PNG is 462x780, but only the LEFT 360x780 is the phone screen; the rest
   is a design annotation column that must never reach the marketing site. This
   frame is a 360:780 window and the image is sized by height, so the annotation
   column falls outside it and is clipped. hero.js crops the WebGL texture the
   same way with texture.repeat. */
.hero__shot-frame {
  position: relative;
  overflow: hidden;
  aspect-ratio: var(--hero-shot-aspect);
  width: 100%;
  border-radius: var(--radius-card);
  border: var(--border-hairline) solid var(--smoke);
  background-color: var(--surface-raised);
  box-shadow: 0 24px 60px -30px var(--surface-base);
}

.hero__shot-frame img {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  display: block;
  height: 100%;
  width: auto;
  max-width: none;
  border: 0;
}

.hero__shot figcaption {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  font-family: var(--font-body);
  font-size: var(--body-size);
  line-height: var(--body-line);
  color: var(--text-secondary-accessible);
}

.hero__shot-label {
  font-family: var(--font-mono);
  font-size: var(--mono-label-size);
  font-weight: var(--mono-label-weight);
  line-height: var(--mono-label-line);
  letter-spacing: var(--mono-label-tracking);
  text-transform: uppercase;
  color: var(--acid);
}

/* The chronology of one morning, numbered without the page having to. */
.hero__shot-label::before {
  content: counter(hero-shot, decimal-leading-zero) " · ";
  color: var(--text-secondary);
}

.hero__shot-text {
  color: var(--text-secondary-accessible);
}

/* -----------------------------------------------------------------------------
   2.2 3D mode
   -------------------------------------------------------------------------- */

/* Off by default, so the track never adds 520vh of empty scroll to a page whose
   JavaScript did not run. */
.hero__track {
  display: none;
}

[data-hero-mode="3d"] .hero__track {
  display: block;
  position: relative;
  height: var(--hero-track-height);
}

/* The fallback stays in the accessibility tree. It carries the alt text and the
   captions, so a screen reader still gets the whole sequence even though the
   canvas is decorative. */
[data-hero-mode="3d"] .hero__fallback {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.hero__stage {
  position: sticky;
  inset-block-start: 0;
  display: block;
  overflow: hidden;
  height: 100vh;
  height: 100svh;
  background-color: var(--surface-base);
}

/* A vignette that sits under the copy and over the canvas, so the captions read
   against something rather than against the render. */
.hero__stage::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0.9;
  background:
    radial-gradient(120% 90% at 50% 40%, transparent 40%, var(--surface-base) 100%),
    linear-gradient(to top, var(--surface-base) 0%, transparent 42%);
}

/* -----------------------------------------------------------------------------
   2.3 Captions
   One per screen, stacked in the same grid cell, cross-faded by hero.js in step
   with the screenshot on the phone.
   -------------------------------------------------------------------------- */

.hero__captions {
  position: absolute;
  z-index: 2;
  inset-inline-start: 0;
  inset-block-end: 0;
  width: min(46ch, calc(100% - var(--space-gutter) * 2));
  margin: 0 var(--space-gutter) clamp(var(--space-xxl), 8vh, var(--space-huge));
  pointer-events: none;
  /* Every caption occupies the same cell, so the block never resizes as the
     copy changes and nothing below it moves. */
  display: grid;
  align-items: end;
}

.hero__caption {
  display: grid;
  gap: var(--space-sm);
  margin: 0;
  grid-column: 1;
  grid-row: 1;
  opacity: 0;
  transform: translate3d(0, var(--space-md), 0);
  transition:
    opacity var(--duration-blaze-fade) var(--ease-spring),
    transform var(--duration-blaze-fade) var(--ease-spring);
}

.hero__caption.is-active {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

.hero__caption-label {
  font-family: var(--font-mono);
  font-size: var(--mono-label-size);
  font-weight: var(--mono-label-weight);
  line-height: var(--mono-label-line);
  letter-spacing: var(--mono-label-tracking);
  text-transform: uppercase;
  color: var(--acid);
}

.hero__caption-text {
  font-family: var(--font-body);
  font-size: clamp(var(--body-size), 1.5vw, var(--title-size));
  font-weight: var(--body-weight);
  line-height: 1.45;
  letter-spacing: var(--title-tracking);
  color: var(--text-primary);
  text-wrap: pretty;
}

/* -----------------------------------------------------------------------------
   2.4 Optional decorations
   The page may place these inside its own [data-hero-stage].
   -------------------------------------------------------------------------- */

/* A hairline rail that fills as the morning plays out. */
.hero__progress {
  position: absolute;
  z-index: 2;
  inset-inline: var(--space-gutter);
  inset-block-end: var(--space-lg);
  height: var(--size-confidence-bar-height);
  border-radius: var(--radius-full);
  background-color: var(--surface-float);
  overflow: hidden;
  pointer-events: none;
}

.hero__progress::after {
  content: "";
  display: block;
  height: 100%;
  width: 100%;
  border-radius: inherit;
  background-color: var(--acid);
  transform-origin: left center;
  transform: scaleX(var(--hero-progress, 0));
}

/* A scroll cue that gets out of the way the moment scrolling starts. */
.hero__hint {
  position: absolute;
  z-index: 2;
  inset-inline-end: var(--space-gutter);
  inset-block-end: clamp(var(--space-xxl), 8vh, var(--space-huge));
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--mono-label-size);
  font-weight: var(--mono-label-weight);
  line-height: var(--mono-label-line);
  letter-spacing: var(--mono-label-tracking);
  text-transform: uppercase;
  color: var(--text-secondary-accessible);
  pointer-events: none;
  opacity: calc(1 - var(--hero-progress, 0) * 8);
}

/* =============================================================================
   3. Narrow and short viewports
   hero.js already declines 3D on a coarse pointer under 900px, but a narrow
   desktop window still gets it, so the stage has to hold together there.
   ============================================================================= */

@media (max-width: 720px) {
  .hero__fallback {
    padding: var(--space-xxl) var(--space-md) var(--space-xxxl);
  }

  .hero__shots {
    gap: var(--space-xl) var(--space-md);
  }

  .hero__captions {
    width: calc(100% - var(--space-md) * 2);
    margin-inline: var(--space-md);
  }

  .hero__progress,
  .hero__hint {
    inset-inline: var(--space-md);
  }

  .hero__hint {
    display: none;
  }
}

@media (max-height: 620px) {
  .hero__captions {
    width: min(38ch, calc(100% - var(--space-gutter) * 2));
    margin-block-end: var(--space-lg);
  }

  .hero__caption-text {
    font-size: var(--body-size);
  }

  .hero__hint {
    display: none;
  }
}

/* =============================================================================
   4. Reduced motion
   hero.js never starts the 3D stage under prefers-reduced-motion and tears it
   down if the preference changes while the page is open. These rules make sure
   nothing left in the hero animates either.
   ============================================================================= */

@media (prefers-reduced-motion: reduce) {
  .hero__canvas,
  .hero__caption {
    transition: none;
  }

  .hero__caption {
    transform: none;
  }
}

/* =============================================================================
   5. Forced colours and print
   ============================================================================= */

@media (forced-colors: active) {
  .hero__shot-frame {
    border-color: CanvasText;
  }

  .hero__canvas,
  .hero__scrim {
    display: none;
  }
}

@media print {
  .hero__canvas,
  .hero__scrim,
  .hero__captions {
    display: none;
  }

  .hero__track {
    display: none;
  }

  [data-hero-mode="3d"] .hero__fallback {
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    overflow: visible;
    clip-path: none;
    white-space: normal;
  }
}
