/* ===== Hero =====
   Was a particle-shape canvas + the fidget-mode tool tiles, no text; the
   canvas was removed (see PARTICLE_ANIMATION_BACKUP.md — it wasn't loading
   reliably on mobile) and replaced with a plain headline. */
.hero {
  position: relative;
  min-height: 100vh;
  overflow: hidden;
}

/* Headline where the particle shape used to sit. Absolute + centered rather
   than in normal flow, same as the canvas it replaces, so it shares that
   layer's spot above the fidget tiles' resting area. z-index is deliberately
   below --z-hero-fidget (102): a tile falling past on its way down should
   cover the text, not the other way round.

   Same two-font combo as the logo wordmark (logo.css): --font-display
   (Georgia, italic) for the accent-coloured half, --font-body (system-ui)
   for the rest — here that split is "blue" vs "everything else" rather than
   "cristina" vs "iftodi", but it's the same pairing. The base rule below
   carries --font-body for the black/white majority of the text; the two
   blue spans (em and .hero__title-accent) opt back into --font-display so
   they keep reading as the logo's serif half regardless of which one it is. */
.hero__title {
  position: absolute;
  top: 40%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: var(--z-content);
  width: 90%;
  max-width: 900px;
  text-align: center;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: var(--fs-600);
  line-height: var(--leading-tight);
  color: var(--c-ink);
}
/* --- headline drop-in, once, after the fidget tiles have landed ---
   Each word starts 40px high and transparent and falls into place. Only
   opacity and transform animate, and every word is inline-block at its
   natural size from the first frame, so nothing reflows as the words
   arrive — a word landing never nudges its neighbours.

   The steps overlap rather than queue: each starts when the one before it
   is 85% done, i.e. 500ms × (1 − 0.85) = 75ms apart. data-step (not
   :nth-child) carries the order because the <br>s between lines are
   siblings too and would throw the counting off — and because two words
   can then share a step to land together.

   Gated behind .is-ready, which js/modules/hero-title.js adds once the
   tiles have settled, so the headline drops in after them rather than
   through them. The words hold the from-state until then. */
/* The drop-in animates `translate`, NOT `transform`, and that is load-bearing
   now that the words are shy. shy.css owns their `transform`, and this
   animation carries `animation-fill-mode: both` — so written as a transform it
   would hold `translateY(0)` forever after landing and the flee offset would
   never render at all. The individual `translate` property is a separate
   channel that composes with `transform` (it applies first), so the two
   effects add instead of one silently cancelling the other. */
@keyframes hero-word-drop {
  from { opacity: 0; translate: 0 -40px; }
  to   { opacity: 1; translate: 0 0; }
}
/* Everything EXCEPT animation-name lives here, and the .is-ready rule below
   supplies only the name. Deliberately not the `animation:` shorthand there:
   the shorthand resets every animation-* it omits, so its implicit
   `animation-delay: 0s` — at (0,3,0), above the (0,2,0) [data-step] rules —
   would silently flatten the stagger and land every word at once. */
.hero__word {
  display: inline-block;
  opacity: 0;
  translate: 0 -40px;
  animation-duration: var(--dur-hero-drop);
  animation-timing-function: ease;
  animation-fill-mode: both;
}
.hero__word[data-step="0"] { animation-delay:   0ms; }
.hero__word[data-step="1"] { animation-delay:  75ms; }
.hero__word[data-step="2"] { animation-delay: 150ms; }   /* product + thinking */
.hero__word[data-step="3"] { animation-delay: 225ms; }

.hero__title.is-ready .hero__word { animation-name: hero-word-drop; }

/* reset.css globally clamps animation-duration to 0.01ms under reduced
   motion, but leaves delays alone — which would still blink the words in
   one by one over 300ms. Skip the animation and just show the text; the
   from-state above has to be overridden or it would stay invisible. */
@media (prefers-reduced-motion: reduce) {
  /* `translate` is the from-state's channel now; `transform` is cleared too
     because shy.css hands these elements one, and shy-cards.js bails out
     under reduced motion without ever writing --dx/--dy. */
  .hero__word { opacity: 1; translate: none; transform: none; animation: none; }
}

/* "ai-assisted" — same italic treatment as .section__title, on this one word
   rather than the whole line, plus the brand highlight colour and the
   logo's serif half of its font pairing. */
.hero__title em {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  color: var(--c-highlight);
}
/* "designer" — highlight colour without the italic, matching the mock;
   same serif as "ai-assisted" since both are the blue half of the pairing. */
.hero__title-accent {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--c-highlight);
}

/* fidget-mode physics layer (Matter.js draws its canvas here).
   z-index sits ABOVE --z-nav (see tokens.css): tiles can fall onto and rest
   on the navbar, but must always paint over it where they overlap, never
   get clipped behind it. The canvas is transparent everywhere except where
   a tile is actually drawn, so this only affects the real overlap pixels. */
.hero__fidget {
  position: absolute;
  inset: 0;
  z-index: var(--z-hero-fidget);
}
.hero__fidget canvas {
  position: absolute;
  inset: 0;
}

/* Tooltip over a hovered tool tile — macOS-dock style bubble with a tail.
   JS sets left/top to the tile's centre; the translate lifts it clear and
   centres it. pointer-events: none so it never steals the drag. */
.fidget-tip {
  position: absolute;
  left: 0; top: 0;
  transform: translate(-50%, -100%) translateY(-4px);
  padding: var(--space-2) var(--space-3);
  max-width: 18rem;
  background: rgba(58, 58, 62, .96);
  color: #fff;
  font-size: var(--text-xs);
  line-height: var(--leading-normal);
  text-align: center;
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  white-space: pre-line;          /* so a label can hold a line break */
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--motion-hover);
}
.fidget-tip.is-visible { opacity: 1; }
/* the little downward tail */
.fidget-tip::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 6px solid transparent;
  border-top-color: rgba(58, 58, 62, .96);
}

/* ===== top bar: logo (left) + sound / colour / theme (right) =====
   One flex row spanning the Hero's top edge, rather than two independently
   absolutely-positioned corners. The logo's content height (icon + text line)
   and the pill-bar's height (button + its own padding) are never the same
   number, so anchoring both to the same `top` offset left their *centres* a
   few px apart — the logo visibly sat higher than the icon row. A single flex
   parent with `align-items: center` lines up their cross-axis centres exactly,
   whatever either one's height happens to be, instead of chasing that gap
   with a hand-measured pixel offset.
   NOTE: absolutely positioned inside the Hero, so the whole row scrolls away
   with it — reachable only at the top of the page. `fixed` was tried and
   reverted: it does work (a fixed child escapes `.hero`'s `overflow: hidden`,
   since `.hero` sets no transform/filter/containment), but the bare logo then
   ran straight into every section heading that scrolled under it. */
.hero__topbar {
  position: absolute;
  top: var(--sp-3);
  left: var(--sp-3);
  right: var(--sp-3);
  z-index: var(--z-hero-controls);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.hero__controls {
  flex-shrink: 0;
}

/* Look/animation live in css/components/logo.css; placement is now just
   "first flex child" — no positioning of its own. */
.hero__logo {
  flex-shrink: 0;
}
