/* ==========================================================================
   ThundersFlag — film layer

   The site is nearly all flat #050505. Flat black reads as "screen"; film reads
   as "footage". This file adds what a camera would have left behind — grain,
   a darkened frame edge, light moving across metal — without touching a single
   layout rule. Remove the stylesheet and the site is exactly as it was.

   Everything here is fixed, pointer-transparent and paint-only. Nothing in this
   file can change the position of anything on the page.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Grain and vignette
   One element, two layers, above the page and below the header.
   -------------------------------------------------------------------------- */
/* Over the content, below the header.

   Underneath was tried first, because grain arguably belongs in the image rather than on
   the glass. Measured across six scroll positions it reached 5-45% of the pixels and the
   vignette all but vanished: every section with a background of its own covered it. Over
   the content it reaches 72-80% at every position, which is the whole frame minus the
   header.

   It is free either way. Five scroll runs on a real GPU: 95th-percentile frame time
   16.8 ms with the layer, 16.8 ms without. (An earlier measurement said the overlay cost
   17 ms; that browser had fallen back to software rendering, where compositing a
   full-viewport translucent surface genuinely is expensive. On a GPU it is not.)

   The header and the skip link stay above it, so navigation is never filmed. */
.film {
    position: fixed;
    inset: 0;
    z-index: 40;
    pointer-events: none;
    contain: strict;
}

/* Fractal noise generated by the browser: no request, no bytes on the wire, and
   it stays sharp at any density because it is resolution-independent. The tile is
   260px so the repeat never becomes a visible pattern. */
.film__grain {
    position: absolute;
    inset: -120px;              /* room to jitter without exposing an edge */
    opacity: var(--grain-opacity);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='260' height='260' filter='url(%23n)'/%3E%3C/svg%3E");
    background-size: 260px 260px;
    /* Its own compositor layer. This one earns permanent promotion: it is a full-viewport
       texture that moves on every frame the page is visible, so without a layer of its own
       the browser repaints the whole viewport instead of transforming a cached one. */
    will-change: transform;
}

/* The frame edge. Gold sits in the middle of the screen, so the corners can fall
   away without losing anything. */
.film__vignette {
    position: absolute;
    inset: 0;
    background: radial-gradient(
        ellipse 78% 68% at 50% 46%,
        transparent 0%,
        transparent 52%,
        rgba(0, 0, 0, calc(var(--vignette) * 0.45)) 82%,
        rgba(0, 0, 0, var(--vignette)) 100%
    );
}

/* Film weave: the grain hops between eight positions at 8 fps, the way a projected
   frame never sits perfectly still. Steps, not a smooth tween — a smooth one reads
   as a moving texture instead of a fresh frame. */
@media (prefers-reduced-motion: no-preference) {
    .film__grain {
        animation: film-weave 1s steps(1, end) infinite;
    }
}

@keyframes film-weave {
    0%    { transform: translate3d(0, 0, 0); }
    12.5% { transform: translate3d(-12px, 6px, 0); }
    25%   { transform: translate3d(8px, -10px, 0); }
    37.5% { transform: translate3d(-6px, -14px, 0); }
    50%   { transform: translate3d(14px, 4px, 0); }
    62.5% { transform: translate3d(-10px, 12px, 0); }
    75%   { transform: translate3d(4px, -6px, 0); }
    87.5% { transform: translate3d(-14px, -4px, 0); }
}

/* --------------------------------------------------------------------------
   Ambient loops stop when nobody is watching
   Marked with [data-loop]; main.js toggles .is-offscreen per element and
   .is-tab-hidden on the document. An animation nobody can see is a thread
   nobody should be paying for.
   -------------------------------------------------------------------------- */
.is-tab-hidden [data-loop],
.is-tab-hidden [data-loop] *,
[data-loop].is-offscreen,
[data-loop].is-offscreen * {
    animation-play-state: paused;
}

.is-tab-hidden [data-loop]::before,
.is-tab-hidden [data-loop]::after,
[data-loop].is-offscreen::before,
[data-loop].is-offscreen::after {
    animation-play-state: paused;
}

/* --------------------------------------------------------------------------
   Light across metal
   A specular pass over gold type when a section arrives. --sweep is a registered
   <percentage> (variables.css), so it interpolates on the compositor rather than
   re-parsing a gradient string every frame.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
    .sweep {
        position: relative;
        background-image: linear-gradient(
            100deg,
            transparent calc(var(--sweep) - 18%),
            rgba(253, 233, 103, 0.85) var(--sweep),
            transparent calc(var(--sweep) + 18%)
        );
        background-repeat: no-repeat;
        -webkit-background-clip: text;
        background-clip: text;
    }

    .sweep.is-lit {
        animation: light-pass var(--sweep-dur) var(--ease-cine) 1 both;
    }
}

@keyframes light-pass {
    from { --sweep: -40%; }
    to   { --sweep: 140%; }
}

/* --------------------------------------------------------------------------
   Scrim
   Where text sits over artwork. Today the artwork is a gradient; the day a real
   photograph is uploaded it goes behind this and the text stays readable without
   anyone revisiting the type. This is the hook the photo-ready slots hang on.
   -------------------------------------------------------------------------- */
.scrim {
    position: relative;
    isolation: isolate;
}

.scrim > * {
    position: relative;
    z-index: 1;
}

.scrim::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
    background: var(--overlay-left);
    pointer-events: none;
}

.scrim--bottom::after {
    background: var(--overlay-bottom);
}

/* --------------------------------------------------------------------------
   Hero title sequence (components/hero-bolt.php)

   Drawn, not filmed. Sizing follows the same container maths the video hero uses
   in responsive.css, so the mark lands where the video's did — opposite the
   headline, not behind it.

   The resting state below is the finished state: crest visible, bolt spent. Nothing
   here depends on JavaScript to become readable, so a blocked script leaves a lit
   crest rather than an empty box.
   -------------------------------------------------------------------------- */
.hero-bolt {
    position: absolute;
    inset: 0;
    overflow: hidden;
    isolation: isolate;
}

/* The charge gathering before the strike, and the afterglow once it has passed. */
/* Two lights, not one wash.

   The first pass put a wide gold radial behind the mark and a second brown one under it;
   together they read as a mustard stain rather than light. What replaces them is directional:
   a tight cold-white key at the impact point, a narrow warm bloom immediately around it, and
   a cool floodlight from the top left to balance the frame. Nothing broad and nothing brown —
   the darkness between the lights is what makes them look like light. */
.hero-bolt__sky {
    position: absolute;
    inset: 0;
    background:
        /* One key light, small and cold. Every warm radial tried here turned into a stain
           behind the mark: gold on near-black at any width reads as yellow haze, not light.
           The mark is already gold — it does not need the air around it to be gold too. */
        radial-gradient(20% 18% at 75% 31%, rgba(226, 234, 245, 0.16), transparent 70%),
        /* fill from the upper left, cool, so the far side is lit rather than merely less black */
        radial-gradient(62% 66% at 8% 2%, rgba(176, 196, 222, 0.085), transparent 70%),
        /* the floor stays dark: it is where the copy sits */
        linear-gradient(180deg, transparent 50%, rgba(5, 5, 5, 0.6) 100%);
}

/* The second light: a cold beam raking down the far side at the logo's own angle. It is
   what the left half is standing in, and it points the eye across the frame toward the mark. */
.hero-bolt__flood {
    position: absolute;
    top: -30%;
    left: -14%;
    width: 52%;
    height: 130%;
    transform: rotate(var(--slant));
    transform-origin: 50% 0;
    background: linear-gradient(
        168deg,
        rgba(214, 228, 245, 0.11) 0%,
        rgba(176, 196, 222, 0.05) 42%,
        transparent 76%
    );
    -webkit-mask-image: radial-gradient(70% 88% at 34% 0%, #000 0%, rgba(0, 0, 0, 0.5) 52%, transparent 86%);
    mask-image: radial-gradient(70% 88% at 34% 0%, #000 0%, rgba(0, 0, 0, 0.5) 52%, transparent 86%);
}

/* The box the strike falls through: from the top of the hero down to the mark. The bolt
   is anchored to the bottom of this box (xMidYMax), so the box height is what decides
   where the tip lands. 46% puts it in the upper third of the crest — the mark then reads
   as being struck rather than pointed at. */
.hero-bolt__strike {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: var(--strike-h, 46%);
    overflow: visible;
}

/* The glow is two drop-shadows on the whole SVG rather than extra paths: one tight and
   near-white for the core, one wide and gold for the air around it. Without them the
   channel is a hairline and reads as a drawn line instead of a discharge. */
.hero-bolt__strike {
    filter:
        drop-shadow(0 0 5px rgba(255, 252, 240, 0.95))
        drop-shadow(0 0 18px rgba(248, 185, 69, 0.65))
        drop-shadow(0 0 44px rgba(248, 185, 69, 0.35));
}

.hero-bolt__path {
    fill: none;
    stroke: url(#bolt-core);
    stroke-width: 6;
    stroke-linecap: round;
    stroke-linejoin: round;
    opacity: 0;
}

.hero-bolt__path--fork {
    stroke-width: 3.5;
}

/* Impact. Sits above the bolt and below the crest, so the mark appears to be revealed
   by the light rather than lit from the front. */
.hero-bolt__flash {
    position: absolute;
    inset: 0;
    background: radial-gradient(48% 40% at 58% 34%, rgba(255, 248, 225, 0.9), rgba(248, 185, 69, 0.25) 45%, transparent 72%);
    opacity: 0;
}

/* Phones: the mark lives in the top band, above the headline, the way the video hero
   already framed it. Centred it would sit behind the copy — there is not enough height
   on a phone for both. The strike shortens to match so its tip still lands on the mark. */
.hero-bolt {
    --strike-h: 22%;
}

.hero-bolt__crest {
    position: absolute;
    inset: 0;
    display: grid;
    justify-items: center;
    align-items: start;
    /* Clear of the fixed header: centred on the band, the mark's crown sits behind it. */
    padding-top: calc(var(--header-h) + 2vh);
}

.hero-bolt__mark {
    width: clamp(170px, 46vw, 240px);
    height: auto;
    filter: drop-shadow(0 18px 40px rgba(0, 0, 0, 0.85));
}

.hero-bolt__plate {
    position: relative;
    display: inline-block;
    line-height: 0;
}

/* The specular pass across the metal: a brightened copy of the mark, shown only through
   a narrow moving band. Masking the copy rather than overlaying a gradient is what keeps
   the light on the mark instead of on the rectangle around it. */
.hero-bolt__shine {
    position: absolute;
    inset: 0;
    opacity: 0;
    filter: brightness(2.8) saturate(0.35);
    mix-blend-mode: screen;
    -webkit-mask-image: linear-gradient(
        104deg,
        transparent calc(var(--sweep) - 9%),
        #000 var(--sweep),
        transparent calc(var(--sweep) + 9%)
    );
    mask-image: linear-gradient(
        104deg,
        transparent calc(var(--sweep) - 9%),
        #000 var(--sweep),
        transparent calc(var(--sweep) + 9%)
    );
}

.hero-bolt__shine .hero-bolt__mark {
    filter: none;
}

/* Anchored to the impact point, which is the bottom edge of the strike box. */
.hero-bolt__sparks {
    position: absolute;
    top: var(--strike-h, 46%);
    left: 0;
    width: 100%;
    height: 0;
}

.hero-bolt__spark {
    position: absolute;
    top: 0;
    left: 50%;
    width: 3px;
    height: 3px;
    background: var(--gold-light);
    box-shadow: 0 0 6px rgba(253, 233, 103, 0.9);
    opacity: 0;
}

@media (min-width: 900px) {
    .hero-bolt__mark {
        width: clamp(280px, 26vw, 460px);
    }
}

/* No motion: the sequence never plays, so the sky and the mark are simply there. The
   bolt and the flash stay at opacity 0 — a static lightning bolt is a scribble. */
@media (prefers-reduced-motion: reduce) {
    .hero-bolt__path,
    .hero-bolt__flash,
    .hero-bolt__shine,
    .hero-bolt__spark {
        opacity: 0 !important;
    }
}

/* The drawn hero fills the frame rather than sitting in the band the video occupies:
   there is no clip to letterbox, and the strike needs the full height to fall through.
   The mask stays on mobile so the sequence dissolves into the copy below it, exactly
   where the video hero already fades out. */
.hero--bolt .hero__media {
    inset: 0;
    height: auto;
    -webkit-mask-image: linear-gradient(180deg, #000 52%, transparent 96%);
    mask-image: linear-gradient(180deg, #000 52%, transparent 96%);
}

@media (min-width: 900px) {
    /* Desktop: the mark takes the right-hand half, opposite the headline — the same
       place the video put it, so the hero's balance is unchanged. */
    .hero--bolt .hero__media {
        -webkit-mask-image: none;
        mask-image: none;
    }

    .hero-bolt {
        --strike-h: 46%;
    }

    /* Desktop has the height to centre the mark opposite the headline. */
    .hero--bolt .hero-bolt__crest {
        left: 50%;
        align-items: center;
        padding-top: 0;
    }

    .hero--bolt .hero-bolt__sparks {
        left: 50%;
        width: 50%;
    }

    /* The impact sits at the centre of the right-hand half, which is three quarters of
       the way across the hero — not the middle, where the headline is. */
    .hero--bolt .hero-bolt__rays {
        left: 75%;
    }

    /* Width as well as offset: left alone, a 100%-wide box starting at the midpoint runs
       off the side of the screen and takes the strike with it. */
    .hero--bolt .hero-bolt__strike {
        left: 50%;
        width: 50%;
    }

    .hero--bolt .hero-bolt__crest {
        padding-bottom: 6vh;
    }
}

/* --------------------------------------------------------------------------
   What keeps the hero alive after the strike

   Three slow layers. Every cycle here runs between 7 and 16 seconds and sits at
   low opacity: a hero you notice moving is a hero you stop reading. They all park
   when the section leaves the screen, because .hero carries [data-loop].
   -------------------------------------------------------------------------- */

/* Light thrown from the point of impact. transform-origin is the impact point, so
   the shafts fan from where the bolt landed rather than from the middle of the box. */
.hero-bolt__rays {
    position: absolute;
    top: var(--strike-h, 46%);
    left: 50%;
    width: 0;
    height: 0;
}

.hero-bolt__rays span {
    position: absolute;
    bottom: 0;
    left: 0;
    width: clamp(90px, 14vw, 200px);
    height: clamp(280px, 46vh, 560px);
    margin-left: calc(clamp(90px, 14vw, 200px) / -2);
    transform-origin: 50% 100%;
    /* The gradient IS the shape. Masking a linear gradient left a visible boundary where the
       mask ran out, which is what turned these into grey polygons behind the mark instead of
       light. A radial anchored at the impact point has no boundary to find: it is brightest
       where the bolt landed and gone before it reaches an edge. */
    background: radial-gradient(
        ellipse 34% 82% at 50% 100%,
        rgba(226, 236, 250, 0.13) 0%,
        rgba(214, 228, 245, 0.05) 42%,
        transparent 76%
    );
    opacity: 0;
}

/* Embers that never stop. Positioned by JS so they scatter rather than stack. */
.hero-bolt__embers {
    position: absolute;
    inset: 0;
}

/* The glow is the background, not a box-shadow. Eighteen embers with two shadows each
   repainted their halos on every frame and took the 95th percentile from 16.8 ms to 33.3;
   one radial gradient per ember looks the same and does not. */
.hero-bolt__embers span {
    position: absolute;
    width: 11px;
    height: 11px;
    background: radial-gradient(
        circle,
        rgba(255, 252, 240, 0.95) 0%,
        rgba(253, 233, 103, 0.8) 16%,
        rgba(248, 185, 69, 0.3) 40%,
        transparent 70%
    );
    opacity: 0;
}

/* The gridiron sits behind everything and stays faint: it is there to stop the frame
   reading as empty black, not to be looked at. Taller and shallower than the section
   default, because a hero is a wide shot — the horizon belongs further up. */
.hero-bolt .ambient {
    opacity: 1;
}

/* Brighter lines than a section gets: in a hero the field has to carry a whole half of
   the frame, and at the section default it is invisible past the first few yards. */
.hero-bolt .ambient__lines {
    background:
        repeating-linear-gradient(90deg, rgba(245, 241, 232, 0.16) 0 2px, transparent 2px 150px),
        repeating-linear-gradient(90deg, rgba(248, 185, 69, 0.09) 0 1px, transparent 1px 30px),
        repeating-linear-gradient(0deg, rgba(245, 241, 232, 0.07) 0 2px, transparent 2px 120px);
}

/* The field runs the full width and the horizon sits mid-frame, so the empty left half is
   standing on something. Shallower rotation than the section default, because a hero is a
   wide shot: the lines should recede across the frame, not pile up at the bottom edge. */
.hero-bolt .ambient__plane {
    height: 74%;
    bottom: -6%;
    transform: perspective(1100px) rotateX(59deg);
    -webkit-mask-image: linear-gradient(0deg, #000 4%, rgba(0, 0, 0, 0.45) 46%, transparent 82%);
    mask-image: linear-gradient(0deg, #000 4%, rgba(0, 0, 0, 0.45) 46%, transparent 82%);
}

@media (prefers-reduced-motion: reduce) {
    .hero-bolt__rays,
    .hero-bolt__embers {
        display: none;
    }
}

/* --------------------------------------------------------------------------
   Playbook (components/hero-playbook.php)

   A coach's board laid over the field. It has to be legible — a diagram nobody can
   read is decoration pretending to be content — so it carries more contrast than a
   watermark would, and the headline sits over it rather than in front of it.

   Resting state is fully drawn, so with no JavaScript the play is simply there.
   -------------------------------------------------------------------------- */
.hero-play {
    position: absolute;
    inset: auto 0 0;
    height: 66%;
    pointer-events: none;
}

.hero-play__svg {
    width: 100%;
    height: 100%;
    overflow: visible;
}

.hero-play__los {
    stroke: rgba(245, 241, 232, 0.3);
    stroke-width: 1.5;
    stroke-dasharray: 9 8;
}

.hero-play__hash {
    stroke: rgba(245, 241, 232, 0.26);
    stroke-width: 2;
}

.hero-play__route {
    fill: none;
    stroke: rgba(245, 241, 232, 0.58);
    stroke-width: 2.6;
    stroke-linecap: round;
    stroke-linejoin: round;
    transition: stroke var(--dur) ease, stroke-width var(--dur) ease;
}

.hero-play__arrow path {
    fill: rgba(245, 241, 232, 0.58);
}

/* Only ever one at a time: a board where everything is lit is a board where nothing
   is being pointed at. */
.hero-play__route.is-live {
    stroke: var(--gold);
    stroke-width: 3.6;
}

/* The snap and the throw: dashed, because neither is a route anybody runs. */
.hero-play__dash {
    fill: none;
    stroke: rgba(248, 185, 69, 0.42);
    stroke-width: 2;
    stroke-dasharray: 7 7;
    stroke-linecap: round;
}

.hero-play__man circle {
    fill: rgba(5, 5, 5, 0.72);
    stroke: rgba(245, 241, 232, 0.62);
    stroke-width: 2;
}

.hero-play__man text {
    fill: rgba(245, 241, 232, 0.82);
    font-family: var(--font-display);
    font-weight: 800;
    font-size: 15px;
    letter-spacing: 0.04em;
    text-anchor: middle;
}

.hero-play__man--qb circle {
    stroke: var(--gold);
}

.hero-play__man--qb text {
    fill: var(--gold);
    font-size: 13px;
}

.hero-play__ball {
    fill: var(--gold-light);
    opacity: 0;
}

/* Phones: the board would sit under the copy, and there is no pointer to read it with.
   The field lines carry that half of the frame on their own. */
@media (max-width: 899px) {
    .hero-play {
        display: none;
    }
}

@media (min-width: 900px) {
    /* Upper left: the one part of the frame nothing else is using. The formation grazes the
       bottom of the headline, where the type is large enough that a line diagram behind it
       reads as depth; the deep routes run up into open sky. The lead paragraph and the
       buttons stay clear of it entirely, because a diagram is not worth a sentence. */
    .hero-play {
        right: 42%;
        left: -1%;
        top: 8%;
        bottom: 47%;
        height: auto;
    }
}
