/* =========================================================
   Header entrance — the one choreography, for every header
   on the site.

   Loaded on EVERY page from includes/head.php, last, so its
   rules win same-specificity ties against a page sheet.

   ── What this is ──────────────────────────────────────────
   Every page opens the same way: the eyebrow lifts, the
   title's words rise one after another from behind their own
   masks, the red accent rule wipes out from the left, then
   the supporting copy and the CTAs settle in. The title
   carries the move; everything else only gets out of its way.

   That was written once for the home hero (home-hero.css)
   and ported to the shared photo hero (page-hero.css). This
   file is where the REST of the site's header families join
   it, so the rhythm is identical whether a page opens on a
   photo plate, an editorial split lockup, a portal title, a
   service hero, an inventory band, an article hero or one of
   the legacy centred lockups.

   Three families are deliberately NOT here, because they
   already shipped their own entrance and re-animating them
   would double up:
     · .header--home            home-hero.css
     · .page-hero--animated     page-hero.css
     · .md-hero                 models-detail.css  (race-in)
     · .models-header-title     models-bg.css      (logo + tagline)
     · .service-flow-heading    service-flow-header.css
   Each is excluded by selector below where it could collide.

   ── The two rules that make this safe to ship ─────────────
   They are home-hero.css's, and they hold here for the same
   reasons:

   ① No element carries a hidden state of its own. The hidden
      state exists ONLY inside each animation's `from`
      keyframe, held there by `animation-fill-mode: both`, so
      anything that stops the animation — a reduced-motion
      preference, a stylesheet that fails to load, a browser
      that does not know the keyframes — lands the element on
      its FINISHED styles. Text can never be left invisible.
   ② Only `transform` and `opacity` animate, so the whole
      sequence stays on the compositor and nothing reflows.

   ── The scale ─────────────────────────────────────────────
   One curve (the announcement bar's --anno-ease), one step
   between beats, the whole thing over in ~1.3s. The beat
   tokens below are the SAME numbers page-hero.css hard-codes;
   they are named here so a family with different furniture
   (a breadcrumb, a price panel, a brand rail) can land on an
   existing beat instead of inventing one.
   ========================================================= */

:root {
    --tv-enter-ease: cubic-bezier(0.22, 1, 0.36, 1);

    /* Durations */
    --tv-enter-dur: 0.75s;
    /* The title's words travel further, so they take longer. */
    --tv-enter-rise-dur: 0.9s;
    --tv-enter-wipe-dur: 0.7s;

    /* Beats. b1 leads, the title runs from --tv-enter-lead-in in
       --tv-enter-step increments, and b2…b6 pick up after it. */
    --tv-enter-b1: 0.05s;   /* eyebrow · breadcrumb · status pills · back chip */
    --tv-enter-lead-in: 0.18s;
    --tv-enter-step: 0.11s;
    --tv-enter-b2: 0.62s;   /* the accent rule's wipe */
    --tv-enter-b3: 0.78s;   /* lead · dek · intro · subtitle */
    --tv-enter-b4: 0.84s;   /* note · extra · byline */
    --tv-enter-b5: 0.9s;    /* CTA row · price panel */
    --tv-enter-b6: 0.98s;   /* trailing hero furniture (brand rail, stat bar) */
}

/* ---------------------------------------------------------
   Keyframes
   --------------------------------------------------------- */

/* 110%, not 100%: the extra tenth covers the clip box's padding slack below,
   so a word is fully clear of its mask before it starts to travel. */
@keyframes tv-enter-rise {
    from {
        transform: translate3d(0, 110%, 0);
    }

    to {
        transform: translate3d(0, 0, 0);
    }
}

@keyframes tv-enter-lift {
    from {
        opacity: 0;
        transform: translate3d(0, 0.9rem, 0);
    }

    to {
        opacity: 1;
        transform: translate3d(0, 0, 0);
    }
}

@keyframes tv-enter-wipe {
    from {
        transform: scaleX(0);
    }

    to {
        transform: scaleX(1);
    }
}

/* ---------------------------------------------------------
   The title's words

   Emitted by tv_hero_words() (includes/hero-words.php) — the
   one splitter, so no header hand-writes these spans.
   .page-hero keeps its own pair of class names (page-hero.css,
   already shipped) but calls the same function.
   --------------------------------------------------------- */

/* Both halves of the word take their type back from the title, explicitly.
   This is not defensive tidiness — it is load-bearing. Splitting a title into
   <span>s drops those spans into the blast radius of every legacy rule that
   styles a bare span, and the site has several. The one that bit is
   m072224.css's `.header-title span { font-size: 2rem; letter-spacing: .05rem;
   line-height: 3rem; color: var(--c4) }` — the legacy subtitle rule — which
   reached straight into the models lineup title and rendered "Freedom by
   Design" at 32px grey instead of ~79px white. `font` is the shorthand, so it
   hands back family, size, weight, style and line-height in one; the three
   properties it does not cover are restated beside it.

   The OUTER box needs this as much as the inner one: its clip padding is sized
   in `em`, so a stolen font-size silently resizes the mask too.

   The class is DOUBLED — (0,2,0) — because `.header-title span` is (0,1,1) and
   would out-specify a single class no matter how late this file loads. Loading
   order alone is not enough against a type selector; this is the same doubling
   .header.page-hero uses in page-hero.css, for the same reason. */
.tv-hero-word.tv-hero-word,
.tv-hero-word-in.tv-hero-word-in {
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;

    /* Colour is called out separately because the offender there is the legacy
       global `* { color: var(--c3) }` in m072224.css, which sets a colour on
       every bare <span> DIRECTLY — so the words would render #b6b6b6 grey
       inside a white title. `inherit` at (0,1,0) beats the universal selector
       and hands each word back its own title's colour, which is why this is
       `inherit` and not a hard-coded white: these spans run inside eight
       different titles. (page-hero.css carries the same line on
       .page-hero__word-in for the same reason.) */
    color: inherit;
}

/* Doubled for the same reason as the type reset above: `.header-title span`
   also sets `text-align`, and the legacy `* { margin: 0; padding: 0 }` would
   otherwise be the only thing this had to beat. */
.tv-hero-word.tv-hero-word {
    display: inline-block;
    overflow: hidden;

    /* The clip box needs slack the line box does not: at the hero ramp's
       line-height 1.02 the inline box is shorter than the capitals, so a flush
       `overflow: hidden` shaves the tops of the letters. The padding buys that
       slack; the matching negative margin hands it straight back, so the MARGIN
       box — which is what the line box measures — stays exactly the size it
       was, and no title reflows by so much as a pixel. */
    padding: 0.16em 0.04em 0.12em;
    margin: -0.16em -0.04em -0.12em;

    /* An inline-block with clipped overflow takes its bottom margin edge as its
       baseline, which would ride the words up off the line. Aligning by the top
       edge instead keeps every word, and every line, on one grid. */
    vertical-align: top;
}

.tv-hero-word-in {
    display: inline-block;

    animation: tv-enter-rise var(--tv-enter-rise-dur) var(--tv-enter-ease) both;
    animation-delay: calc(var(--i, 0) * var(--tv-enter-step) + var(--tv-enter-lead-in));
}

/* ---------------------------------------------------------
   1 · Shared page-head lockup — .support-head
       split  : Blog, Contact, Events, FAQ, Financing (×2),
                Owner's Manual, Video Tutorials (×2),
                Careers Apply, Contact Service, Production
                Progress
       portal : the thirteen account pages
   Markup: includes/support-header.php · layout: page-head.css
   (split) and support-header.css (portal).
   --------------------------------------------------------- */

.support-head--split .support-head__back,
.support-head--portal .support-head__back,
.support-head--split .support-head__eyebrow,
.support-head--portal .support-head__eyebrow {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-b1) both;
}

/* Fallback for a title the splitter did not reach (a caller passing markup, a
   future variant): it lifts as a unit rather than sitting still while the rest
   of the lockup moves around it. Where the words ARE split, they own the move
   and this must not run — hence the :has() guard. */
.support-head--split .support-head__title:not(:has(.tv-hero-word)),
.support-head--portal .support-head__title:not(:has(.tv-hero-word)) {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-lead-in) both;
}

/* Both variants re-anchor this rule to the left edge (page-head.css /
   support-header.css), so it wipes from the title's first letter. The base
   centred .support-head is deliberately NOT included: it centres its rule with
   a translateX, which a scaleX animation would clobber. */
.support-head--split .support-head__title::after,
.support-head--portal .support-head__title::after {
    transform-origin: 0 50%;
    animation: tv-enter-wipe var(--tv-enter-wipe-dur) var(--tv-enter-ease) var(--tv-enter-b2) both;
}

.support-head--split .support-head__lead,
.support-head--portal .support-head__lead {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-b3) both;
}

.support-head--split .support-head__extra,
.support-head--portal .support-head__extra {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-b4) both;
}

/* ---------------------------------------------------------
   2 · Service hero — service/index.php and buzz.php
   Layout: header-hero.css
   --------------------------------------------------------- */

.service-hero__eyebrow {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-b1) both;
}

.service-hero__title:not(:has(.tv-hero-word)) {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-lead-in) both;
}

.service-hero__title::after {
    transform-origin: 0 50%;
    animation: tv-enter-wipe var(--tv-enter-wipe-dur) var(--tv-enter-ease) var(--tv-enter-b2) both;
}

.service-hero__intro {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-b3) both;
}

.service-hero__actions {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-b5) both;
}

/* The furniture below the CTA row — the support line, the brand rail and the
   stat bar — lands last, as one group, so the hero settles top to bottom. */
.service-hero__support,
.service-hero__brands,
.service-hero__why {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-b6) both;
}

/* ---------------------------------------------------------
   3 · Vans-for-Sale — the two listing bands and every
       detail page's hero
   Markup: vans-for-sale.php / used-vans-for-sale.php and
   includes/vfs-header.php · layout: vfs-listing.css and
   vans-for-sale.css
   --------------------------------------------------------- */

.vfs-hero__eyebrow,
.vfs-hero__pills {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-b1) both;
}

.vfs-hero__title:not(:has(.tv-hero-word)) {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-lead-in) both;
}

.vfs-hero__title::after {
    transform-origin: 0 50%;
    animation: tv-enter-wipe var(--tv-enter-wipe-dur) var(--tv-enter-ease) var(--tv-enter-b2) both;
}

.vfs-hero__lead,
.vfs-hero__sub {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-b3) both;
}

/* The listing pages' CTA row and the detail page's price/facts panel are the
   same beat: each is the thing the visitor acts on. */
.vfs-hero__actions,
.vfs-hero__panel {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-b5) both;
}

/* ---------------------------------------------------------
   4 · Blog article hero — includes/blog-post-template.php
   Layout: blog-article.css
   --------------------------------------------------------- */

.bp-crumbs {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-b1) both;
}

.bp-hero__title:not(:has(.tv-hero-word)) {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-lead-in) both;
}

.bp-hero__dek {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-b3) both;
}

.bp-hero__meta {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-b4) both;
}

/* ---------------------------------------------------------
   5 · Models lineup hero — models.php
   The site's third copy of the shared hero lockup (the other
   two, home and .page-hero, animate already). It renders
   through the legacy .header-title box, so it is excluded
   from the legacy cascade in §6 and takes the full
   choreography here instead.
   Layout: models-bg.css
   --------------------------------------------------------- */

.models-hero__eyebrow {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-b1) both;
}

.models-header-img .header-title h1:not(:has(.tv-hero-word)) {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-lead-in) both;
}

.models-header-img .header-title h1::after {
    transform-origin: 0 50%;
    animation: tv-enter-wipe var(--tv-enter-wipe-dur) var(--tv-enter-ease) var(--tv-enter-b2) both;
}

.models-hero__lead {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) var(--tv-enter-b3) both;
}

/* ---------------------------------------------------------
   6 · The legacy centred lockup — .header > .header-title
       (~45 pages: the eighteen service detail pages, the
       seventeen Build & Price iframe pages and their thank-
       yous, the Buy-this-Van contact pages, Resources,
       Financing (Hearth), Service Thanks, Join Newsletter,
       Newsletter Thanks, and the four Sprinter/Transit
       chassis pages)

   These carry hand-written markup — an <h1>, a <span>
   subtitle and sometimes a <button> — with no component to
   route a word-splitter through. Rather than edit sixty
   files, the cascade is applied to the box's children by
   position, on the SAME curve and duration as everything
   above: the title leads, the subtitle follows once it has
   landed, a CTA lands last.

   The box has two names. Most of these pages call it
   .header-title; the seventeen Build & Price iframe pages and
   their thank-yous call it .title (an <h1> plus a row of
   chassis buttons). Both are matched, and only as a DIRECT
   child of a page header, so neither name can reach a `.title`
   somewhere in the body.

   Two headers use .header-title and are excluded because they
   already animate (the lineup pages' logo + tagline) or take
   the richer treatment in §5 (models.php).
   --------------------------------------------------------- */

.header:not(.models-header-img) > .header-title:not(.models-header-title) > *,
.header > .title > * {
    animation: tv-enter-lift var(--tv-enter-dur) var(--tv-enter-ease) 0.12s both;
}

.header:not(.models-header-img) > .header-title:not(.models-header-title) > *:nth-child(2),
.header > .title > *:nth-child(2) {
    animation-delay: 0.42s;
}

.header:not(.models-header-img) > .header-title:not(.models-header-title) > *:nth-child(3),
.header > .title > *:nth-child(3) {
    animation-delay: 0.58s;
}

/* Everything past the third lands together — the Build & Price headers carry
   four chassis buttons in a row, and staggering them individually would read as
   a queue rather than one control. */
.header:not(.models-header-img) > .header-title:not(.models-header-title) > *:nth-child(n + 4),
.header > .title > *:nth-child(n + 4) {
    animation-delay: 0.72s;
}

/* ---------------------------------------------------------
   Reduced motion

   `animation: none` is all that is needed: with every hidden
   state living only inside a keyframe (see the note at the
   top), dropping the animation lands each element on its
   finished styles. Matches the reduced-motion blocks at the
   foot of home-hero.css and page-hero.css.
   --------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
    .tv-hero-word-in,

    .support-head--split .support-head__back,
    .support-head--portal .support-head__back,
    .support-head--split .support-head__eyebrow,
    .support-head--portal .support-head__eyebrow,
    .support-head--split .support-head__title:not(:has(.tv-hero-word)),
    .support-head--portal .support-head__title:not(:has(.tv-hero-word)),
    .support-head--split .support-head__title::after,
    .support-head--portal .support-head__title::after,
    .support-head--split .support-head__lead,
    .support-head--portal .support-head__lead,
    .support-head--split .support-head__extra,
    .support-head--portal .support-head__extra,

    .service-hero__eyebrow,
    .service-hero__title:not(:has(.tv-hero-word)),
    .service-hero__title::after,
    .service-hero__intro,
    .service-hero__actions,
    .service-hero__support,
    .service-hero__brands,
    .service-hero__why,

    .vfs-hero__eyebrow,
    .vfs-hero__pills,
    .vfs-hero__title:not(:has(.tv-hero-word)),
    .vfs-hero__title::after,
    .vfs-hero__lead,
    .vfs-hero__sub,
    .vfs-hero__actions,
    .vfs-hero__panel,

    .bp-crumbs,
    .bp-hero__title:not(:has(.tv-hero-word)),
    .bp-hero__dek,
    .bp-hero__meta,

    .models-hero__eyebrow,
    .models-header-img .header-title h1:not(:has(.tv-hero-word)),
    .models-header-img .header-title h1::after,
    .models-hero__lead,

    .header:not(.models-header-img) > .header-title:not(.models-header-title) > *,
    .header > .title > * {
        animation: none;
    }
}
