/**
 * Preloader / Hero ("Crisp Loading Animation") — ported from preloader.md.
 * Layout/animation mechanics untouched. Colors swapped from the resource's
 * neutral light demo palette (#eaeaea/#f4f4f4) to this site's dark baseline,
 * and font swapped from Haffer XH (not licensed here) to Spectral (this
 * site's existing headline font) — both flagged inline below.
 * .crisp-header__top (logo + hamburger) is intentionally NOT styled/used —
 * see the REQUIRED ADAPTATION note in views/preloader.php.
 */

/* Disable Scroll on Loading — REQUIRED FIX (not part of the Osmo resource):
   the original selector here was `main:has(.crisp-header.is--loading)`,
   which assumed .crisp-header was a DESCENDANT of <main>. In this theme
   it's a sibling — rendered before <main> in header.php, specifically so
   it can cover the nav bar (z-index) during loading — so :has() never
   matched anything and this rule never applied. Confirmed empirically:
   <main>'s computed height during loading was its full ~5000px+ content
   height, not 100dvh, meaning the page was fully scrollable the entire
   time the loader was playing. A user scrolling immediately (or fast)
   could reach later ScrollTrigger-driven sections — including the
   highlight-text reveal — before their own setup had settled, which was a
   real contributor to that section feeling scroll-speed-dependent/unreliable.
   Replaced with a class toggle that doesn't depend on DOM nesting: set in
   header.php's early inline script (before any other script runs), removed
   in preloader.js once .is--loading clears. */
html.is-preloading,
html.is-preloading body {
  overflow: hidden;
}

/* Loader */

.crisp-loader {
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
  font-size: 1vw;
  display: flex;
  position: absolute;
  top: 0;
  left: 0;
  overflow: hidden;
}

.crisp-loader__wrap {
  font-size: var(--size-font);
  justify-content: center;
  align-items: center;
  display: flex;
  position: relative;
}

.crisp-loader__groups {
  position: relative;
  overflow: hidden;
}

.crisp-loader__group {
  border-radius: .5em;
  justify-content: center;
  align-items: center;
  display: flex;
  position: relative;
}

.crisp-loader__single {
  padding-left: 1em;
  padding-right: 1em;
  position: relative;
}

.crisp-loader__media {
  border-radius: .5em;
  justify-content: center;
  align-items: center;
  width: 10em;
  height: 10em;
  display: flex;
  position: relative;
}

.crisp-loader__media.is--scaling {
  will-change: transform;
  border-radius: 0;
  transition-property: border-radius;
  transition-duration: .5s;
  transition-timing-function: cubic-bezier(1, 0, 0, 1);
  display: flex;
}

.crisp-loader__cover-img {
  object-fit: cover;
  border-radius: inherit;
  width: 100%;
  height: 100%;
  position: absolute;
}

.crisp-loader__media.is--scaling.is--radius {
  border-radius: .5em;
}

.crisp-loader__group.is--relative {
  position: relative;
  left: 100%;
}

.crisp-loader__group.is--duplicate {
  position: absolute;
}

.crisp-loader__cover-img.is--scale-down {
  will-change: transform;
}

/* REQUIRED SWAP: resource's gradient fades to a light neutral (#eaeaea);
   this site's baseline is dark, so it fades to --primary-color instead. */
.crisp-loader__fade {
  pointer-events: none;
  background-image: linear-gradient(90deg, var(--primary-color) 20%, transparent);
  width: 5em;
  height: calc(100% + 2px);
  position: absolute;
  top: -1px;
  left: -1px;
}
.crisp-loader__fade.is--duplicate {
  left: auto;
  right: -1px;
  transform: scaleX(-1);
}

/* Header */

/* REQUIRED SWAP: #eaeaea (light demo bg) -> var(--primary-color) (this
   site's dark baseline). */
.crisp-header {
  background-color: var(--primary-color);
  justify-content: center;
  align-items: center;
  width: 100%;
  display: flex;
  position: relative;
  overflow: hidden;
  /* REQUIRED FIX (not part of the Osmo resource): components/header/
     top-navigation.php has a legacy site-wide scroll-reveal <style> block
     — `section { opacity:0; transform:translateY(1rem); transition:
     opacity .3s, transform .3s; }` — meant for content sections fading in
     on scroll. .crisp-header is a <section> too, so it inherits that by
     plain element-selector match, even though this hero has its own
     load-time reveal (preloader.js) and its own GSAP-driven slide
     transform on menu open/close (underlay-nav.js's mainSlideEls). That
     leftover `transition: ..., transform .3s ease-out` fought with GSAP's
     own per-frame transform updates on every toggle — the CSS transition
     kept chasing GSAP's constantly-moving target with its own easing on
     top, which is what read as the hero's content lagging behind the rest
     of the page during the slide. This class selector's higher specificity
     overrides the legacy element selector outright.
  */
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: none;
  /* Below the underlay nav header (z-index:100) once resolved, so the
     nav/menu stays clickable as normal site chrome after the hero settles
     in — but matches [data-main]'s z-index:2 (not 1), the site's existing
     convention for covering .underlay-nav__menu (z-index:1) when closed.
     At z-index:1 this tied with the menu panel and lost on DOM order,
     leaving the closed menu panel's own dark background visibly covering
     the right ~30em (--menu-width) of the hero. */
  z-index: 2;
  /* underlay-nav.js now slides this element on menu open/close (see the
     mainSlideEls addition there) alongside [data-main]. Unlike typical page
     content, it holds all 5 full-size slideshow slides simultaneously
     (invisible ones via opacity:0, not display:none, so the slideshow's own
     crossfade/transitions keep working) — meaning the browser composites
     every one of them each frame during the slide, not just the visible
     slide. That's real extra paint cost [data-main] doesn't carry, which is
     what reads as the hero moving slower/less smoothly. will-change hints
     the browser to promote this to its own GPU layer ahead of time instead
     of deciding mid-animation. */
  will-change: transform;
}

/* Only while actually loading does this need to sit ABOVE the nav
   (z-index:100) — covering the menu bar so it's not visible/clickable
   underneath the loading animation. initCrispLoadingAnimation() removes
   .is--loading once the sequence completes, which drops this back down
   to the base z-index:1 above and restores normal nav access. */
.crisp-header.is--loading {
  z-index: 150;
}

/* Loading: Hidden */
.crisp-header.is--loading.is--hidden {
  display: none;
}

/* Loading: is Loading */
.crisp-header.is--loading .crisp-loader {
  display: flex;
}

/* Loading: Loading Done */
.crisp-loader {
  display: none;
}

/* Static background image (first Slides entry, not a carousel) — sits
   behind .crisp-header__content. A dark gradient overlay keeps the white
   text/CTA readable regardless of what the uploaded image looks like. */
.crisp-header__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  /* Same reasoning as .crisp-header's own will-change: this full-bleed
     image + gradient overlay are new additions inside the subtree that
     slides on menu open/close (see underlay-nav.js's mainSlideEls) — hint
     the browser to promote them ahead of time rather than decide mid-tween. */
  will-change: transform;
}

/* Parallax mask technique (stellae Global Parallax Setup): this inner
   wrapper is taller than its masked parent (.crisp-header__bg, overflow:
   hidden above) so global-parallax.js has room to shift it (yPercent)
   without ever exposing empty space at either edge — the parallax here is
   configured to move it *down* (data-parallax-end="15" in preloader.php),
   so it needs headroom above, not just below.
   BUG FIX: top was 0, putting the entire extra 20% height below — with
   object-fit:cover's default center object-position, cover-fitting the
   same image into this 120%-tall box crops it differently than the
   loading animation's own reveal box (.crisp-loader__media, exactly
   100dvh, no extra height). That mismatch made the background image visibly
   jump down the instant loading concluded and this box took over — every
   pixel of that 20% height difference split evenly (10% above, 10% below,
   via top:-10%) reproduces the exact same crop the loading animation
   already showed, so the handoff is seamless. (Verified: with top:0, the
   skyline in the revealed background sat ~90px lower than in the loading
   animation's final frame at a 900px-tall viewport; with top:-10% the two
   frames align pixel-for-pixel.) */
.crisp-header__bg-wrap {
  position: absolute;
  top: -10%;
  left: 0;
  width: 100%;
  height: 120%;
}

/* .crisp-loader has no opaque backdrop of its own — it always relied on
   .crisp-header's plain background-color showing through the gaps between
   its small loader-media thumbnails. Now that .crisp-header__bg sits behind
   everything, those same gaps would show the full busy photo instead of a
   clean backdrop during loading. Hidden until .is--loading is removed. */
.crisp-header.is--loading .crisp-header__bg {
  display: none;
}

.crisp-header__bg-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* A real element (not ::after) so preloader.js can fade it in with GSAP —
   pseudo-elements can't be animated directly. Initial opacity:0 is set via
   gsap.set() in JS, matching this site's existing JS-driven-initial-state
   convention, not duplicated here in CSS. */
.crisp-header__bg-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(11, 13, 23, 0.55), rgba(11, 13, 23, 0.75));
}

/* REQUIRED SWAP: #f4f4f4 -> #fff (this site's white token) for text sitting
   on the dark hero background. */
.crisp-header__content {
  color: #fff;
  flex-flow: column;
  justify-content: center;
  align-items: center;
  width: 100%;
  min-height: 100dvh;
  padding: 2.5em;
  display: flex;
  position: relative;
  z-index: 1;
}

/* No longer an absolutely-positioned overlay over a background slideshow
   (that's been removed) — just a normal centered content block now. */
.crisp-header__center {
  width: 100%;
  max-width: 50em;
  text-align: center;
  margin-left: auto;
  margin-right: auto;
}

.crisp-header__h1 {
  text-align: center;
  letter-spacing: -.02em;
  margin-top: 0;
  margin-bottom: .5em;
  font-family: Spectral, serif!important;
  font-size: calc(3vw + 3dvh);
  font-weight: 600;
  line-height: 1.1;
}
.crisp-header__h1 div{
  font-family: Spectral, serif!important;
}
.crisp-header__h1 div:nth-of-type(2) { 
  color:var(--secondary-color);
}

.crisp-header__h1 > * {
  margin: -0.1em -0.05em;
  padding: 0.1em 0.05em;
}

.crisp-header__copy {
  /* REQUESTED CHANGE: was Spectral (same serif as .crisp-header__h1 above,
     left untouched) — switched to this site's other font (Karla, the
     sans-serif used for body copy everywhere else) so the subheadline
     reads distinctly from the headline. */
  font-family: Karla, sans-serif;
  font-size: 1.5em;
  font-weight: 400;
  line-height: 1.4;
  margin-bottom: 1.5em;
}

.crisp-header__cta-wrap {
  display: flex;
  justify-content: center;
}

/* btn-bubble-arrow.css's own .btn-group defaults to font-size:2em, sized for
   a normal-text context — scoped down here same as highlight-text.css. */
.crisp-header__cta-wrap .btn-group {
  font-size: 1.5em;
}
