/* ============================================================
   GOOD UNICORN — HOME HERO
   The hero section from the 2026 design, ported from the showcase
   build. Everything here is scoped to `.hero` (or to the fixed GL
   canvas the hero owns) so it cannot reach any other section of
   the homepage. Loaded AFTER the page's own styles, so these win
   on the hero without any !important.
   ============================================================ */

.hero {
    --hero-sans: "Red Hat Display", system-ui, -apple-system, "Segoe UI", sans-serif;
    --hero-text: "Inter Tight", system-ui, -apple-system, "Segoe UI", sans-serif;
    /* The design's display grey — a step down from paper white. */
    --hero-ink: #E4E4E4;
    /* 115px in the design's 1680 frame = 6.85vw. The vh term keeps the whole
       composition inside one viewport on short/wide screens. */
    --hero-h1: clamp(44px, min(6.85vw, 12vh), 115px);
    /* The sticky header's measured box, so `align-content: center` divides
       the REMAINING height evenly rather than centring behind the header. */
    --hero-hd: 86px;
}

/* ---- the act ---------------------------------------------------------
   The homepage hero was a flex column with its own padding; the design
   centres a single content block in the band under the header.

   The container and the dial share ONE grid cell. That is what aligns the
   dial to the bottom-right of the copy block without absolute positioning
   — and absolute positioning is not available here: the dial has to stay a
   direct child of .hero. Nested inside .container (z-index 2) it would be
   trapped in that stacking context at 2, below the fullscreen overlay at
   990, and `#video-trigger.active { z-index: 991 }` could no longer lift it
   to act as the close button. */
.hero {
    min-height: 100svh;
    display: grid;
    grid-template-columns: 1fr;
    align-content: center;
    padding: var(--hero-hd) 0 0;
    overflow: clip;
}
.hero > .container { grid-area: 1 / 1; }
.hero > #video-trigger {
    grid-area: 1 / 1;
    justify-self: end;
    align-self: end;
    /* The grid cell is the full hero width, so `justify-self: end` alone puts
       the dial on the VIEWPORT edge. Mirror .container's own gutter — its
       auto margins plus its padding — so it lands on the copy column's right
       edge instead, which is where the design has it. */
    margin-right: calc(max(0px, (100% - var(--container)) / 2) + var(--space-5));
}
@media (min-width: 768px) {
    .hero > #video-trigger {
        margin-right: calc(max(0px, (100% - var(--container)) / 2) + var(--space-7));
    }
}

/* ---- WebGL contract --------------------------------------------------
   home-hero.js adds html.gl-on only once a frame can actually render.
   Until then (and again the moment the context is lost) the hero keeps
   the shipped CSS background, swirl and all. When the particles ARE the
   mark, the hero goes transparent and the dark field moves onto the
   canvas itself — the GL clear colour is transparent, so a CSS
   background on the canvas paints behind the points. */
/* z-index MUST be negative here, and this is the one line that differs
   from the showcase. There every act is a positioned, sticky box, so a
   canvas at z-index 0 sits below them. This page's sections are static,
   and painting order puts a positioned z-index:0 box ABOVE static
   in-flow content — so the canvas's dark field covered the light section
   under the hero for a whole viewport of scrolling, until the hero's
   IntersectionObserver finally hid it. Negative z-index puts it behind
   page content while still above the root background, which is exactly
   where a backdrop belongs. */
.gl-canvas {
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
}
/* The showcase hero's ground, rebuilt. Two things were wrong in the first
   pass: the base was #0A0020 where the showcase uses #0E0124, and a 95%
   left scrim (inherited from the old hero art) crushed that side to black.
   Underneath its particles the showcase also runs an "aurora" — six
   additively-blended orbs drifting on a 2D canvas — and without it the
   field reads flat and the particles read hot against it.
   These are those six orbs, same colours, same centres, same radii (the
   canvas sizes them off min(w,h), which is what vmin is), as static CSS.
   A second full-viewport animated canvas is not worth its paint cost on
   the public homepage; the drift is imperceptible at 0.0002/frame anyway.
   Alphas are the canvas's a*0.5 centre stops, nudged up where stacking
   source-over loses against `lighter`. */
html.gl-on .gl-canvas {
    background:
        radial-gradient(circle 55vmin at 72% 18%, rgba(180, 155, 240, 0.28), rgba(180, 155, 240, 0) 100%),
        radial-gradient(circle 42vmin at 60% 62%, rgba(214, 107, 213, 0.19), rgba(214, 107, 213, 0) 100%),
        radial-gradient(circle 38vmin at 16% 30%, rgba(124, 200, 255, 0.16), rgba(124, 200, 255, 0) 100%),
        radial-gradient(circle 30vmin at 86% 70%, rgba(116, 224, 184, 0.12), rgba(116, 224, 184, 0) 100%),
        radial-gradient(circle 64vmin at 40% 42%, rgba(74, 42, 107, 0.44), rgba(74, 42, 107, 0) 100%),
        radial-gradient(circle 72vmin at 20% 14%, rgba(28, 4, 71, 0.50), rgba(28, 4, 71, 0) 100%),
        radial-gradient(90% 70% at 78% 18%, rgba(180, 155, 240, 0.10), transparent 60%),
        #0E0124;
}
html.gl-on .hero { background: none; }
/* No z-index is added to the hero's children: .hero is positioned with
   z-index:auto and comes after the canvas in DOM order, so its content
   already paints above it. Adding one here would open stacking contexts
   and re-create the trap described above. */

/* ---- type ------------------------------------------------------------
   Red Hat Display Regular 115/120 with -1.75 tracking in a 1138px box —
   all four expressed against the font size so the three-line break holds
   at every step of the clamp (1138/115 = 9.9em, 120/115 = 1.043,
   -1.75/115 = -0.0152em). */
.hero h1 {
    font-family: var(--hero-sans);
    font-size: var(--hero-h1);
    font-weight: 400;
    line-height: 1.043;
    letter-spacing: -0.0152em;
    color: var(--hero-ink);
    max-width: 9.9em;
    margin: 0 0 clamp(40px, 8.8vh, 85px);
    text-wrap: balance;
}

/* The intro line sits ABOVE the button, 28px apart, in a 410px column. */
.hero .hero-foot {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 28px;
}
.hero .hero-sub {
    font-family: var(--hero-text);
    font-size: clamp(16px, 1.15vw, 19px);
    font-weight: 400;
    line-height: 1.3;
    color: var(--hero-ink);
    max-width: 410px;
    margin: 0;
}
.hero .hero-ctas {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 16px;
    margin: 0;
}

/* ---- the button ------------------------------------------------------
   Not the header's capsule: the design draws the hero CTA as a white
   14px-radius block with violet text. Scoped to the hero so .btn stays
   exactly as it is everywhere else on the page. */
.hero .hero-cta {
    display: inline-flex;
    align-items: center;
    gap: 29.75px;
    min-height: 56px;
    padding: 0 22.75px;
    border-radius: 14px;
    background: #FAFAFA;
    color: #381C55;
    font-family: var(--hero-text);
    font-size: 15.75px;
    font-weight: 400;
    line-height: 1;
    white-space: nowrap;
    text-decoration: none;
    border: 0;
    transition: background 160ms cubic-bezier(.16,1,.3,1),
                transform 160ms cubic-bezier(.34,1.45,.5,1);
}
.hero .hero-cta:hover { background: #fff; transform: translateY(-1px); }
.hero .hero-cta .arrow { transition: transform 160ms cubic-bezier(.34,1.45,.5,1); }
.hero .hero-cta:hover .arrow { transform: translateX(6px); }

/* ---- the showreel dial ----------------------------------------------
   The existing #video-trigger keeps its id, its script and its overlay —
   only the look changes. Grid placement (above) puts it on the content
   block's bottom-right corner, which is where the design has it: right
   edge of the column, bottom flush with the button. */
.hero #video-trigger {
    position: relative;
    right: auto;
    bottom: auto;
    top: auto;
    left: auto;
    /* NB: no `margin: 0` here — this block sits after the grid placement
       above, at equal specificity, and the shorthand would silently reset
       the margin-right that pulls the dial in to the copy column's edge. */
    width: clamp(96px, 7vw, 128px);
    height: auto;
    aspect-ratio: 1;
    border-radius: 50%;
    color: #FAFAFA;
}
/* The frosted disc. Its own layer because the blend applies to the
   backdrop only — on the trigger itself it would drag the ring text and
   the mouse through the blend and wash them out. */
.hero #video-trigger::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background: rgba(34, 29, 56, 0.29);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    mix-blend-mode: screen;
    /* Unmasked, a 14px blur reads as a hard-edged lens sitting on the
       swirl; this fades the blurred backdrop back into the sharp one. */
    -webkit-mask: radial-gradient(closest-side, #000 58%, rgba(0,0,0,0.45) 84%, transparent 100%);
    mask: radial-gradient(closest-side, #000 58%, rgba(0,0,0,0.45) 84%, transparent 100%);
    pointer-events: none;
}
/* display:block matters: an inline SVG sits on a line box, and the
   baseline gap under it stretches the aspect-ratio:1 box into an oval. */
.hero #video-trigger #circle-svg { position: relative; display: block; }
.hero #video-trigger #circle-text {
    font-family: var(--hero-text);
    font-size: 31px;
    font-weight: 800;
    /* tracking comes from textLength/lengthAdjust on the textPath, which
       spreads the phrase across the exact circumference; a CSS
       letter-spacing on top fights it and re-introduces overflow */
    letter-spacing: 0;
    fill: currentColor;
    text-transform: uppercase;
}
/* main's rule tunes letter-spacing per font to fill the old two-phrase
   ring; textLength owns the fit now, so it must stay at zero in both
   states or the phrase overflows once DM Sans confirms. */
.hero #video-trigger.reel-font-ready #circle-text { letter-spacing: 0; }
/* The icons ship at a fixed 30px, which is 25% of a 118px dial but 47% of
   a 64px one — on a phone the mouse rendered as a big empty oval. Sizing
   them as a share of the dial keeps the design's proportion at every
   breakpoint. Width alone would leave the intrinsic height, so both are
   set and the viewBox does the rest. */
.hero #video-trigger .reel-icon {
    color: currentColor;
    width: 25%;
    height: 25%;
}

/* ============================================================
   RESPONSIVE
   Measured at 19 viewport sizes, 1920 down to 320.
   ============================================================ */

/* The mobile header is shorter than the desktop one, so the band the
   composition centres in is different. */
@media (max-width: 860px) {
    .hero { --hero-hd: 68px; }
}

/* Below 640 the 6.85vw ramp falls under its own 44px floor and stops
   moving — a 320px phone would set the headline at the same size as a
   640px one and wrap it to six lines. A second ramp takes over at the
   handover point (both pass through ~44px at 640) and the vertical
   rhythm tightens with it. */
@media (max-width: 640px) {
    .hero { --hero-h1: clamp(29px, 8.6vw, 44px); }
    .hero h1 { margin-bottom: clamp(24px, 5vh, 44px); }
    .hero .hero-foot { gap: 20px; }
}

/* Grid keeps the dial on the copy block's bottom-right at every size, so
   on a phone it lands beside the button with no repositioning — only the
   scale changes. */
@media (max-width: 860px) {
    .hero #video-trigger { width: 84px; }
}

/* The button is a fixed ~180px, so the dial and the gap are the only
   give in the narrow row. */
@media (max-width: 480px) {
    .hero #video-trigger { width: 76px; }
    .hero .hero-ctas { width: 100%; gap: 14px; }
}
@media (max-width: 400px) {
    .hero #video-trigger { width: 64px; }
    .hero .hero-ctas { gap: 12px; }
}

/* On a portrait screen the copy column is the full width, so the mark is
   behind the words wherever it is placed — home-hero.js already sizes it
   down and quiets it there, and this is the belt to that braces: a soft
   ink wash under the copy only. Keyed on aspect ratio so it fires on
   exactly the screens the script calls narrow; desktop keeps the copy
   left and the mark right, and needs neither. */
@media (max-aspect-ratio: 9/10) {
    .hero .container::before {
        content: "";
        position: absolute;
        inset: -8% -12% -10%;
        z-index: -1;
        pointer-events: none;
        background: radial-gradient(74% 56% at 50% 50%,
            rgba(10, 1, 28, 0.72) 0%,
            rgba(10, 1, 28, 0.46) 48%,
            rgba(10, 1, 28, 0) 84%);
    }
}

/* Short landscape screens: the composition has to arrive in one view. */
@media (max-height: 520px) and (orientation: landscape) {
    .hero { --hero-h1: clamp(22px, 3.1vw, 32px); }
    .hero h1 { margin-bottom: 24px; }
    .hero .hero-foot { gap: 16px; }
    .hero .hero-sub { font-size: 15px; }
    .hero #video-trigger { width: 64px; }
}

@media (prefers-reduced-motion: reduce) {
    .hero .hero-cta, .hero .hero-cta .arrow { transition: none; }
}
