/* ==========================================================================
   Graphyte Design — custom overrides for Relume Publish
   Loaded in <head> after Relume's own bundle.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Page canvas
   Relume ships `body { background: var(--colors-background-primary) }`, which
   resolves to #ffffff. The page's only section is `.scheme-2` (#030710), so
   white flashes on overscroll and on any viewport taller than the content.

   Track the scheme token rather than hard-coding the hex, so this survives a
   palette change in the Relume editor. Note it must be `--scheme2-background`,
   not `--scheme-background` — the latter is scheme-1 at :root and is white.

   Relume injects custom head code at the TOP of <head>, so this sheet loads
   BEFORE Relume's bundle and loses every specificity tie. Their rule is
   `body { background: var(--colors-background-primary) }` at (0,0,1), so we
   use `html body` at (0,0,2) to win regardless of order. Do not simplify this
   back to a bare `body` — it silently stops working.
   -------------------------------------------------------------------------- */
html,
html body {
  background-color: var(--scheme2-background, #030710);
}

/* Dark scrollbars, overscroll gutter and form controls to match. Remove this
   block if any light-scheme sections get added to the site. */
:root:root {
  color-scheme: dark;
}

/* --------------------------------------------------------------------------
   Hero — fill the viewport, stay vertically centred

   Relume already ships `.section { display: flex; align-items: center }`, so
   the centring mechanism exists; the section simply had no surplus height to
   centre within. Giving it a viewport-height floor is enough. `align-items`
   is restated below so this file owns the centring guarantee rather than
   inheriting it from a Relume default that could change.

   `min-height`, never `height`: on viewports shorter than the content the
   section grows and the page scrolls, instead of cropping the logo.

   `svh` over `vh`/`dvh`: on mobile, `100vh` resolves to the *large* viewport
   (browser chrome hidden), so the hero overflows on load; `100dvh` reflows as
   the chrome hides and shows. `100svh` always fits without jank. The `100vh`
   line above it is the fallback for browsers predating svh.

   Scoped to the first section so any sections added later are unaffected. If
   a navbar is ever added, subtract it: `calc(100svh - var(--navbar-height))`.
   -------------------------------------------------------------------------- */
main > .page-section:first-child > .section {
  min-height: 100vh;
  min-height: 100svh;
  align-items: center;

  /* Relume applies 160px of padding top AND bottom at desktop widths. With
     410px of content that sets a 730px floor on the section, so any viewport
     shorter than 730px — most laptops, once browser chrome is subtracted —
     overflowed 100svh, gained a scrollbar and sat off-centre in the window.

     Because the section is min-height + align-items:center and the padding is
     symmetric, the padding does not influence where the content sits; it only
     raises the minimum height. Capping it against viewport height therefore
     moves nothing when the content already fits, and simply stops the
     overflow when it does not. The 2rem floor keeps breathing room on
     genuinely short viewports, where the section still grows and scrolls. */
  padding-block: clamp(2rem, 6vh, var(--section-padding-large, 10rem));
}

/* --------------------------------------------------------------------------
   Graphite lattice — host and stacking context

   The SVG is injected as the section's first child by lattice.js.

   The .container promotion is belt-and-braces, not a fix: Relume puts
   .z-index-1 on an inner .stack, and a flex item with a non-auto z-index
   forms a stacking context even while position:static — so content already
   paints above the lattice. This rule just stops us depending on that class
   staying where it is.
   -------------------------------------------------------------------------- */
main > .page-section:first-child > .section {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}

main > .page-section:first-child > .section > .container {
  position: relative;
  z-index: 1;
}

.gd-lattice {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 0;
}

/* --------------------------------------------------------------------------
   Primary button — brand blue on the dark scheme

   Relume derives every button state from two tokens:
     :hover          color-mix(in srgb, var(--button-fill) 90%, var(--button-text))
     :active         color-mix(in srgb, var(--button-fill) 85%, var(--button-text))
     :focus-visible  outline: 2px solid var(--button-fill)
   so overriding the tokens keeps hover and active coherent for free. Setting
   `background` on .button directly would fix the resting state and leave hover
   mixing from white — a pale smear over the blue.

   Relume declares these tokens in its own `.scheme-2` block at (0,1,0), and
   its bundle loads AFTER this file (custom head code is injected at the top of
   <head>), so a plain `.scheme-2` here would tie and lose. The class is
   repeated to reach (0,2,0) and win outright. Relume already ships #2B5CE0
   buttons in .scheme-1 and .scheme-4, so this brings the dark scheme into line
   rather than introducing a new value.
   -------------------------------------------------------------------------- */
.scheme-2.scheme-2 {
  --button-fill: #2B5CE0;
  --button-text: #FFFFFF;
}

/* The focus ring takes its colour from --button-fill, which would now draw a
   blue ring immediately outside a blue button — 3.56:1 against the page, down
   from 20.16:1. Pin it white. Delete this block for Relume's default back. */
.scheme-2 .button:focus-visible {
  outline-color: #FFFFFF;
}

/* --------------------------------------------------------------------------
   Entrance — staggered rise and fade

   Deliberately CSS-only. A JS reveal would need the hidden start state in the
   stylesheet, which leaves content invisible forever if the script fails to
   run; applying the start state from JS instead flashes the content first.
   Keyframes with `animation-fill-mode: both` hold the from-state during the
   delay, and this sheet is render-blocking, so nothing paints unstyled.

   Wrapped in `no-preference` rather than overridden afterwards, so for anyone
   who asked for less motion the animation is never declared at all.

   Four steps, not three: the heading and paragraph move a beat apart so the
   copy cascades rather than arriving as one block. Tune the three knobs below.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
  main > .page-section:first-child > .section {
    --gd-rise-distance: 10px;
    --gd-rise-duration: 600ms;
    --gd-rise-stagger: 90ms;
  }

  @keyframes gd-rise {
    from { opacity: 0; transform: translateY(var(--gd-rise-distance, 10px)); }
    to   { opacity: 1; transform: none; }
  }

  @keyframes gd-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
  }

  main > .page-section:first-child > .section .media-wrapper,
  main > .page-section:first-child > .section h2,
  main > .page-section:first-child > .section p,
  main > .page-section:first-child > .section .button-group {
    animation: gd-rise var(--gd-rise-duration, 600ms)
               var(--easing-easeOut, cubic-bezier(0, 0, .2, 1)) both;
  }

  main > .page-section:first-child > .section .media-wrapper { animation-delay: 0ms; }
  main > .page-section:first-child > .section h2             { animation-delay: calc(var(--gd-rise-stagger) * 1); }
  main > .page-section:first-child > .section p              { animation-delay: calc(var(--gd-rise-stagger) * 2); }
  main > .page-section:first-child > .section .button-group  { animation-delay: calc(var(--gd-rise-stagger) * 3); }

  /* The lattice is injected by JS after the copy has begun moving, so without
     this it snaps in. Slower and fade-only — it should settle, not arrive. */
  .gd-lattice {
    animation: gd-fade 1200ms var(--easing-easeOut, cubic-bezier(0, 0, .2, 1)) both;
  }
}
