/* ==========================================================================
   UP AL — Hero
   Typography and whitespace still carry the section. Alongside it, the
   brand's signature visual: a 3-slide Story-style slideshow (real
   project shots, crossfading — js/hero-story.js) held in a living glass
   viewfinder — framed by camera focus brackets, a motion-tracking
   reticle, a real segmented progress rail, a waveform, and a REC
   readout with a running timecode, all on a layered glass panel that
   answers mouse movement with a soft parallax tilt (js/hero.js). Each
   slide fills the frame itself (.hero__slide inside .hero__visual,
   first inside .hero__stage-surface so the chrome above it keeps
   compositing on top, camera-viewfinder-over-a-shot); every other
   material — the glass recipe, chrome gradients, ambient-sweep device —
   is the same one used across the rest of the site. At ≥901px the
   composition sits in a second grid column beside the text; below that
   it stacks under the text in the same single column, sized down but
   otherwise identical.
   ========================================================================== */

.hero {
  position: relative;
  z-index: 0;
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding-block: calc(var(--header-height) + var(--space-8)) var(--space-10);
  scroll-margin-top: var(--header-height);
  overflow: hidden;
  background: radial-gradient(ellipse 140% 65% at 50% -15%, rgba(158, 207, 247, 0.055), transparent 60%);
}

.hero__layout {
  position: relative;
  z-index: 1;
}

.hero__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2.5rem;
  max-width: 800px;
  margin-inline: auto;
  text-align: center;
}

.hero__heading {
  font-size: var(--fs-hero);
  font-weight: var(--fw-extrabold);
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-tight);
  color: var(--color-text-primary);
  text-wrap: balance;
}

.hero__subheading {
  max-width: 580px;
  margin-inline: auto;
  font-size: var(--fs-lead);
  font-weight: var(--fw-regular);
  line-height: var(--lh-relaxed);
  color: var(--color-text-secondary);
  text-wrap: balance;
}

.hero__actions {
  margin-block-start: 1rem;
}

.hero__microcopy {
  margin-block-start: var(--space-4);
  font-size: var(--fs-small);
  font-weight: var(--fw-regular);
  color: var(--color-text-muted);
}

/* A second, closely-spaced line under the microcopy — same secondary
   type as .hero__microcopy (same size/weight/color), just set off with
   a tighter top margin and a light italic so it reads as a signature
   line closing out the block, not a second heading. */

.hero__signature {
  margin-block-start: var(--space-2);
  font-size: var(--fs-small);
  font-weight: var(--fw-regular);
  font-style: italic;
  color: var(--color-text-muted);
  opacity: 0.85;
}

/* ---------- Desktop: text + glass composition ---------- */

@media (min-width: 901px) {
  .hero__layout {
    display: grid;
    grid-template-columns: minmax(0, 0.92fr) minmax(340px, 1.08fr);
    gap: var(--space-9);
    align-items: center;
  }

  .hero__inner {
    align-items: flex-start;
    text-align: start;
    max-width: 560px;
    margin-inline: 0;
  }

  .hero__subheading {
    margin-inline: 0;
  }

  .hero__actions {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
  }
}

/* ---------- Entrance (subtle, staggered) ---------- */

@media (prefers-reduced-motion: no-preference) {
  .hero__heading,
  .hero__subheading,
  .hero__actions,
  .hero__composition {
    opacity: 0;
    animation: hero-fade-up 820ms var(--ease-premium) both;
  }

  .hero__heading {
    animation-delay: 60ms;
  }

  .hero__subheading {
    animation-delay: 200ms;
  }

  .hero__actions {
    animation-delay: 340ms;
  }

  .hero__composition {
    animation-delay: 460ms;
  }
}

@keyframes hero-fade-up {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ---------- Small screens ---------- */

@media (max-width: 560px) {
  .hero {
    padding-block: calc(var(--header-height) + var(--space-6)) var(--space-8);
  }

  .hero__inner {
    gap: 1.75rem;
  }

  .hero__actions {
    margin-block-start: var(--space-2);
  }
}

/* ---------- Decorative background ----------
   A single abstract ribbon, echoing the loop in the UP AL mark, plus a
   soft directional glow. Both sit far below the content in opacity and
   are purely atmospheric — decorative only, never interactive. */

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

.hero__background svg {
  position: absolute;
  bottom: -12%;
  inset-inline-end: -10%;
  width: min(64vw, 820px);
  height: auto;
  opacity: 0.055;
}

@media (max-width: 700px) {
  .hero__background svg {
    width: min(120vw, 620px);
    bottom: -8%;
    inset-inline-end: -30%;
  }
}

/* ==========================================================================
   Hero composition — the brand's signature visual (all breakpoints)

   A layered glass viewfinder, not a card. Concept: a real photo is what
   the interface is watching — held inside a camera frame with a reticle
   that drifts and refocuses, a segmented progress rail, a waveform, and
   a live REC readout with a real running timecode. The whole panel
   answers mouse movement with a soft parallax tilt (js/hero.js) — three
   depth layers (surface+photo / chrome / REC readout) drifting at
   different rates (mouse-only, so it's inert on touch devices, which is
   fine — the tap zones are the touch equivalent). Every material — glass
   recipe, chrome gradients, corner-bracket device, ambient sweep — is
   the same one used elsewhere on the site, just choreographed into one
   composition instead of a static card. Below 901px it's the same
   composition at a smaller size, stacked under the text instead of
   beside it.
   ========================================================================== */

.hero__composition {
  position: relative;
  width: 100%;
  margin-block-start: var(--space-8);
}

@media (min-width: 901px) {
  .hero__composition {
    margin-block-start: 0;
  }
}

.hero__orb {
  width: 560px;
  height: 560px;
  inset-inline-end: -160px;
  top: 50%;
  transform: translateY(-50%);
  background: radial-gradient(circle, rgba(158, 207, 247, 0.14), transparent 70%);
  opacity: 0.8;
}

.hero__stage {
  position: relative;
  z-index: 1;
  width: 100%;
  aspect-ratio: 1402 / 1122;
  perspective: 1200px;
}

/* A small soft light bleed right where the photo's own diagonal beam
   reaches the frame's top edge — sits on .hero__stage itself (not
   clipped, unlike the surface/visual below it), so a few pixels of it
   spill past the glass boundary instead of being cut off flush. Reads
   as the photo's own light breaking the frame, not a decoration
   competing with it. Positioned with a physical `left` (not a logical
   inline-start) since it has to line up with a fixed point in the
   photo itself, regardless of page direction. */

.hero__stage::before {
  content: "";
  position: absolute;
  z-index: 2;
  left: 65.8%;
  top: -8px;
  width: 56px;
  height: 56px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.5), rgba(158, 207, 247, 0.22) 45%, transparent 72%);
  filter: blur(6px);
  opacity: 0.85;
  pointer-events: none;
}

.hero__stage-surface {
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
  overflow: hidden;
  background: radial-gradient(120% 90% at 16% -12%, rgba(255, 255, 255, 0.1), transparent 55%),
    radial-gradient(36% 28% at 84% 4%, rgba(255, 255, 255, 0.06), transparent 62%),
    linear-gradient(205deg, rgba(255, 255, 255, 0.035) 0%, transparent 42%),
    radial-gradient(130% 85% at 50% 125%, rgba(0, 0, 0, 0.34), transparent 60%), var(--color-bg-elevated);
  backdrop-filter: blur(28px) saturate(160%);
  -webkit-backdrop-filter: blur(28px) saturate(160%);
  box-shadow: var(--shadow-card);
  transition: transform 500ms var(--ease-premium);
  transform: translate3d(calc(var(--hero-tx, 0) * 5px), calc(var(--hero-ty, 0) * 5px), 0)
    rotateX(calc(var(--hero-ty, 0) * -2.2deg)) rotateY(calc(var(--hero-tx, 0) * 2.2deg));
}

/* Camera focus frame — the same viewfinder-corner device used on the
   Services preview frame, with a slow periodic focus-pull so it reads as
   actively focusing rather than a static crop mark. */

.hero__stage-corner {
  position: absolute;
  z-index: 3;
  width: 18px;
  height: 18px;
  opacity: 0.55;
  pointer-events: none;
}

.hero__stage-corner--start-start {
  inset-block-start: var(--space-5);
  inset-inline-start: var(--space-5);
  border-block-start: 1px solid var(--color-border-strong);
  border-inline-start: 1px solid var(--color-border-strong);
}

.hero__stage-corner--start-end {
  inset-block-start: var(--space-5);
  inset-inline-end: var(--space-5);
  border-block-start: 1px solid var(--color-border-strong);
  border-inline-end: 1px solid var(--color-border-strong);
}

.hero__stage-corner--end-start {
  inset-block-end: var(--space-5);
  inset-inline-start: var(--space-5);
  border-block-end: 1px solid var(--color-border-strong);
  border-inline-start: 1px solid var(--color-border-strong);
}

.hero__stage-corner--end-end {
  inset-block-end: var(--space-5);
  inset-inline-end: var(--space-5);
  border-block-end: 1px solid var(--color-border-strong);
  border-inline-end: 1px solid var(--color-border-strong);
}

@media (prefers-reduced-motion: no-preference) {
  .hero__stage-corner {
    animation: hero-focus-pull 6s ease-in-out infinite;
  }
}

@keyframes hero-focus-pull {
  0%,
  82%,
  100% {
    opacity: 0.55;
    transform: scale(1);
  }
  90% {
    opacity: 0.95;
    transform: scale(0.9);
  }
}

/* Dynamic light — two chrome sweeps at different angles/speeds, layered
   for depth rather than one flat pass. */

.hero__sweep {
  position: absolute;
  z-index: 1;
  inset-block: 0;
  pointer-events: none;
}

.hero__sweep--a {
  inset-inline-start: -40%;
  width: 30%;
  background: linear-gradient(75deg, transparent 0%, rgba(255, 255, 255, 0.07) 50%, transparent 100%);
  transform: translateX(0) skewX(-18deg);
}

.hero__sweep--b {
  inset-inline-start: -55%;
  width: 20%;
  background: linear-gradient(75deg, transparent 0%, rgba(158, 207, 247, 0.09) 50%, transparent 100%);
  transform: translateX(0) skewX(-18deg);
}

@media (prefers-reduced-motion: no-preference) {
  .hero__sweep--a {
    animation: hero-sweep-a 13s ease-in-out infinite;
  }

  .hero__sweep--b {
    animation: hero-sweep-b 17s ease-in-out infinite;
    animation-delay: 4s;
  }
}

@keyframes hero-sweep-a {
  0%,
  8% {
    transform: translateX(0) skewX(-18deg);
  }
  46%,
  54% {
    transform: translateX(430%) skewX(-18deg);
  }
  100% {
    transform: translateX(430%) skewX(-18deg);
  }
}

@keyframes hero-sweep-b {
  0%,
  6% {
    transform: translateX(0) skewX(-18deg);
  }
  40%,
  48% {
    transform: translateX(600%) skewX(-18deg);
  }
  100% {
    transform: translateX(600%) skewX(-18deg);
  }
}

/* ---------- The visual: a 3-slide Story ----------
   The real centerpiece — now three real project shots (the original
   photo plus two more) crossfading through a single glass frame like a
   premium Instagram Story, driven by js/hero-story.js. Structure:
   .hero__visual holds three .hero__slide layers (one per image, each
   absolutely stacked, only the .is-active one opaque) plus one shared
   .hero__visual-overlay scrim on top of all of them. Its own border +
   radius mirror the glass surface's, so the frame stays crisp even
   though a photo now covers the surface's own background. Slide 1
   (the existing photo) already matches the frame's own ratio, so
   object-fit: cover fills it edge-to-edge with no extra cropping
   beyond what was already approved; slides 2 and 3 are natively 16:9
   and use a different two-layer treatment instead (see
   .hero__visual-img--backdrop/--contain below) so nothing in them ever
   gets cropped.

   Each plain .hero__visual-img (slide 1) is untouched from the
   single-photo version: it still carries its own constant fill scale
   plus its own share of the mouse parallax (a few pixels less/more
   than the frame moves, so the photo reads as sitting slightly behind
   the glass). The
   very slow Ken Burns zoom/pan lives one level up, on .hero__slide —
   a different element, so the two transforms compose (nest) instead of
   fighting over the same property: the slide's own subtle scale/pan
   is layered on top of the image's already-established parallax scale. */

.hero__visual {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
}

.hero__slide {
  position: absolute;
  inset: 0;
  z-index: 0;
  opacity: 0;
  transform: scale(1) translate(0, 0);
  transition: opacity 450ms var(--ease-premium), transform 5600ms var(--ease-premium);
}

.hero__slide.is-active {
  z-index: 1;
  opacity: 1;
}

/* Three distinct, barely-there camera moves (2.5–3% zoom + a sliver of
   pan) so consecutive slides never feel like they're repeating the
   exact same motion. */

.hero__slide:nth-child(1).is-active { transform: scale(1.028) translate(-0.6%, 0.4%); }
.hero__slide:nth-child(2).is-active { transform: scale(1.026) translate(0.5%, -0.5%); }
.hero__slide:nth-child(3).is-active { transform: scale(1.03) translate(-0.4%, -0.6%); }

.hero__visual-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  transition: transform 500ms var(--ease-premium);
  transform: translate3d(calc(var(--hero-tx, 0) * 6px), calc(var(--hero-ty, 0) * 6px), 0) scale(1.09);
  /* A light unifying grade so all three slides — one existing photo,
     two new screenshots processed separately — read as one consistent
     cinematic palette rather than different sources. */
  filter: contrast(1.04) saturate(1.04);
}

/* Slides 2 and 3 are natively 16:9 — much wider/shorter than the
   frame's own 1402:1122 ratio. Forcing them to cover would crop off
   the phone on one side or the platform icons on the other, which the
   brief explicitly rules out ("if needed, slightly reduce the image
   scale instead of cropping"). So these two get a two-layer treatment
   instead of the single object-fit: cover image every other slide
   uses:
     - .hero__visual-img--backdrop: the same photo, blown up and
       blurred, filling the frame edge-to-edge like every other slide
       (so there's never a hard black bar or empty gap) — purely
       ambient, aria-hidden, no parallax of its own.
     - .hero__visual-img--contain: the same photo again, sharp, scaled
       to fit entirely inside the frame with zero cropping — this is
       the one carrying the real alt text and the normal parallax.
   Same technique full-screen video players use for mismatched-aspect
   content (Apple TV, Spotify Canvas) — reads as an intentional
   cinematic treatment, not a fallback. */

.hero__visual-img--backdrop {
  object-fit: cover;
  transform: scale(1.18);
  transition: none;
  filter: blur(46px) saturate(1.1) brightness(0.55) contrast(1.04);
}

.hero__visual-img--contain {
  object-fit: contain;
  filter: contrast(1.05) saturate(1.05) drop-shadow(0 22px 46px rgba(0, 0, 0, 0.5));
}

.hero__visual-overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  background-color: rgba(8, 12, 16, 0.12);
  pointer-events: none;
}

/* A soft ambient glow behind the photo's own chrome mark, slowly
   breathing — the one animation on the artwork itself that's visible
   at rest, not just on mouse move. Positioned with a physical
   left/top (not logical) to stay locked to the mark's actual position
   in the photo regardless of page direction. */

.hero__visual-glow {
  position: absolute;
  z-index: 1;
  left: 49.9%;
  top: 47.2%;
  width: 22%;
  aspect-ratio: 1 / 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.35), rgba(158, 207, 247, 0.18) 45%, transparent 75%);
  filter: blur(10px);
  opacity: 0.5;
  pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
  .hero__visual-glow {
    animation: hero-visual-glow-pulse 5s ease-in-out infinite;
  }
}

@keyframes hero-visual-glow-pulse {
  0%,
  100% {
    opacity: 0.35;
    transform: translate(-50%, -50%) scale(0.96);
  }
  50% {
    opacity: 0.6;
    transform: translate(-50%, -50%) scale(1.04);
  }
}

/* ---------- Motion-tracking reticle ----------
   A small ring that drifts along a fixed loop inside the frame — the
   "camera" is quietly tracking something. Falls back to a still resting
   position in browsers without offset-path support, or under reduced
   motion. */

.hero__reticle {
  position: absolute;
  z-index: 3;
  top: 0;
  inset-inline-start: 0;
  width: 22px;
  height: 22px;
  offset-path: path("M40,210 C120,60 280,40 330,140 C370,220 260,290 170,260 C100,236 90,180 150,160");
  offset-distance: 0%;
  opacity: 0.7;
  pointer-events: none;
}

.hero__reticle::before,
.hero__reticle::after {
  content: "";
  position: absolute;
  background-color: var(--color-accent);
  opacity: 0.8;
}

.hero__reticle::before {
  inset-inline-start: 0;
  top: 50%;
  width: 100%;
  height: 1px;
  transform: translateY(-50%);
}

.hero__reticle::after {
  inset-block-start: 0;
  left: 50%;
  width: 1px;
  height: 100%;
  transform: translateX(-50%);
}

.hero__reticle-ring {
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  border: 1px solid var(--color-accent);
  opacity: 0.85;
}

@supports not (offset-path: path("M0,0")) {
  .hero__reticle {
    top: 18%;
    inset-inline-start: 62%;
  }
}

@media (prefers-reduced-motion: no-preference) {
  .hero__reticle {
    animation: hero-reticle-travel 22s linear infinite, hero-reticle-breathe 3s ease-in-out infinite;
  }
}

@keyframes hero-reticle-travel {
  to {
    offset-distance: 100%;
  }
}

@keyframes hero-reticle-breathe {
  0%,
  100% {
    opacity: 0.55;
    transform: scale(0.92);
  }
  50% {
    opacity: 0.9;
    transform: scale(1);
  }
}

/* ---------- Waveform ----------
   A short row of bars near the lower corner — quiet texture standing in
   for audio/content production, cheap to animate via scaleY only. */

.hero__waveform {
  position: absolute;
  z-index: 1;
  inset-block-end: var(--space-6);
  inset-inline-start: var(--space-6);
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 20px;
  opacity: 0.5;
}

.hero__waveform span {
  display: block;
  width: 2px;
  height: 100%;
  border-radius: 1px;
  background-color: var(--color-text-secondary);
  transform: scaleY(0.3);
  transform-origin: bottom;
}

@media (prefers-reduced-motion: no-preference) {
  .hero__waveform span {
    animation: hero-waveform 1.6s ease-in-out infinite;
  }
}

.hero__waveform span:nth-child(1) { animation-duration: 1.3s; animation-delay: -0.1s; }
.hero__waveform span:nth-child(2) { animation-duration: 1.7s; animation-delay: -0.6s; }
.hero__waveform span:nth-child(3) { animation-duration: 1.2s; animation-delay: -0.3s; }
.hero__waveform span:nth-child(4) { animation-duration: 1.9s; animation-delay: -0.9s; }
.hero__waveform span:nth-child(5) { animation-duration: 1.4s; animation-delay: -0.2s; }
.hero__waveform span:nth-child(6) { animation-duration: 1.8s; animation-delay: -1.1s; }
.hero__waveform span:nth-child(7) { animation-duration: 1.3s; animation-delay: -0.5s; }
.hero__waveform span:nth-child(8) { animation-duration: 1.6s; animation-delay: -0.8s; }
.hero__waveform span:nth-child(9) { animation-duration: 1.5s; animation-delay: -0.4s; }
.hero__waveform span:nth-child(10) { animation-duration: 1.7s; animation-delay: -1s; }

@keyframes hero-waveform {
  0%,
  100% {
    transform: scaleY(0.25);
  }
  50% {
    transform: scaleY(1);
  }
}

/* ---------- Segmented timeline: real Story progress bars ----------
   One segment per slide (three segments, three slides). Each bar's fill
   is driven by js/hero-story.js writing a --fill custom property (0–1)
   onto the button itself every frame — read by the ::after pseudo's
   transform, so a real screen-reader-usable <button> can still drive a
   value pseudo-elements can't be scripted directly. Bars before the
   current slide sit fully lit (--fill: 1), bars after sit empty
   (--fill: 0), and the current one climbs smoothly in real time — the
   same right-to-left-of-reading fill direction as every other
   underline/reveal on the site. The active segment also picks up a
   faint chrome-blue glow. Clicking any segment jumps straight to that
   slide (wired in JS); an invisible taller hit-area (::before) makes
   the thin 2px bar easier to click/tap without changing how it looks. */

.hero__timeline {
  position: absolute;
  z-index: 1;
  inset-inline: var(--space-6);
  inset-block-end: var(--space-5);
  display: flex;
  gap: 5px;
}

.hero__timeline-seg {
  position: relative;
  flex: 1;
  height: 2px;
  border-radius: 1px;
  background-color: var(--color-border-strong);
  overflow: hidden;
  border: none;
  margin: 0;
  padding: 0;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
}

.hero__timeline-seg::before {
  content: "";
  position: absolute;
  inset-block: -10px;
  inset-inline: 0;
}

.hero__timeline-seg:focus-visible {
  outline-offset: 3px;
}

.hero__timeline-seg::after {
  content: "";
  display: block;
  width: 100%;
  height: 100%;
  background: var(--gradient-chrome-blue);
  transform: scaleX(var(--fill, 0));
  transform-origin: right center;
}

.hero__timeline-seg.is-active::after {
  filter: drop-shadow(0 0 4px rgba(158, 207, 247, 0.7));
}

/* ---------- Tap zones (mobile Story navigation) ----------
   Two invisible full-height buttons split down the middle — tapping the
   physical left side goes back a slide, the physical right side
   advances (kept as real left/right, not logical start/end, so it
   matches the side of the screen regardless of the page's RTL
   direction — the same convention every Story-style UI uses). Kept
   clear of the timeline's own (taller, invisible) hit-area at the
   bottom so the progress bars stay independently clickable. Not part
   of the tab order or accessibility tree (tabindex="-1",
   aria-hidden="true" in the markup) — the progress bar buttons above
   already provide an equivalent, better-labelled way to navigate. */

.hero__tap-zone {
  position: absolute;
  z-index: 1;
  top: 0;
  bottom: 44px;
  width: 50%;
  border: none;
  margin: 0;
  padding: 0;
  background: transparent;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
}

.hero__tap-zone--prev {
  left: 0;
}

.hero__tap-zone--next {
  right: 0;
}

/* ---------- Floating particles ----------
   A handful of soft, blurred motes drifting slowly upward and fading —
   the only purely atmospheric element, kept small and low-opacity so it
   reads as depth, not confetti. */

.hero__particle {
  position: absolute;
  z-index: 1;
  border-radius: 50%;
  background-color: var(--color-accent);
  pointer-events: none;
  opacity: 0;
}

@media (prefers-reduced-motion: no-preference) {
  .hero__particle {
    animation: hero-particle-float linear infinite;
  }
}

.hero__particle:nth-child(1) { top: 78%; inset-inline-start: 20%; width: 3px; height: 3px; animation-duration: 13s; animation-delay: 0s; }
.hero__particle:nth-child(2) { top: 62%; inset-inline-start: 72%; width: 2px; height: 2px; animation-duration: 16s; animation-delay: 2s; }
.hero__particle:nth-child(3) { top: 30%; inset-inline-start: 12%; width: 2px; height: 2px; animation-duration: 19s; animation-delay: 5s; }
.hero__particle:nth-child(4) { top: 85%; inset-inline-start: 55%; width: 3px; height: 3px; animation-duration: 14s; animation-delay: 7.5s; }
.hero__particle:nth-child(5) { top: 15%; inset-inline-start: 68%; width: 2px; height: 2px; animation-duration: 17s; animation-delay: 3.5s; }
.hero__particle:nth-child(6) { top: 45%; inset-inline-start: 85%; width: 2px; height: 2px; animation-duration: 15s; animation-delay: 9s; }

@keyframes hero-particle-float {
  0% {
    transform: translateY(0);
    opacity: 0;
  }
  12% {
    opacity: 0.5;
  }
  85% {
    opacity: 0.35;
  }
  100% {
    transform: translateY(-60px);
    opacity: 0;
  }
}

/* ---------- REC readout ----------
   A small glass pill outside the frame with a pulsing dot and a real
   running timecode (js/hero.js updates it once a second) — the
   composition's foreground layer, so it carries the most parallax
   movement. */

.hero__chip {
  position: absolute;
  z-index: 4;
  top: 6%;
  inset-inline-end: -8%;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.5rem 0.9rem;
  border-radius: var(--radius-full);
  border: 1px solid var(--color-border);
  background: var(--color-bg-elevated);
  backdrop-filter: blur(20px) saturate(150%);
  -webkit-backdrop-filter: blur(20px) saturate(150%);
  box-shadow: var(--shadow-card);
  transition: transform 500ms var(--ease-premium);
  transform: translate3d(calc(var(--hero-tx, 0) * 14px), calc(var(--hero-ty, 0) * 14px), 0);
}

/* On the narrow phones where the composition is now also visible, the
   frame itself is much narrower than on desktop, so the same -8% bleed
   would push the chip close to (or past) the actual screen edge. Pull
   it in just enough to stay clear, without losing the "breaks the
   frame" detail. */

@media (max-width: 560px) {
  .hero__chip {
    inset-inline-end: -2%;
  }
}

.hero__chip-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background-color: var(--color-accent);
  box-shadow: 0 0 8px 1px rgba(158, 207, 247, 0.55);
}

@media (prefers-reduced-motion: no-preference) {
  .hero__chip-dot {
    animation: hero-live-pulse 2.4s ease-in-out infinite;
  }
}

@keyframes hero-live-pulse {
  0%,
  100% {
    opacity: 0.4;
  }
  50% {
    opacity: 1;
  }
}

.hero__chip-label {
  font-size: 0.75rem;
  font-weight: var(--fw-semibold);
  letter-spacing: 0.08em;
  color: var(--color-text-secondary);
}

.hero__chip-time {
  font-size: 0.75rem;
  font-weight: var(--fw-medium);
  font-variant-numeric: tabular-nums;
  color: var(--color-text-muted);
}
