/* ==========================================================================
   Hero  —  Figma "Component 449 / Hero Section - Animate" (705:132587)
   Frame 1920 x 1080, background Mono/Dark.
   Coordinates below are the Figma values, scaled by --s.
   ========================================================================== */

.hero {
  /* Everything below the masthead is lifted by this, in design units. The
     frame is 1080 tall and a Windows laptop's viewport, after its chrome, is
     shorter than a MacBook's -- so the tagline and SCROLL DOWN were falling
     under the fold there while being fine on a Mac. The masthead stays put;
     only the content that has to fit moves. */
  --hero-lift: 56;

  top: 0;
  height: calc(1080 * var(--s));
  background: var(--c-mono-dark);
  overflow: hidden;
  z-index: 2;
}

.hero__item { position: absolute; }

/* --- dot sphere ---------------------------------------------------------
   663x663. Rises 149px from the Default position (top 309) to Animate (160). */
.hero__sphere {
  left: calc(892 * var(--s));
  top: calc((160 - var(--hero-lift, 0)) * var(--s));
  width: calc(663 * var(--s));
  height: calc(663 * var(--s));
  transform: translateY(calc(149 * var(--s)));
  transition: transform calc(var(--rv-dur) * 1.4) var(--rv-ease);
}
.hero.is-in .hero__sphere { transform: none; }

/* The galaxy. hero-galaxy.js puts a canvas and a bloom layer in this box and
   orbits ~11,700 dots inside it; see that file for the motion model.

   The <img> stays in the markup as the first paint and as the no-JS state —
   it is the same artwork, and the two masks in base.css load it anyway, so it
   costs nothing extra here. The canvas fades it out once it has a frame down,
   which keeps the hero from being briefly empty on a slow connection. */
.hero__sphere img {
  display: block;
  width: 100%;
  height: 100%;
  transition: opacity .4s linear;
}

.hero__galaxy-canvas {
  position: absolute;
  inset: 0;
  display: block;
  opacity: 0;
  transition: opacity .4s linear;
}
/* Not scoped to .hero__sphere: the engine mounts on any [data-galaxy] box --
   the About hero has one of its own -- and adds .is-galaxy to whichever box
   it mounted on. */
.is-galaxy .hero__galaxy-canvas { opacity: 1; }

/* Core bloom. A wash behind the dots so the middle of the field reads warmer
   than the rim. It sits on its own layer rather than in the canvas because the
   canvas is written with putImageData, which overwrites instead of blending
   and would erase anything drawn underneath it. */
.hero__galaxy-glow {
  position: absolute;
  left: 50%;
  top: 50%;
  /* The outermost dot sits at 325.72 of the 663 box, so the field's own
     diameter is 98.2% of this box — not 100%. Sizing the gradient to that
     makes "60%" below mean 60% of the sphere's radius, which is where the
     bloom is meant to reach, rather than 60% of some larger square. */
  width: 98.2%;
  height: 98.2%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  background: radial-gradient(closest-side circle,
              #7FB4FF 0%, rgba(127, 180, 255, 0.55) 32%, rgba(127, 180, 255, 0) 60%);
}

/* --- detect overlay ------------------------------------------------------
   Four L corners, a label and a confidence score, drawn around the formed
   shape during the hold. hero-galaxy.js positions and sizes these from the
   shape's own box every frame; everything here is only appearance.

   The corners are Ls, not a rectangle: each element carries two 1px borders
   and grows its arms out of the corner as the bracket draws. */

.hero__det {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
}

.hero__det-c {
  position: absolute;
  display: block;
  width: 0;
  height: 0;
  border: 0 solid var(--c-accent-yellow);
}
.hero__det-c--tl { border-top-width: 1px; border-left-width: 1px; }
.hero__det-c--tr { border-top-width: 1px; border-right-width: 1px; }
.hero__det-c--br { border-bottom-width: 1px; border-right-width: 1px; }
.hero__det-c--bl { border-bottom-width: 1px; border-left-width: 1px; }

/* Scan line. Faint, and brightest at its middle so it reads as a sweep
   rather than as a rule someone left across the artwork. The dots doing the
   brightening are what actually sells it; this is the visible edge of it. */
.hero__det-scan {
  position: absolute;
  display: block;
  height: 1px;
  opacity: 0;
  background: linear-gradient(to right,
              rgba(255, 209, 26, 0) 0%,
              rgba(255, 209, 26, 0.85) 22%,
              rgba(255, 209, 26, 0.85) 78%,
              rgba(255, 209, 26, 0) 100%);
}

.hero__det-label,
.hero__det-score {
  position: absolute;
  font-size: max(11px, calc(13 * var(--s)));
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  /* sit above the corner rather than on it */
  transform: translateY(calc(-100% - 7 * var(--s)));
  opacity: 0;
}
.hero__det-score { text-align: right; }

@media (prefers-reduced-motion: reduce) {
  .hero__det { display: none; }
}

/* The reveal transform above is the box's only movement: it rises 149px once,
   on scroll, and then holds. Everything else happens inside the canvas. */

/* --- masthead ----------------------------------------------------------- */

.hero__logo {
  left: calc(48 * var(--s));
  top: calc(36 * var(--s));
  width: calc(162.633 * var(--s));
  height: calc(40 * var(--s));
}
/* The img is wrapped in a link to home, and an inline <a> has no size of its
   own -- so the img's 100% would resolve against a shrink-to-fit box. */
.hero__logo a { display: block; width: 100%; height: 100%; }
.hero__logo img { display: block; width: 100%; height: 100%; }

.hero__nav {
  left: calc(335 * var(--s));
  top: calc(32 * var(--s));
  width: calc(533 * var(--s));
  height: calc(48 * var(--s));
}
.hero__nav-inner {
  display: flex;
  align-items: flex-start;
  gap: calc(24 * var(--s));
  padding: calc(14 * var(--s)) calc(24 * var(--s)) calc(15 * var(--s));
  background: var(--c-light-dark);
}

.hero__login {
  left: calc(1555.7 * var(--s));
  top: calc(32 * var(--s));
  width: calc(120.304 * var(--s));
  height: calc(48 * var(--s));
}
.hero__login .btn { width: 100%; height: 100%; }

.hero__cta-top {
  left: calc(1700 * var(--s));
  top: calc(32 * var(--s));
  width: calc(172 * var(--s));
  height: calc(48 * var(--s));
}
.hero__cta-top .btn { width: 100%; height: 100%; }

/* --- metadata row (y 430) ------------------------------------------------ */

.hero__eyebrow {
  left: calc(50 * var(--s));
  top: calc((430 - var(--hero-lift, 0)) * var(--s));
  width: calc(285 * var(--s));
  height: calc(38 * var(--s));
}

.hero__nodisrupt {
  left: calc(643 * var(--s));
  top: calc((430 - var(--hero-lift, 0)) * var(--s));
  width: calc(143 * var(--s));
  height: calc(38 * var(--s));
  white-space: nowrap;
}

.hero__clock {
  left: calc(1782 * var(--s));
  top: calc((449 - var(--hero-lift, 0)) * var(--s));
  width: calc(84 * var(--s));
  height: calc(19 * var(--s));
  white-space: nowrap;
}

/* --- hairlines (y 491.5) ------------------------------------------------- */

/* Both rules run at the sphere from opposite sides, and both fade out as they
   reach it rather than stopping on a hard edge -- so the dots look like what
   interrupts them. Only the end facing the sphere fades: the long rule's
   right, the short rule's left. A mask rather than a gradient fill, so the
   rule keeps its own colour and the reveal still animates the fill's width
   underneath. */
.hero__rule-long {
  left: calc(48 * var(--s));
  top: calc((491.5 - var(--hero-lift, 0)) * var(--s));
  width: calc(861 * var(--s));
  -webkit-mask-image: linear-gradient(to right, #000 62%, transparent 100%);
          mask-image: linear-gradient(to right, #000 62%, transparent 100%);
}

.hero__rule-short {
  left: calc(1538 * var(--s));
  top: calc((491.5 - var(--hero-lift, 0)) * var(--s));
  width: calc(334 * var(--s));
  -webkit-mask-image: linear-gradient(to left, #000 62%, transparent 100%);
          mask-image: linear-gradient(to left, #000 62%, transparent 100%);
}

/* --- headline ------------------------------------------------------------ */

.hero__title {
  left: calc(48 * var(--s));
  top: calc((516 - var(--hero-lift, 0)) * var(--s));
  width: calc(653 * var(--s));
  height: calc(86 * var(--s));
}
.hero__title h1 {
  font-family: var(--f-sans);
  font-weight: 300;
  font-size: calc(72 * var(--s));
  line-height: 1.2;
  letter-spacing: calc(-2.88 * var(--s));
  color: var(--c-mono-light);
  white-space: nowrap;
}
/* Figma kerns this single glyph open by +2.16px. */
.hero__title .kern-r { letter-spacing: calc(2.16 * var(--s)); }

/* --- body copy ----------------------------------------------------------- */

.hero__lede {
  left: calc(335 * var(--s));
  top: calc((662 - var(--hero-lift, 0)) * var(--s));
  width: calc(495 * var(--s));
  height: calc(84 * var(--s));
}
.hero__lede p {
  font-size: calc(20 * var(--s));
  line-height: 1.4;
  letter-spacing: calc(-0.6 * var(--s));
  color: var(--c-mono-light);
}

/* --- primary CTA --------------------------------------------------------- */

.hero__cta {
  left: calc(335 * var(--s));
  top: calc((794 - var(--hero-lift, 0)) * var(--s));
  width: calc(184 * var(--s));
  height: calc(48 * var(--s));
}
.hero__cta .btn { width: 100%; height: 100%; }

/* --- outlined wordmarks (bottom right) ----------------------------------- */

.hero__wm-1 {
  left: calc(1532.25 * var(--s));
  top: calc((812.16 - var(--hero-lift, 0)) * var(--s));
  width: calc(334.522 * var(--s));
  height: calc(52.848 * var(--s));
}
.hero__wm-2 {
  left: calc(1242.94 * var(--s));
  top: calc((898.16 - var(--hero-lift, 0)) * var(--s));
  width: calc(621.623 * var(--s));
  height: calc(67.104 * var(--s));
}
.hero__wm-1 img, .hero__wm-2 img { width: 100%; height: 100%; }

/* --- scroll cue ---------------------------------------------------------- */

.hero__scroll {
  left: calc(48 * var(--s));
  top: calc((981 - var(--hero-lift, 0)) * var(--s));
  width: calc(127 * var(--s));
  height: calc(19 * var(--s));
  white-space: nowrap;
}

/* ==========================================================================
   Reflow — below 900px the 1920 composition is rebuilt as a single column.
   ========================================================================== */

@media (max-width: 1023px), (max-width: 1200px) and (orientation: portrait) {
  .hero {
    height: auto;
    min-height: 100svh;
    padding: 24px 20px 48px;
    display: flex;
    flex-direction: column;
  }

  .hero__item { position: static; width: auto; height: auto; }

  .hero__sphere {
    position: absolute;
    left: 50%;
    top: 42%;
    width: min(115vw, 620px);
    height: min(115vw, 620px);
    transform: translate(-50%, -50%) translateY(60px);
    opacity: 0.55;
    pointer-events: none;
  }
  .hero.is-in .hero__sphere { transform: translate(-50%, -50%); }

  /* Masthead becomes a simple bar; the desktop nav pill is replaced by a
     disclosure list so 4 links do not crowd a phone width. */
  .hero__masthead {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    position: relative;
    z-index: 2;
  }
  .hero__logo { width: 132px; height: 32px; flex: none; }
  .hero__login { display: none; }
  .hero__cta-top .btn { font-size: 14px; padding: 10px 16px; }

  .hero__body { position: relative; z-index: 2; margin-top: 56px; }

  .hero__eyebrow { font-size: 12px; letter-spacing: 0.04em; }
  .hero__nodisrupt { margin-top: 10px; font-size: 12px; }
  .hero__clock { display: none; }

  /* Nothing to run at down here -- the sphere is behind the copy, not beside
     it -- so the rule keeps its full length. */
  .hero__rule-long { margin: 20px 0 0; width: 100%; -webkit-mask-image: none; mask-image: none; }
  .hero__rule-short { display: none; }

  .hero__title { margin-top: 20px; }
  .hero__title h1 {
    font-size: clamp(38px, 11vw, 60px);
    letter-spacing: -0.04em;
    white-space: normal;
  }

  .hero__lede { margin-top: 20px; max-width: 46ch; }
  .hero__lede p { font-size: 16px; }

  .hero__cta { margin-top: 28px; }
  .hero__cta .btn { width: 184px; height: 48px; font-size: 15px; }
  .hero__cta .btn__c { width: 11.217px; height: 12px; }

  .hero__wordmarks {
    position: relative;
    z-index: 2;
    margin-top: auto;
    padding-top: 56px;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 4px;
  }
  .hero__wm-1 { width: 46%; max-width: 240px; }
  .hero__wm-2 { width: 86%; max-width: 440px; }

  .hero__scroll { margin-top: 24px; font-size: 12px; position: relative; z-index: 2; }
}

/* Four links fit comfortably at tablet width, so the nav stays there now that
   the reflow starts at 1200; only a phone lacks the room. The comment in the
   reflow block once promised a disclosure list in the nav's place, which was
   never built -- so below this width the hero still has no primary navigation
   and a menu is owed. */
@media (max-width: 767px), (min-width: 768px) and (max-width: 1200px) and (orientation: portrait) {
  .hero__nav { display: none; }
}

/* The .rv slide wrapper sits between a fixed-size mask and a button. Without
   an explicit height, the button's height:100% resolves against an auto-height
   parent and collapses to its content box (39px instead of Figma's 48px). */
.hero__login .rv,
.hero__cta-top .rv,
.hero__cta .rv { height: 100%; }

/* Grouping wrappers exist only for the mobile reflow; on desktop they must
   not create a layout box, so the absolutely-positioned items keep resolving
   against .hero. */
.hero__masthead,
.hero__body,
.hero__wordmarks { display: contents; }

@media (max-width: 1023px), (max-width: 1200px) and (orientation: portrait) {
  .hero__masthead  { display: flex; }
  .hero__body      { display: block; }
  .hero__wordmarks { display: flex; }
}
