/* OnProfit app shell — the shared page frame: reset, nav, buttons, footer (+ their responsive rules).
   SINGLE SOURCE OF TRUTH, imported by the homepage AND every subpage. Change a shell element here once
   and it propagates everywhere. Pages OVERRIDE with scoped selectors for page-specific looks
   (e.g. body.on-light nav ...) — never re-declare these elements inline.
   Design tokens live in tokens.css (loaded before this file). */
*{margin:0;padding:0;box-sizing:border-box}
html{height:100%}
body{min-height:100%}
html,body{-webkit-text-size-adjust:100%;text-size-adjust:100%}   /* stop iOS from auto-inflating long text blocks (keeps sub + body the same size on phones) */

/* THE standard content grid for every page (from the homepage "shift" panel).
   max-width 1408 + 24px side padding → content caps at 1360, centered. Hero is exempt. */
/* Skip link: hidden until a keyboard user tabs to it, then it sits over the nav.
   Every page gives its first content section id="main" tabindex="-1" so focus
   actually lands there rather than only scrolling. */
.skip-link{position:absolute;left:24px;top:-64px;z-index:100;
  display:inline-flex;align-items:center;padding:10px 18px;border-radius:var(--radius-sm);
  background:var(--surface);color:var(--ink);text-decoration:none;
  font-family:var(--display);font-weight:500;font-size:16px;line-height:24px;   /* 500 like every other button (was the last 600 holdout after the 2026-08-06 weight pass) */
  transition:top .18s ease}
.skip-link:focus-visible{top:16px;outline:2px solid var(--action);outline-offset:2px}
#main:focus{outline:none}

/* ---- BREAKPOINTS ----------------------------------------------------------------------------------
   Three shared breakpoints. Use these unless a component genuinely needs its own.

     max-width: 640px     phone          (min-width: 641px  = tablet and up)
     max-width: 900px     narrow tablet
     max-width:1024px     tablet         (min-width:1025px  = desktop)

   A handful of components deliberately break at their own width because their layout stops
   working before the shared breakpoint. Each one carries a comment saying why; they are
   intentional, not drift, so do not "tidy" them into the shared set:

     1180px  how-it-works.css 4-up cards would drop to ~245px and headings wrap to 3 lines
     1200px  results.css      the scatter layout needs the full 1408 to read
     1200px  about.css        the hero is absolutely composed at 1512
     1280px  index.css        testimonial logo/portrait alignment stops fitting below this
     1440px  how-it-works.css the fixed 704 panel pair has to start sharing the frame

   Removed 2026-08-06, do not reintroduce without a reason:
     560px  was listed for the capacity journey labels but no query had used it for some time.
     820px  was where the shift panels switched from two columns to stacked. They now stack
            from 1024 down, so it changed no layout — it only shortened the graphic box, which
            the 640 phone breakpoint does instead.

   Media queries cannot read var(), so these values are literals. If you change one, grep for
   the number: several files use the same value for the same reason.
   ---------------------------------------------------------------------------------------------------- */
.frame{width:100%;max-width:1408px;margin:0 auto;padding:0 24px;box-sizing:border-box}

/* Shared scroll-reveal (same disclosure pattern as the homepage): elements fade +
   slide up as they enter view. Driven by reveal.js. */
.reveal{opacity:0;transform:translateY(26px);transition:opacity .7s ease, transform .8s cubic-bezier(.22,.61,.36,1)}
.reveal.in{opacity:1;transform:none}
@media (prefers-reduced-motion: reduce){ .reveal{opacity:1;transform:none;transition:none} }
/* NEVER give <html> a background. While html has none, THIS background is propagated to the
   canvas and painted first, so full-bleed layers on a negative z-index (index.css .backdrop,
   how-it-works.css .hiw-alt-box::before) paint on top of it and stay visible. The moment html
   gets a background, body's stops propagating and paints as an ordinary block background
   INSTEAD — which is above the negative z-index layer — and hides it. On the homepage that
   showed as a hard horizontal line where body's height:100% box ended (2026-08-06).
   To colour the pre-paint canvas, use color-scheme on <html> (inline, on every page). */
body{color:var(--white);font-family:var(--sans);background:var(--ink)}
a{color:inherit;text-decoration:none}

nav{position:fixed;top:0;left:0;right:0;z-index:20;padding-top:8px;background:transparent;transition:transform .35s ease, background .3s ease, backdrop-filter .3s ease}
html.menu-locked, html.menu-locked body{overflow:hidden}
.nav-in{display:flex;align-items:center;justify-content:space-between;height:64px;width:100%;padding:0 56px}
.brand{display:flex;align-items:center;flex:0 0 auto}
.brand img{height:24px;width:auto;display:block}
.nav-right{display:flex;align-items:center;gap:56px}
.navlink{font-family:var(--display);font-weight:500;font-size:18px;color:var(--white);white-space:nowrap;
  display:inline-block;transition:color .2s ease, transform .5s cubic-bezier(.34,1.56,.64,1)}   /* same spring the .menu-fab hover uses — one motion language across the header. inline-block so transform applies; scale never reflows the row */
.navlink:hover{color:var(--soft);transform:scale(1.05);text-decoration:underline;text-underline-offset:6px}   /* hover underline (Victoria, 2026-08-14) — the theme variants only recolour, so this carries across light and dark chrome */
.nav-cta{display:flex;align-items:center;gap:16px;flex:none}
/* mobile hamburger — hidden on desktop, shown in the header on phones */
.nav-burger{display:none;align-items:center;justify-content:center;flex:none;
  background:none;border:none;color:var(--surface);cursor:pointer;padding:8px;margin-right:-8px}
/* two bare lines (no box) that morph into an X when the menu opens */
.nav-burger .burger-box{position:relative;display:block;width:24px;height:12px}
.nav-burger .burger-bar{position:absolute;left:0;width:24px;height:2px;border-radius:var(--radius-xs);background:currentColor;
  transform-origin:center;transition:transform .3s ease}
.nav-burger .burger-bar:nth-child(1){top:2px}
.nav-burger .burger-bar:nth-child(2){top:10px}
nav.menu-open .nav-burger .burger-bar:nth-child(1){transform:translateY(4px) rotate(45deg)}
nav.menu-open .nav-burger .burger-bar:nth-child(2){transform:translateY(-4px) rotate(-45deg)}
/* nav scroll behaviour: frosted bar once scrolled, hides on scroll-down / returns on scroll-up (driven by shell.js) */
nav.scrolled{background:rgba(13,26,59,.32);backdrop-filter:blur(24px) saturate(1.5);-webkit-backdrop-filter:blur(24px) saturate(1.5);border-bottom:1px solid var(--tint-soft-hover)}
nav.nav-hidden{transform:translateY(calc(-100% - 8px))}
/* floating "Menu" shortcut — shows while the nav is hidden */
.menu-fab{position:fixed;top:16px;right:56px;z-index:25;display:inline-flex;align-items:center;justify-content:center;gap:8px;
  background:var(--tint-soft);border:1px solid var(--soft);border-radius:var(--radius-pill);padding:4px 16px;cursor:pointer;
  font-family:var(--display);font-weight:500;font-size:18px;line-height:34px;color:var(--soft);   /* matches .btn (2026-08-06): the fab reads as a secondary button, so it tracks the button weight */
  opacity:0;transform:translateY(-12px) scale(.86);transform-origin:top right;pointer-events:none;
  transition:opacity .32s ease, transform .5s cubic-bezier(.34,1.56,.64,1), background .3s ease}
.menu-fab.show{opacity:1;transform:translateY(0) scale(1);pointer-events:auto}
.menu-fab.show:hover{background:var(--tint-soft-hover);transform:translateY(0) scale(1.05)}
.menu-fab.show:active{transform:translateY(0) scale(.97)}
.menu-fab svg{width:20px;height:20px;display:block}
/* Button system (Figma 12:2335): Primary / Primary Dark / Secondary — Anaheim Medium (500,
   changed from SemiBold 600 on 2026-08-06). Anaheim is a variable font and the pages request
   wght@400..700, so 500 is a real weight, not a synthesised one. */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;box-sizing:border-box;font-family:var(--display);font-weight:500;font-size:18px;line-height:34px;border-radius:var(--radius-sm);padding:4px 16px;cursor:pointer;border:none;white-space:nowrap;transition:filter .2s ease, background .2s ease}
.btn-primary{background:var(--surface);color:var(--ink)}
.btn-primary-dark{background:var(--ink);color:var(--surface)}
.btn-secondary{background:var(--tint-soft);color:var(--soft);border:1px solid var(--soft)}
.btn-primary:hover{filter:brightness(1.04)}
.btn-primary-dark:hover{filter:brightness(1.18)}
.btn-secondary:hover{background:var(--tint-soft-hover)}
.brand{display:flex;align-items:center}
.brand img{height:28px;width:auto;display:block}

/* Footer — inset rounded card; dot texture sits inside with dark air around it */
.footer{position:relative;background:transparent;padding:0 20px 20px}   /* floats on the section gradient (footer moved inside .lower) */
.footer-card{position:relative;overflow:hidden;padding:40px 0 0;min-height:609px;display:flex;flex-direction:column;
  background-color:transparent;   /* float on the section gradient; keep the dot texture */
  background-image:radial-gradient(rgba(58,111,255,.6) 1px, transparent 1.5px);background-size:10px 10px}
.footer-nav{position:relative;z-index:2;display:flex;flex-direction:column;padding-left:28px}
.footer-nav a{font-family:var(--display);font-weight:500;font-size:20px;line-height:32px;letter-spacing:1px;color:var(--white);display:inline-flex;align-items:center;gap:8px;width:max-content}
.footer-nav a:hover{color:var(--soft)}
.footer-nav .grp2{margin-top:32px}
.footer-wordmark{position:relative;z-index:1;width:100%;margin-top:auto;pointer-events:none}
.footer-wordmark img{display:block;width:100%;height:auto}
.footer-copy{position:absolute;left:36px;bottom:16px;z-index:2;font-family:var(--sans);font-weight:400;font-size:16px;color:var(--white)}

@media(max-width:900px){
  .nav-in{padding:0 24px}
  .navlink,.nav-cta{display:none}
  .nav-burger{display:inline-flex}
}
@media(max-width:1024px){
  /* --- header: logo left, hamburger right --- */
  nav{top:0}
  .menu-fab{display:none}              /* on mobile the header hamburger replaces the floating shortcut */
  .nav-in{height:56px;padding:0 16px}
  .nav-burger{display:inline-flex}
  /* --- tap the hamburger: a full-viewport menu takes over, burger morphs to an X --- */
  nav.menu-open{z-index:100}                       /* lift the whole nav above the cookie card etc. */
  .brand,.nav-burger{position:relative;z-index:3}  /* logo + X stay above the overlay */
  .nav-right{position:fixed;inset:0;height:100vh;height:100dvh;z-index:2;
    display:flex;flex-direction:column;align-items:stretch;gap:0;
    background:var(--ink);padding:84px 24px calc(28px + env(safe-area-inset-bottom));overflow-y:auto;
    opacity:0;visibility:hidden;transform:translateY(-6px);
    transition:opacity .28s ease, transform .28s ease, visibility 0s .28s}
  nav.menu-open .nav-right{opacity:1;visibility:visible;transform:none;
    transition:opacity .28s ease, transform .28s ease}
  .nav-right .navlink{display:block;font-family:var(--display);font-weight:600;font-size:20px;color:var(--white);
    padding:20px 0;border-bottom:1px solid rgba(124,152,248,.14)}   /* same size/weight as the CTA buttons */
  .nav-right .navlink:first-of-type{border-top:1px solid rgba(124,152,248,.14)}
  .nav-right .navlink:hover{color:var(--soft);transform:none}   /* the desktop hover scale grows a full-width drawer link from its CENTRE, shoving its left edge out of alignment (Victoria, 2026-08-14) */
  .nav-cta{display:flex;flex-direction:column;gap:12px;margin-top:auto;padding-top:28px}
  .nav-cta .btn{width:100%}
  /* footer: pull the dotted card closer to the screen edges + shorter on mobile/tablet */
  .footer{padding:0 8px 8px}
  .footer-card{min-height:488px}   /* ~20% shorter than the 609px desktop card */
}
/* on a phone the wordmark scales down until its letters are the same height as the copyright
   line, and the copy is absolutely positioned on top of them — legible at desktop width, a
   collision here. Take the copy out of the overlay and let it sit under the mark. */
@media (max-width:640px){
  .footer-copy{position:static;left:auto;bottom:auto;padding:20px 16px 24px}
  /* the wordmark is decorative (alt=""). At this width its dots land at the same scale as the
     card's own dot texture, so the letterforms stop reading as a wordmark and become noise.
     The brand still appears in the copyright line below. */
  .footer-wordmark{display:none}
  .footer-card{min-height:0}   /* the 488 was reserving space for the mark */
}
@media (min-width:641px) and (max-width:1024px){
  /* mobile-menu CTAs side by side on tablet instead of full-width stacked */
  .nav-cta{flex-direction:row;gap:16px}
  .nav-cta .btn{flex:1;width:auto}
}

/* === NEW: minimal page header for the scaffolded subpages (layout only) === */

/* Cross-document view transitions (2026-07-29): the browser cross-fades between pages instead of
   blanking, so moving between sections reads as one continuous space rather than a page load.
   Chrome 126+ / Safari 18.2+; Firefox ignores it and gets today's behaviour. No JS, no library.
   REVERT: delete this block. */
@view-transition { navigation: auto; }
@media (prefers-reduced-motion: reduce) { @view-transition { navigation: none; } }

/* ---- shared section type (2026-07-30, page duplicates removed 2026-08-04) --------------------------
   .cat-eyebrow / .cm-h / .cm-sub are declared ONCE, here. They used to be copied into how-it-works and
   index as well; those copies are gone, so this is the single source. Pages scope-override them
   (e.g. `.testi .cm-h`), they never re-declare them.
   ---------------------------------------------------------------------------------------------------- */
.cat-eyebrow{display:block;font-family:var(--sans);font-weight:400;font-size:12px;line-height:20px;letter-spacing:1px;text-transform:uppercase;color:var(--bridge);margin-bottom:8px}
.on-light .cat-eyebrow{color:var(--action)}
.cm-h{font-family:var(--display);font-weight:500;font-size:40px;line-height:44px;color:var(--ink);margin:8px 0 0}
.cm-sub{font-family:var(--sans);font-weight:300;font-size:16px;line-height:24px;color:var(--deep);margin:16px 0 0;max-width:640px}
/* half of the 1360 grid: 632 + 96 gutter + 632. how-it-works has .hiw-head with the identical value;
   that should be retired into this one. */
.col-half{max-width:calc(50% - 48px)}
@media (max-width:1024px){ .col-half{max-width:none} }
/* .cm-h steps down on smaller screens, matching .panel-content .shift-h on the homepage
   (40 -> 32 -> 28). Without these it stayed 40px on phones and competed with the hero
   headline. The homepage's own .testi .cm-h / .closer .cm-h overrides are more specific,
   so those sections keep their existing sizes. */
@media (max-width:1024px){ .cm-h{font-size:32px;line-height:40px} }
@media (max-width:640px){  .cm-h{font-size:28px;line-height:34px} }

/* ---- Light pages -------------------------------------------------------
   For pages whose top section sits on --surface instead of --ink. Put
   `on-light` on <body>: nav links, logo, both nav buttons and the Menu pill
   all flip to dark-on-light, and the scrolled nav gets a light frosted bar.
   SINGLE SOURCE — do not re-declare any of this in a page <style> block.
Single source for the light-page shell; see the LIGHT PAGES block below. */
/* ---- LIGHT PAGES ----------------------------------------------------------------------------------
   `body.on-light` is the ONE light-page variant. Put it on any page with a light background
   and the shell inverts to suit: dark logo, dark nav links, dark burger, inverted buttons,
   and a frosted header once scrolled.

   This replaced three near-identical copies on 2026-08-04 — on-light (about, results),
   hiw-light (how-it-works) and uc-light (use-cases) — which were 54 declarations doing the
   same job in three places. Do not add a fourth: add the class, and scope any genuinely
   page-specific tweak under it.
   ---------------------------------------------------------------------------------------------------- */
body.on-light{background:var(--surface)}
body.on-light nav .brand img,
body.on-light nav.scrolled .brand img{filter:brightness(0)}
body.on-light nav .navlink,
body.on-light nav.scrolled .navlink{color:var(--ink)}
body.on-light nav .navlink:hover,
body.on-light nav.scrolled .navlink:hover{color:var(--action)}
body.on-light .nav-burger,
body.on-light nav.scrolled .nav-burger{color:var(--ink)}
body.on-light nav .btn-primary,
body.on-light nav.scrolled .btn-primary{background:var(--ink);color:var(--surface)}
body.on-light .btn-secondary,
body.on-light nav .btn-secondary,
body.on-light nav.scrolled .btn-secondary{background:rgba(13,26,59,.05);color:var(--ink);border-color:var(--ink)}
body.on-light .btn-secondary:hover,
body.on-light nav .btn-secondary:hover,
body.on-light nav.scrolled .btn-secondary:hover{background:rgba(13,26,59,.1)}
body.on-light nav.scrolled{background:rgba(241,243,255,.72);backdrop-filter:blur(24px) saturate(1.4);
  -webkit-backdrop-filter:blur(24px) saturate(1.4);border-bottom:1px solid rgba(13,26,59,.08)}
body.on-light .menu-fab{background:rgba(13,26,59,.05);border-color:var(--ink);color:var(--ink)}
body.on-light .menu-fab.show:hover{background:rgba(13,26,59,.1)}

/* ---- ADAPTIVE NAV (2026-08-14, Victoria)
   Every selector is prefixed with `body` ON PURPOSE: body.on-light's nav rules carry body as
   an extra ELEMENT of specificity (0,3,2); without the prefix these tie at (0,3,1) and lose. The class is DOUBLED (.nav-dark.nav-dark) to sit one class above any tie —
   repeated classes count each occurrence, and it beats !important arms races. ------------------------------------------------------
   shell.js samples the painted background under the header as you scroll and sets exactly one
   of body nav.nav-light.nav-light (light behind: dark-on-light chrome) or body nav.nav-dark.nav-dark (dark behind:
   white-on-dark chrome). Placed AFTER the body.on-light block on purpose: these tie it on
   specificity, so source order lets the live sample override the page's static default —
   which remains the no-JS fallback. Surfaces the sampler cannot read (gradients, images,
   video) declare themselves with data-nav="dark" / data-nav="light". The floating Menu pill
   mirrors the theme as body .menu-fab.fab-light.fab-light / .fab-dark. */
body nav.nav-light.nav-light .brand img,
body nav.nav-light.nav-light.scrolled .brand img{filter:brightness(0)}
body nav.nav-light.nav-light .navlink,
body nav.nav-light.nav-light.scrolled .navlink{color:var(--ink)}
body nav.nav-light.nav-light .navlink:hover,
body nav.nav-light.nav-light.scrolled .navlink:hover{color:var(--action)}
body nav.nav-light.nav-light .nav-burger,
body nav.nav-light.nav-light.scrolled .nav-burger{color:var(--ink)}
body nav.nav-light.nav-light .btn-primary,
body nav.nav-light.nav-light.scrolled .btn-primary{background:var(--ink);color:var(--surface)}
body nav.nav-light.nav-light .btn-secondary,
body nav.nav-light.nav-light.scrolled .btn-secondary{background:rgba(13,26,59,.05);color:var(--ink);border-color:var(--ink)}
body nav.nav-light.nav-light .btn-secondary:hover,
body nav.nav-light.nav-light.scrolled .btn-secondary:hover{background:rgba(13,26,59,.1)}
body nav.nav-light.nav-light.scrolled{background:rgba(241,243,255,.72);backdrop-filter:blur(24px) saturate(1.4);
  -webkit-backdrop-filter:blur(24px) saturate(1.4);border-bottom:1px solid rgba(13,26,59,.08)}
body nav.nav-dark.nav-dark .brand img,
body nav.nav-dark.nav-dark.scrolled .brand img{filter:none}
body nav.nav-dark.nav-dark .navlink,
body nav.nav-dark.nav-dark.scrolled .navlink{color:var(--white)}
body nav.nav-dark.nav-dark .navlink:hover,
body nav.nav-dark.nav-dark.scrolled .navlink:hover{color:var(--soft)}
body nav.nav-dark.nav-dark .nav-burger,
body nav.nav-dark.nav-dark.scrolled .nav-burger{color:var(--white)}
body nav.nav-dark.nav-dark .btn-primary,
body nav.nav-dark.nav-dark.scrolled .btn-primary{background:var(--surface);color:var(--ink)}
body nav.nav-dark.nav-dark .btn-secondary,
body nav.nav-dark.nav-dark.scrolled .btn-secondary{background:var(--tint-soft);color:var(--soft);border-color:var(--soft)}
body nav.nav-dark.nav-dark .btn-secondary:hover,
body nav.nav-dark.nav-dark.scrolled .btn-secondary:hover{background:var(--tint-soft-hover)}
body nav.nav-dark.nav-dark.scrolled{background:rgba(13,26,59,.32);backdrop-filter:blur(24px) saturate(1.5);
  -webkit-backdrop-filter:blur(24px) saturate(1.5);border-bottom:1px solid var(--tint-soft-hover)}
body .menu-fab.fab-light.fab-light{background:rgba(13,26,59,.05);border-color:var(--ink);color:var(--ink)}
body .menu-fab.fab-light.fab-light.show:hover{background:rgba(13,26,59,.1)}
body .menu-fab.fab-dark.fab-dark{background:var(--tint-soft);border-color:var(--soft);color:var(--soft)}
/* the mobile drawer is an --ink overlay on every page: the adaptive light chrome must yield
   inside it, same reason (and same !important arms race) as the body.on-light block above */
@media (max-width:1024px){
  nav.menu-open.nav-light .brand img{filter:none !important}
  nav.menu-open.nav-light .navlink{color:var(--white) !important}
  nav.menu-open.nav-light .navlink:hover{color:var(--soft) !important}
  nav.menu-open.nav-light .nav-burger{color:var(--white) !important}
  nav.menu-open.nav-light .btn-primary{background:var(--surface) !important;color:var(--ink) !important}
  nav.menu-open.nav-light .btn-secondary{background:var(--tint-soft) !important;color:var(--soft) !important;border-color:var(--soft) !important}
}

/* The dark band that carries the footer. .footer / .footer-card are transparent
   by design and float on whatever wraps them, so every page needs a real dark
   element underneath. results.html has an identical `.pf-lower`; fold it in. */
.lower-dark{background:var(--ink)}

/* The mobile drawer is a full-screen --ink overlay on EVERY page, light ones included,
   so the on-light nav treatment has to stop at its edge. Without this the links are set
   to --ink and the logo is filtered to black, both on a dark background: the menu opens
   and appears empty except for the buttons. Scoped to nav.menu-open so the CLOSED header
   on a light page keeps its dark logo and dark burger.

   !important is still load-bearing: `body.on-light nav.scrolled .navlink` above TIES these
   on specificity, and page stylesheets load after this one, so without it a page-level
   override could win and the drawer would go dark again. */
@media (max-width:1024px){
  body.on-light nav.menu-open .brand img{filter:none !important}
  body.on-light nav.menu-open .navlink{color:var(--white) !important}
  body.on-light nav.menu-open .navlink:hover{color:var(--soft) !important}
  body.on-light nav.menu-open .nav-burger{color:var(--white) !important}
  body.on-light nav.menu-open .btn-primary{background:var(--surface) !important;color:var(--ink) !important}
  body.on-light nav.menu-open .btn-secondary{background:var(--tint-soft) !important;color:var(--soft) !important;border-color:var(--soft) !important}
  body.on-light nav.menu-open .btn-secondary:hover{background:var(--tint-soft-hover) !important}
}

/* Section label: a 9px square + a spaced Anaheim caption. Promoted here 2026-08-04
   because it now appears on About ("What we believe") and on the homepage
   testimonial, and a third page-scoped copy would break the single-source rule.
   The square uses currentColor so it always matches its label.
   Matched to .panel-head (index.css) on 2026-08-06: 16px with a 12px gap, was 20px/16.
   These two are the same label drawn twice — same font, weight, letter-spacing and 9px
   square — and they only stopped matching when .panel-head went to 16px that morning.
   Keep them in step: if one moves, move the other, or merge them properly. */
.sec-label{display:inline-flex;align-items:center;gap:12px;
  font-family:var(--display);font-weight:500;font-size:16px;line-height:28px;
  letter-spacing:2px;color:var(--white)}
.sec-label::before{content:"";flex:0 0 auto;width:9px;height:9px;background:currentColor}

/* ---- hero headline entrance ---------------------------------------------
   The same fade-and-grow the results hero uses, made shareable so every page
   opens the same way. Opt in by adding class="hero-rise" to the <h1>; shell.js
   adds .lit once the first frame has painted.

   Opacity and scale only, no translate: two of these headlines are absolutely
   positioned and one is centred with translateX(-50%), so a transform that
   included a translate would fight their layout.

   The hidden state lives here rather than in the markup, so if shell.js fails
   to load the headline is simply visible instead of invisible. */
.hero-rise{opacity:0;transform:scale(.94);transform-origin:50% 70%}
.hero-rise.lit{opacity:1;transform:scale(1);
  transition:opacity .7s ease, transform .9s cubic-bezier(.22,.61,.36,1)}
/* second beat (2026-08-13): page subtitles carry BOTH classes (hero-rise hero-rise-2) and
   follow the headline by a third of a second — title first, then the text below it. */
.hero-rise-2.lit{transition-delay:.35s}
@media (prefers-reduced-motion:reduce){
  .hero-rise,.hero-rise.lit{opacity:1;transform:none;transition:none}
}

/* ---- cookie consent card (site-wide since 2026-08-12; markup + logic injected by
   cookie-consent.js on every page — GA consent has to be asked wherever a visitor lands) ---- */
@keyframes promoIn{from{opacity:0;transform:translateY(24px) scale(.94)}to{opacity:1;transform:none}}
.cookie-card{position:fixed;right:24px;bottom:24px;z-index:60;width:340px;box-sizing:border-box;
  display:flex;flex-direction:column;gap:16px;background:var(--ink);border:1px solid rgba(124,152,248,.28);border-radius:var(--radius-md);padding:24px;
  box-shadow:0 16px 44px rgba(0,5,30,.40);opacity:0;
  animation:promoIn .6s cubic-bezier(.22,.61,.36,1) .8s forwards;transition:opacity .4s ease, transform .4s ease}
.cookie-card.dismissed{animation:none;opacity:0;transform:translateY(24px) scale(.94);pointer-events:none}
.cookie-card-copy{margin:0;font-family:var(--sans);font-weight:400;font-size:14px;line-height:1.5;color:var(--white)}
.cookie-card-copy a{color:var(--white);text-decoration:underline}
.cookie-card-actions{display:flex;gap:8px}
.cookie-card-actions .btn{flex:1 1 0}   /* equal widths: consent choices carry equal visual weight, deliberately */
@media (prefers-reduced-motion:reduce){.cookie-card{animation:none;opacity:1}}
@media (max-width:640px){ .cookie-card{left:16px;right:16px;bottom:16px;width:auto} }   /* phones: span the bottom */

/* ---- EXPERIMENT: page exit transition (2026-08-13; driven by page-exit.js) ----
   The whole page fades on the way OUT of an internal navigation. Entry animations
   are each page's own. REVERT with page-exit.js and its script tags. */
html.page-exit body{opacity:0;transition:opacity .28s ease}
