/* ==========================================================================
   FORMULA SPEED — wow layer
   Hero choreography, particle field, brand icons, infographics, layout variants.
   Loads after fs.css and depends on its tokens.
   ========================================================================== */

@property --rv { syntax: '<percentage>'; inherits: false; initial-value: 0%; }

/* ---------- Hero: every stage anchored bottom-left ----------
   One reading position for the whole run. The copy never moves, so the eye
   stays put and only the words change. The block sits on the floor of the
   viewport, in the left gutter: the corner belongs to the copy, and the cue
   and the telemetry are pushed onto the right rail rather than reserving a
   band underneath it (see the .scroller override further down). */
.hero-copy.chor {
  /* minmax(0, 1fr): the spacer row may collapse completely, so a tall stage
     eats the empty space above it instead of overflowing the stick — which
     clips the top of the headline, because .hero-stick is overflow:hidden. */
  grid-template-rows: minmax(0, 1fr) auto;
  align-content: end;
  justify-items: start;
  padding-bottom: clamp(1.3rem, 3.4vh, 2.4rem);
}
/* The cap is in rem, never ch: a ch here resolves against the stage's 16px
   body font, not the display size, and would clip the headline mid-word.
   44rem is not a taste figure: the longest single token in the hero copy
   ("concessionaria.") measures ~670px at the 5rem ceiling set below, so the
   box has to stay wider than that or the word breaks and the line count grows.
   The 62vw term keeps the column clear of the telemetry on mid-width screens,
   where the HUD reaches much further left than it does at 1440. */
.hero-copy.chor .hero-stage {
  grid-area: 2 / 1;
  align-self: end;
  justify-self: start;
  text-align: left;
  max-width: min(69rem, 78vw, 100%);
}
.hero-stage .d1,
.hero-stage .d2 {
  max-width: 100%;
  /* Last-resort guard: a word that cannot fit breaks instead of being cut
     off by the stick's overflow:hidden. Never fires at the sizes below. */
  overflow-wrap: break-word;
  /* pretty avoids leaving a single word alone on the final line; balance is
     the fallback where pretty is unsupported. The copy also binds its last
     two words with a non-breaking space, which no engine can override. */
  text-wrap: balance;
  text-wrap: pretty;
}
/* The hero headline tracks BOTH viewport axes. The global .d1 floor of 2.9rem
   is wider than a 390px phone can hold for the longest token, and a width-only
   clamp ignores height: on a 1440x620 laptop the 96px ceiling made a three-line
   stage 285px tall, so the block started at mid-screen however hard its bottom
   edge was pinned. The vh term is what keeps it on the floor, and it is also
   what guarantees the stage always fits the space it is given — nothing is ever
   clipped off the top by the stick. */
/* Hero display weight */
.hero-stage .d1,
.hero-stage .d2 {
  font-variation-settings: 'wdth' 120, 'wght' 880;
  letter-spacing: -0.05em;
  /* Same travelling formula as the rest of the display scale: a hard 0.88
     collides once the clamp drops this to ~37px on a phone. */
  line-height: calc(0.787em + 0.775rem);
}
.hero-stage .d1 { font-size: clamp(2.3rem, min(8.4vw, 12vh), 7rem); }
.hero-stage .d2 { font-size: clamp(1.75rem, min(5.9vw, 8.4vh), 4.7rem); }
.hero-stage .lede {
  font-size: clamp(1rem, 1.5vw, 1.3rem);
  margin-top: .85rem;
  max-width: min(42ch, 100%);
}
.hero-cta { margin-top: clamp(1.1rem, 2.8vh, 1.7rem); }

@media (max-width: 899px) {
  /* Under the desktop breakpoint the cue stays bottom-left and the telemetry
     bottom-right, so here — and only here — the copy clears that strip. */
  .hero-copy.chor { padding-bottom: calc(clamp(1.4rem, 5vh, 3rem) + 3.4rem); }
  .hero-copy.chor .hero-stage { max-width: 100%; }
  .hero-stage .lede { max-width: 100%; }
}

/* The headline is swept in by the airflow, left to right */
.hero-stage .d1,
.hero-stage .d2 {
  clip-path: inset(0 100% 0 0);
  transition: clip-path 780ms var(--e-out);
}
/* The revealed state opens the clip a hair past the box on the block axis.
   The display line-height is 0.92, so the content box is shorter than the
   glyph box: an inset of exactly 0 shaves the cap tops and the descenders
   ("Un pezzo alla volta.") the moment the sweep finishes. */
.hero-stage.on .d1,
.hero-stage.on .d2 { clip-path: inset(-0.12em -0.06em -0.14em 0); }

/* A hairline runs ahead of the headline like a tuft in the flow */
.hero-stage .tuft {
  display: block;
  height: 2px;
  width: clamp(80px, 14vw, 260px);
  background: linear-gradient(90deg, var(--p7), var(--p5), transparent);
  margin-bottom: clamp(.6rem, 1.6vh, 1rem);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 900ms var(--e-out);
}
.hero-stage.on .tuft { transform: scaleX(1); }
.hero-stage .lede { transition-delay: 160ms; }

@media (prefers-reduced-motion: reduce) {
  .hero-stage .d1,
  .hero-stage .d2 { clip-path: none; }
  .hero-stage .tuft { transform: scaleX(1); }

  /* Without the scrub there is nothing to advance the stages, so the script
     switches every one of them on at once. They all occupy grid cell 2/1, so
     left alone they print on top of each other and none of the hero copy can
     be read. Here they become a plain stacked column and the stick grows to
     hold them instead of cropping the top off at 100dvh. */
  .hero { height: auto !important; }
  .hero-stick {
    position: relative;
    height: auto;
    min-height: 100dvh;
    overflow: visible;
  }
  .hero-copy.chor {
    grid-template-rows: auto;
    row-gap: clamp(1.4rem, 3.4vh, 2.4rem);
    padding-top: calc(var(--nav-h) + clamp(2rem, 6vh, 4rem));
    padding-bottom: clamp(5rem, 10vh, 7rem);
  }
  .hero-copy.chor .hero-stage {
    grid-area: auto;
    align-self: start;
  }
  .scroller { display: none; }
}

/* ---------- Particle field: dust carried by the flow ---------- */
#dust,
#pdust {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 3;
}
/* On interior pages the dust is a fixed ambient layer behind everything */
#pdust { position: fixed; z-index: 1; opacity: .5; }

/* ---------- Animated scroll indicator ---------- */
.scroller {
  position: absolute;
  z-index: 6;
  left: var(--gut);
  bottom: clamp(1.4rem, 5vh, 3rem);
  display: flex;
  align-items: center;
  gap: .85rem;
}
.scroller svg { width: 19px; height: 34px; overflow: visible; flex-shrink: 0; }
.scroller .hull { fill: none; stroke: var(--rule-2); stroke-width: 1.4; }
.scroller .bead { fill: var(--p7); animation: bead 2.3s var(--e-soft) infinite; }
.scroller .chev { stroke: var(--p3); stroke-width: 1.4; fill: none; opacity: 0; }
.scroller .chev.c1 { animation: chev 2.3s var(--e-soft) infinite; }
.scroller .chev.c2 { animation: chev 2.3s var(--e-soft) .22s infinite; }
.scroller span {
  font-family: var(--ff-mono);
  font-size: .6rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--vapour);
}
@keyframes bead {
  0%, 8%    { transform: translateY(0);    opacity: 0; }
  22%       { opacity: 1; }
  62%       { transform: translateY(9px);  opacity: 1; }
  80%, 100% { transform: translateY(13px); opacity: 0; }
}
@keyframes chev {
  0%, 20% { opacity: 0;  transform: translateY(-3px); }
  55%     { opacity: .9; transform: translateY(0); }
  100%    { opacity: 0;  transform: translateY(4px); }
}
/* From the desktop breakpoint up, the cue leaves the bottom-left corner and
   stacks above the telemetry on the right rail. That corner is the only place
   the hero copy is allowed to end, so nothing else may claim it: with the cue
   there the copy had to reserve ~99px of clearance, which is exactly what
   floated it back toward the middle of the screen. */
@media (min-width: 900px) {
  .scroller {
    left: auto;
    right: var(--gut);
    bottom: calc(clamp(1.4rem, 5vh, 3rem) + 4.2rem);
    flex-direction: row-reverse;
  }
}
@media (prefers-reduced-motion: reduce) {
  .scroller .bead,
  .scroller .chev { animation: none; opacity: 1; }
  /* The hero block further up already asks for the cue to go away under
     reduced motion, but it does so BEFORE the base .scroller rule, which
     re-declares display:flex at the same specificity and therefore wins.
     Repeating it here — after that rule — is what actually hides it.
     It has to be hidden: under reduced motion the stick becomes a tall
     stacked column, so the cue, still absolute against it, lands on the
     last stage and covers the closing line and its two buttons. */
  .scroller { display: none; }
}

/* ---------- Brand icons: one stroke weight, drawn in the world's grammar ---- */
.ico { width: 32px; height: 32px; flex-shrink: 0; display: block; }
.ico path,
.ico circle,
.ico line,
.ico rect,
.ico polyline {
  fill: none;
  stroke: var(--accent, var(--p3));
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.ico .solid { fill: var(--accent, var(--p3)); stroke: none; }
.ico .faint { stroke: var(--rule-2); }
.plate .ico { margin-bottom: .3rem; }

/* ---------- Layout variants so no two bands read alike ---------- */
@media (min-width: 980px) {
  .split.asym  { grid-template-columns: 1.55fr 1fr; }
  .split.asym2 { grid-template-columns: 1fr 1.55fr; }
}

/* Overlap: the figure runs under a floating card */
.overlap { position: relative; display: grid; }
@media (min-width: 980px) {
  .overlap { grid-template-columns: repeat(12, 1fr); align-items: center; }
  .overlap .frame { grid-column: 1 / 8; grid-row: 1; }
  .overlap .card {
    grid-column: 6 / 13;
    grid-row: 1;
    position: relative;
    z-index: 2;
    background: rgba(13, 19, 27, .94);
    backdrop-filter: blur(16px);
    border: 1px solid var(--rule);
    padding: clamp(1.6rem, 3vw, 2.8rem);
  }
  .overlap.rev .frame { grid-column: 6 / 13; }
  .overlap.rev .card  { grid-column: 1 / 8; }
}
@media (max-width: 979px) {
  .overlap .card {
    background: var(--plenum);
    border: 1px solid var(--rule);
    border-top: 0;
    padding: 1.6rem;
  }
}

/* Full-bleed band with a figure behind the type */
.bleed { position: relative; overflow: hidden; min-height: 64vh; display: grid; align-items: center; }
.bleed > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; }
.bleed::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(90deg, rgba(4,6,10,.96) 0%, rgba(4,6,10,.74) 46%, rgba(4,6,10,.22) 100%);
}
.bleed > .wrap-w { position: relative; z-index: 2; padding-block: clamp(3rem, 7vw, 5rem); }

/* ---------- Infographic: the two payment routes ---------- */
.routes { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule); }
@media (min-width: 860px) { .routes { grid-template-columns: 1fr 1fr; } }
.route { background: var(--tunnel); padding: clamp(1.5rem, 3vw, 2.4rem); display: grid; gap: 1rem; align-content: start; }
.route h3 { font-size: clamp(1.15rem, 2vw, 1.5rem); }
.route p { font-size: .93rem; color: var(--mist); }
.rbar { display: grid; gap: .5rem; margin-top: .4rem; }
.rbar-row { display: grid; grid-template-columns: 11ch 1fr; gap: .8rem; align-items: center; }
.rbar-row span {
  font-family: var(--ff-mono);
  font-size: .56rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--vapour);
}
.rbar-row i { display: block; height: 8px; background: rgba(150,180,214,.14); position: relative; overflow: hidden; }
.rbar-row i::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 820ms var(--e-out);
}
.rise-on .rbar-row i::after { transform: scaleX(var(--v, 0)); }
.rise-on .rbar-row:nth-child(2) i::after { transition-delay: 110ms; }
.rise-on .rbar-row:nth-child(3) i::after { transition-delay: 220ms; }

/* ---------- Drifting outline headline, parallaxed by scroll ---------- */
.drift { overflow: hidden; padding-block: clamp(1.2rem, 3vw, 2.4rem); }
.drift-in {
  display: flex;
  gap: 2.5rem;
  width: max-content;
  font-family: var(--ff-display);
  font-variation-settings: 'wdth' 120, 'wght' 860;
  font-size: clamp(2.6rem, 11vw, 9rem);
  letter-spacing: -.055em;
  line-height: .9;
  color: transparent;
  -webkit-text-stroke: 1px rgba(235, 241, 247, .42);
  white-space: nowrap;
  will-change: transform;
}
.drift-in em { font-style: normal; color: var(--p7); -webkit-text-stroke: 0; }

/* ---------- Pull quote ---------- */
.pull {
  font-family: var(--ff-display);
  font-variation-settings: 'wdth' 108, 'wght' 760;
  font-size: clamp(1.5rem, 3.4vw, 2.6rem);
  line-height: 1.08;
  letter-spacing: -.035em;
  max-width: 22ch;
  text-wrap: balance;
}
.pull em { font-style: normal; color: var(--p7); }

/* ==========================================================================
   BUILD — the assembly equation, with the real thing at both ends
   ========================================================================== */
.build { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule); position: relative; }
@media (min-width: 980px) { .build { grid-template-columns: 1fr 1.15fr 1fr; } }

.bnode { background: var(--tunnel); display: grid; gap: .9rem; align-content: start; padding: clamp(1.3rem, 2.6vw, 2rem); }
.bnode .ck {
  font-size: .72rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--vapour);
  font-family: var(--ff-display);
  font-variation-settings: 'wdth' 106, 'wght' 700;
}
.bnode strong {
  font-family: var(--ff-display);
  font-variation-settings: 'wdth' 112, 'wght' 800;
  font-size: clamp(1.15rem, 2.1vw, 1.55rem);
  letter-spacing: -.035em; line-height: 1.12;
}
.bnode p { font-size: .9rem; color: var(--mist); }

.bshot {
  position: relative;
  aspect-ratio: 16 / 10;
  background: radial-gradient(68% 62% at 50% 52%, #131A23 0%, var(--void) 100%);
  overflow: hidden;
  border: 1px solid var(--rule);
}
.bshot img { width: 100%; height: 100%; object-fit: contain; }

/* The operators ride the seams instead of occupying their own columns */
.bop {
  position: absolute; top: 50%; z-index: 3;
  width: 34px; height: 34px;
  display: grid; place-items: center;
  transform: translate(-50%, -50%);
  background: var(--void);
  border: 1px solid var(--rule-2);
  border-radius: 50%;
  font-family: var(--ff-display);
  font-variation-settings: 'wdth' 116, 'wght' 850;
  font-size: 1.05rem;
  color: var(--p7);
  line-height: 1;
}
@media (min-width: 980px) {
  .bop.a { left: calc(100% / 3.15); }
  .bop.b { left: calc(100% - 100% / 3.15); }
}
@media (max-width: 979px) {
  .bop { position: static; transform: none; justify-self: center; margin: -.4rem 0; }
}

.bopts { display: grid; gap: .1rem; }
.bopt {
  display: grid; grid-template-columns: 26px 1fr; gap: .8rem; align-items: center;
  padding: .62rem 0;
  border-bottom: 1px solid var(--rule);
  font-size: .92rem; color: var(--mist);
}
.bopt:last-child { border-bottom: 0; }
.bopt svg { width: 26px; height: 26px; }
.bopt svg * { fill: none; stroke: var(--accent, var(--p3)); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }

/* The hero backdrop's blur now lives with the rest of its filter chain, in
   fs-comp.css — see the note on .phero-fig img there. Declaring it here as
   well only looked like it worked: filter is a single property, so the later
   file replaced this value outright. */

/* A bleed band whose photograph has not been supplied yet: the placeholder
   takes the image's place exactly, behind the same scrim and pull quote. */
.bleed > .ph {
  position: absolute; inset: 0; z-index: 0;
  aspect-ratio: auto; border: 0; border-radius: 0;
}
.bleed > .ph ~ .wrap-w { z-index: 2; }
