/* ─────────────────────────────────────────────────────────────────────────────
   THE STAGE — hero layout, the room the android stands in, and the telemetry
   that labels it.

   THE FLOOR
   One perspective grid, on the floor plane only, rotated 90deg and masked to
   fade out with distance. It is the only grid on this page and it is here for
   a specific reason: it is what tells the eye that the figure has a FLOOR
   instead of a background, and a floor is what makes a 3D scene read as a room
   rather than as a poster with a drop shadow. Remove it and the rig becomes a
   cutout floating on black.

   It lives outside .rig deliberately. The grid needs mask-image and overflow to
   fade, and both of those flatten a preserve-3d subtree — put it inside the rig
   and every plate on the figure collapses onto one plane. Its own perspective
   is its own, and the two scenes line up because they share --depth-perspective.

   THE TELEMETRY
   Three callouts, each naming a real thing from the project, each anchored to
   the plate it describes, each joined to it by a hairline leader:

     state.db   → the chest core    (Memory is one SQLite file)
     ~95 lines  → the jaw and neck  (the loop is that short)
     watch      → the visor         (the dashboard lights up as it thinks)

   This is the load-bearing decision in the hero. A HUD of invented telemetry
   would tell a visitor nothing and would be the first thing on the page to look
   generic. Three real facts, placed on the object that performs them, tell the
   visitor what the thing IS before they read a word of copy — and they quietly
   account for three of the project's four pillars.
   ───────────────────────────────────────────────────────────────────────────── */

.hero {
  padding-top: clamp(80px, 8vh, 108px);
  padding-bottom: clamp(48px, 6vh, 80px);
  position: relative;
  overflow: hidden;
}

.hero .shell {
  max-width: var(--hero-max);
}

.hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 500px);
  gap: var(--space-8) clamp(40px, 4vw, 68px);
  align-items: center;
  /* The right inset exists for the annotations, not for symmetry. The two
     callouts hang 180px past the stage's right edge, which ran them off a
     1440 viewport; pulling the grid in by up to 44px puts them inside the
     frame without shrinking the figure or the copy. */
  padding-inline-end: clamp(0px, 6vw, 84px);
}

/* ── copy column ────────────────────────────────────────────────────────────
   Positioned, not static, because the registration marks on it are drawn at its
   corners and the padding keeps the headline clear of them. */
.hero__copy {
  position: relative;
  z-index: var(--z-copy);
  padding: var(--space-6) 0;
}

/* THE REGISTRATION MARKS — top-left and bottom-right.
   Two diagonal corners, because four is a certificate border and two is a
   drawing origin. Drawn with two borders each on the two pseudo-elements, so
   the copy inside stays selectable text and no markup is spent on them. */
.hero__copy::before,
.hero__copy::after {
  content: "";
  position: absolute;
  width: var(--mark-len);
  height: var(--mark-len);
  pointer-events: none;
  border-color: var(--mark-ink);
  border-style: solid;
  border-width: 0;
}

.hero__copy::before {
  top: 0;
  left: calc(var(--space-4) * -1);
  border-top-width: var(--rule-width);
  border-left-width: var(--rule-width);
}

.hero__copy::after {
  bottom: 0;
  right: calc(var(--space-4) * -1);
  border-bottom-width: var(--rule-width);
  border-right-width: var(--rule-width);
}

.hero__mark {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-6);
}

.hero__mark img {
  width: 32px;
  height: 32px;
  image-rendering: -webkit-optimize-contrast;
}

/* ── the headline ─────────────────────────────────────────────────────────────
   JetBrains Mono ExtraBold, four hand-broken lines, and one word stamped in
   outline. The breaks are authored rather than left to the browser because the
   whole composition depends on where they fall: three short statement lines and
   then one long word that the eye lands on last.

   The line wrappers are <span>s holding whole words, not split characters, so
   the accessible name of the h1 is still one uninterrupted string. */
.hero__title {
  font-family: var(--face-mono);
  font-size: var(--t-display);
  font-weight: 800;
  letter-spacing: -0.045em;
  line-height: var(--leading-display);
  margin-bottom: var(--space-6);
  text-wrap: nowrap;
}

.hero__title .ln {
  display: block;
  white-space: nowrap;
}

/* THE STAMP.
   Transparent fill with a 1px cyan stroke: the register of a technical drawing,
   where a section is called out by outlining it rather than by filling it.

   Guarded by @supports, and that guard is not decoration. A stroked word with a
   transparent fill is INVISIBLE in any engine without -webkit-text-stroke, and
   a hero headline that renders as a blank space on Firefox for Linux is a worse
   outcome than a filled one. Inside the guard the fill is set to transparent;
   outside it, the word is simply bold text. */
.hero__title .stamp {
  font-style: normal;
  color: var(--text-ink);
  letter-spacing: -0.03em;
}

@supports (-webkit-text-stroke: 1px #000) {
  .hero__title .stamp {
    -webkit-text-stroke: 1px color-mix(in srgb, var(--accent) 88%, transparent);
    color: var(--stroke-empty);
    text-shadow: 0 0 26px color-mix(in srgb, var(--accent) 34%, transparent);
  }
}

/* THE NUMBER.
   Cyan, with a tick rule under it rather than an underline: a 1px line with
   the accent's full opacity on its leading 30% and fading out, which is how a
   dimension line is drawn on a drawing rather than how a hyperlink is
   underlined. */
.hero__title .tick {
  font-style: normal;
  color: var(--accent-fg);
  text-shadow: 0 0 30px color-mix(in srgb, var(--accent) 48%, transparent);
  background: linear-gradient(
      90deg,
      var(--accent) 0%,
      color-mix(in srgb, var(--accent) 30%, transparent) 34%,
      transparent 100%
    )
    no-repeat left calc(100% + 8px) / 100% 1px;
  padding-bottom: 8px;
}

.hero__lead {
  margin-bottom: var(--space-8);
  max-width: 44ch;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-bottom: var(--space-6);
}

/* The install line, set under the buttons because it is what 90% of the people
   who reach this page came for. Copyable, mono, with the prompt as part of the
   block so the copied string is the whole command. */
.install {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  height: var(--control-min-h);
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4);
  background: var(--surface-sunk);
  border: var(--rule-width) solid var(--rule);
  border-radius: var(--radius);
  font: 500 var(--t-small) / 1 var(--face-mono);
  color: var(--text-muted);
  cursor: pointer;
  transition: border-color var(--motion-fast) var(--motion-ease),
    color var(--motion-fast) var(--motion-ease);
}

.install:hover {
  border-color: color-mix(in srgb, var(--accent) 46%, transparent);
  color: var(--text-ink);
}

.install code {
  color: var(--accent-fg);
}

.install__prompt {
  color: var(--text-faint);
  user-select: none;
}

.install__state {
  font-size: var(--t-micro);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-faint);
  min-width: 5ch;
}

.install.is-copied {
  border-color: color-mix(in srgb, var(--ok) 55%, transparent);
}

.install.is-copied .install__state {
  color: var(--ok);
}

/* ── stage ────────────────────────────────────────────────────────────────────
   The android's room. 480px wide and 660 tall on desktop, which is the size at
   which the rig's own 420x560 box has floor above it and headroom below the
   floor line — the two things that make it a standing figure. */
.stage {
  position: relative;
  width: 100%;
  max-width: 500px;
  aspect-ratio: 500 / 590;
  justify-self: center;
  perspective: var(--depth-perspective);
  perspective-origin: 50% 42%;
  z-index: var(--z-stage);
}

/* The horizon: one hairline where the floor plane meets the back wall. It gives
   the eye somewhere to put the vanishing point.

   At 62% it cut straight across the figure's chest, which read as a stray rule
   through the artwork rather than as a room. At 74% it sits below the
   abdomen, which is where the horizon has to be for a bust standing on a
   floor it is only partly shown above. */
.stage__horizon {
  position: absolute;
  left: 8%;
  right: 8%;
  top: 74%;
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent 0%,
    color-mix(in srgb, var(--accent) 26%, transparent) 30%,
    color-mix(in srgb, var(--accent) 26%, transparent) 70%,
    transparent 100%
  );
}

.stage__floor {
  position: absolute;
  left: -60%;
  right: -60%;
  top: 74%;
  bottom: -6%;
  transform-origin: 50% 0%;
  transform: rotateX(74deg);
  /* Two repeating gradients at right angles: one set of lines receding away
     from the camera, one set running across. They are the same 90px cell, so
     the intersections land on the grid. */
  background-image: repeating-linear-gradient(
      90deg,
      color-mix(in srgb, var(--text-ink) 13%, transparent) 0 1px,
      transparent 1px 90px
    ),
    repeating-linear-gradient(
      0deg,
      color-mix(in srgb, var(--text-ink) 9%, transparent) 0 1px,
      transparent 1px 90px
    );
  /* Fade the grid out with distance from the camera. A mask, not an opacity —
     an opacity would fade the whole plane uniformly and the far end would stay
     as bright as the near end, which is the thing that makes CSS grids look
     like wallpaper. */
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 34%, #000 72%, transparent 100%);
  mask-image: linear-gradient(180deg, transparent 0%, #000 34%, #000 72%, transparent 100%);
}

.stage__rig {
  position: absolute;
  left: 50%;
  top: 48%;
  translate: -50% -50%;
}

.stage__packets {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* Above the metal and below nothing. Packets are light travelling in front of
     the figure, so drawing them over the plates is the physically honest
     order — and it means the sink at the chest core can be covered by the
     core's own ignition instead of needing a second layer. */
  z-index: var(--z-copy);
  pointer-events: none;
}

/* ── telemetry callouts ───────────────────────────────────────────────────────
   Each is a label plus a hairline leader that runs to its plate. The leader is
   drawn with a pseudo-element so the label stays selectable text and the
   geometry stays in CSS. */
.callout {
  position: absolute;
  font: 500 var(--t-micro) / 1.3 var(--face-mono);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-faint);
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.callout::before {
  content: "";
  height: 1px;
  width: 26px;
  flex: none;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--accent) 50%, transparent));
}

.callout b {
  font-weight: 500;
  color: var(--accent-fg);
}

/* The two side callouts mirror each other, so the leader is placed with an
   explicit order on each side rather than with flex-direction: row-reverse.
   Reversing the axis moves the leader to the wrong end — a callout sitting
   right of the figure needs its leader on the LEFT to point back at it, and
   row-reverse puts it on the right. Order states which end is which. */
/* Both side callouts hang OUTSIDE the stage on the RIGHT and point back in.

   The first version anchored them with `left: -12px` / `right: -12px` and put
   the label first in the flex row — which puts the LABEL on the inside and the
   leader on the outside, so both labels were drawn across the figure's head and
   chest. Anchoring by the box's LEFT edge instead (left: 100%+10) puts the whole
   box outside the stage with only its leader reaching in, and the 40px gutter
   between the 420px rig and the 500px stage is exactly what the leader is sized
   to cross.

   Both on the right, not one per side: the left callout's 180px of text
   reached back across the copy column and printed over the headline. There is
   300px of empty shell to the right of the stage and nothing to put in it, so
   that is where annotations go. */
.callout--core {
  left: calc(100% + 12px);
  top: 58%;
  text-align: left;
}

.callout--core::before {
  order: 1;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--accent) 50%, transparent));
}

.callout--loop {
  left: calc(100% + 12px);
  top: 36%;
}

.callout--loop::before {
  order: 1;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--accent) 50%, transparent));
}

.callout--loop span,
.callout--core span,
.callout--loop b,
.callout--core b {
  order: 2;
}

.callout--watch {
  left: 50%;
  top: 4%;
  translate: -50% 0;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  color: var(--text-muted);
}

.callout--watch::before {
  width: 1px;
  height: 26px;
  order: 2;
  background: linear-gradient(180deg, transparent, color-mix(in srgb, var(--accent) 46%, transparent));
}

/* ── responsive ───────────────────────────────────────────────────────────────
   Below 1040 the hero stacks and the figure goes UNDER the copy, because the
   copy is what a visitor came for and the figure is what makes them stay. The
   callouts go with it: they hang outside the stage by design, and a stacked
   stage is centred in a column with no room outside it. Nothing is lost —
   every fact on them is also on the four pillar cards below. */
@media (max-width: 1040px) {
  .hero__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--section-y);
  }

  .hero__copy {
    order: 1;
  }

  .stage {
    order: 2;
    max-width: 420px;
  }

  .callout {
    display: none;
  }
}

@media (max-width: 700px) {
  .stage {
    max-width: 320px;
  }

  /* The rig is 420px wide and the stage is 320px here, so the figure hangs 15px
     past each edge of a 390px viewport. `.hero`'s overflow:hidden swallows it,
     which means it cannot cause a scrollbar — but the shoulders and the plinth
     are exactly the parts that must stay whole, so the figure is scaled to fit
     rather than clipped. `scale` and not `transform: scale()`: it composes
     with the transform that pointer parallax writes, so the two never fight. */
  .rig {
    scale: 0.72;
  }

  .hero__actions .btn {
    flex: 1 1 auto;
  }
}

/* ── reduced motion ───────────────────────────────────────────────────────────
   The packets are the one thing on this page that cannot be switched off by a
   duration — they are drawn by a rAF loop, not by a keyframe. The canvas is
   simply removed, and the core keeps its static ignition so the figure is
   still lit and still finished-looking. */
@media (prefers-reduced-motion: reduce) {
  .stage__packets {
    display: none;
  }
}
