/* ================================================================
   base.css — reset + page shell + shared type utilities.
   Every rule here mirrors something the reference actually emits.
   Load order:  fonts.css -> tokens.css -> base.css -> css/sections/*.css
   ================================================================ */

/* ---------------------------------------------------------------
   1. Reset — verbatim from the Framer runtime sheet
   --------------------------------------------------------------- */
html, body, #main {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

:root {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

* {
  box-sizing: border-box;
  -webkit-font-smoothing: inherit;
}

h1, h2, h3, h4, h5, h6, p, figure {
  margin: 0;
}

body, input, textarea, select, button {
  font-family: sans-serif;
  font-size: 12px;
}

/* Framer's page background, set by an inline <style> on every page. */
body {
  background: var(--c-white);
}

/* Framer normalises rich-text elements to zero box model. */
p, div, figure, h1, h2, h3, h4, h5, h6, ol, ul {
  /* only the ones Framer resets; lists keep their own padding elsewhere */
}
p.rt, div.rt, figure.rt,
h1.rt, h2.rt, h3.rt, h4.rt, h5.rt, h6.rt,
ol.rt, ul.rt {
  margin: 0;
  padding: 0;
}

/* Images: Framer never lets an image overflow or add a baseline gap. */
img, svg, video, canvas {
  display: block;
  max-width: 100%;
}

/* Links and buttons inherit type; the reference never shows a UA button. */
a {
  color: inherit;
  text-decoration: none;
}
button {
  border: 0;
  background: none;
  color: inherit;
  cursor: pointer;
  font: inherit;
}
input, textarea {
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
}

/* ---------------------------------------------------------------
   2. Page shell
   Reference structure (raw/<bp>/*.html):

     <body>
       <div id="main">
         <div class="page" data-layout-template="true">     .framer-e5d0c.framer-12ixkfb
           <div class="page__menu"> …menu-line partial… </div>   .framer-n5w4k8-container
           …sections… (the Framer page root is display:contents)
           <div id="overlay"></div>
           <div class="page__grow"></div>                   .framer-1g7tlmx
         </div>
       </div>
     </body>

   The menu-line wrapper is position:fixed, so the menu contributes ZERO to
   page height — page height is the sum of the in-flow sections only.
   --------------------------------------------------------------- */
#main {
  width: 100%;
}

.page {
  min-height: 100vh;
  width: 100%;
  background-color: var(--c-white);
  display: flex;
  flex-flow: column;
  place-content: center flex-start;
  align-items: center;
  gap: 0;
  padding: 0;
  position: relative;
  overflow: var(--overflow-clip-fallback, clip);
  height: min-content;
}

/* The fixed wrapper that holds partials/menu-line.html lives in
   css/sections/menu-line.css as `.menu-line-fixed` (order:-1000 is not
   needed because the element is out of flow). */

/* Framer's flex-grow spacer, keeps short pages at 100vh. */
.page__grow {
  margin: 0;
  background: none;
  flex-grow: 1;
  width: 0;
  height: 0;
  position: relative;
}

[data-layout-template="true"] > #overlay {
  margin-bottom: 0;
}

/* Every top-level section is a full-width flex child of .page. */
.page > [data-section] {
  width: 100%;
  flex: none;
}

/* ---------------------------------------------------------------
   3. Type utilities — the reference's `.framer-styles-preset-*` set,
      renamed. Values live in tokens.css. Mapping table in SHARED-NOTES.md.
   --------------------------------------------------------------- */
.t-h1 {
  font-family: var(--font-sans);
  font-weight: var(--t-h1-weight);
  font-size: var(--t-h1-size);
  line-height: var(--t-h1-lh);
  letter-spacing: var(--t-h1-ls);
  color: var(--c-graphite);
}

.t-h2 {
  font-family: var(--font-sans);
  font-weight: var(--t-h2-weight);
  font-size: var(--t-h2-size);
  line-height: var(--t-h2-lh);
  letter-spacing: var(--t-h2-ls);
  color: var(--c-graphite);
}

.t-display {
  font-family: var(--font-sans);
  font-weight: var(--t-display-weight);
  font-size: var(--t-display-size);
  line-height: var(--t-display-lh);
  letter-spacing: var(--t-display-ls);
  color: var(--c-graphite);
}

.t-h2-invert {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: var(--t-h2-invert-size);
  line-height: 100%;
  letter-spacing: -.05em;
  color: var(--c-white);
}

.t-h3-caps {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: var(--t-h3-caps-size);
  line-height: 90%;
  letter-spacing: -.05em;
  text-transform: uppercase;
  color: var(--c-graphite);
}

.t-mono-lg {
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: var(--t-mono-lg-size);
  line-height: 100%;
  letter-spacing: -.03em;
  color: var(--c-white);
}

/* presets w45aii (p) and y9h0vp (h3) — identical declarations */
.t-mono {
  font-family: var(--font-mono);
  font-weight: var(--t-mono-weight);
  font-size: var(--t-mono-size);
  line-height: var(--t-mono-lh);
  letter-spacing: var(--t-mono-ls);
  text-transform: uppercase;
  color: var(--c-black);
}

.t-mono-xs {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: var(--t-mono-xs-size);
  line-height: var(--t-mono-xs-lh);
  letter-spacing: var(--t-mono-xs-ls);
  text-transform: uppercase;
  color: var(--c-grey);
}

.t-body {
  font-family: var(--font-sans);
  font-weight: var(--t-body-weight);
  font-size: var(--t-body-size);
  line-height: var(--t-body-lh);
  letter-spacing: var(--t-body-ls);
  color: var(--c-black);
}

.t-body-sm {
  font-family: var(--font-sans);
  font-weight: var(--t-body-sm-weight);
  font-size: var(--t-body-sm-size);
  line-height: var(--t-body-sm-lh);
  letter-spacing: var(--t-body-sm-ls);
  color: var(--c-body);
}

/* Colour overrides that appear inline in the fragments. */
.t-on-dark  { color: var(--c-white); }
.t-on-light { color: var(--c-black); }

/* ---------------------------------------------------------------
   4. Shared primitives
   --------------------------------------------------------------- */

/* Framer's rich-text container: a column flex box that never shrinks. */
.rtc {
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  justify-content: flex-start;
  outline: none;
}

/* Framer splits animated headings into per-word / per-character spans.
   The word span carries white-space:nowrap (it controls line breaking) and
   each character is inline-block. Reproduce both — dropping them changes
   kerning and therefore line counts and measured heights. */
.split > span            { white-space: nowrap; }
.split span span         { display: inline-block; }

/* The 1px "Vert line" rules used as section dividers everywhere.
   The reference draws them with a ::after border on a zero-width box. */
.vline {
  position: relative;
  flex: none;
}
.vline::after {
  content: "";
  position: absolute;
  inset: 0;
  border-width: var(--border-top-width, 0) var(--border-right-width, 0)
                var(--border-bottom-width, 0) var(--border-left-width, 0);
  border-color: var(--border-color, transparent);
  border-style: var(--border-style, solid);
  box-sizing: border-box;
  border-radius: inherit;
  pointer-events: none;
  width: 100%;
  height: 100%;
}

/* Framer's [data-border] helper, kept verbatim so fragment-copied markup
   that still carries data-border="true" + inline --border-* vars renders. */
[data-border="true"]::after {
  content: "";
  border-width: var(--border-top-width, 0) var(--border-right-width, 0)
                var(--border-bottom-width, 0) var(--border-left-width, 0);
  border-color: var(--border-color, transparent);
  border-style: var(--border-style, solid);
  box-sizing: border-box;
  border-radius: inherit;
  pointer-events: none;
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
}

/* Framer's SSR variant wrapper — transparent to layout. */
.ssr-variant { display: contents; }

/* ---------------------------------------------------------------
   5. Utilities
   --------------------------------------------------------------- */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Breakpoint visibility helpers. Bounds are the REFERENCE's layout-variant
   bounds (min 1440 / 810–1439.98 / max 809.98). See SHARED-NOTES.md before
   using them — the type-scale queries use different bounds. */
@media (min-width: 1440px)                            { .hide-desktop { display: none !important; } }
@media (min-width: 810px) and (max-width: 1439.98px)  { .hide-tablet  { display: none !important; } }
@media (max-width: 809.98px)                          { .hide-phone   { display: none !important; } }

/* ---------------------------------------------------------------
   5b. HOVER MOTION — shared timing for the reference's two signature
       hover transitions.

   MEASURED on the LIVE reference (2026-08-18, 1440x900) with
   site-clone-kit/scripts/oberon-hover-measure.mjs: a real Playwright
   mouse hover, getComputedStyle sampled every 30ms through hover-IN
   and hover-OUT, then a least-squares cubic-bezier fit against the
   sampled progress curve (rmse <= 0.006 on all four).

     CTA pill fill sweep  (reference .framer-arvcc7 -> the `__edge`
     sliver each CTA already ships here)
       in   510ms  cubic-bezier(.23, .25, .25, 1.07)
       out  500ms  cubic-bezier(.4,  .03, .1,  .97)

     Nav / footer arrow link  (reference .framer-tYKN9 -> .arrow-link)
       in   150ms  cubic-bezier(.42, .15, .23, 1.02)
       out  155ms  cubic-bezier(.5,  .75, .25, 1)

   Both are Framer springs with bounce 0, played through its layout
   projection. The sampler records a 1.0015 scaleX blip on the fill at
   the very end of the sweep and a -0.15% dip at the end of the
   retract; both are projection re-measures a few tenths of a pixel
   wide, not designed overshoot, so the fitted curves stop at 1.

   IMPORTANT — what the projection numbers do NOT mean: the sampler
   also shows the label container at scaleX .90 (in) / 1.11 (out).
   That is Framer counter-scaling a box whose LAYOUT width jumps
   straight to its hover value; every child carries the inverse
   scale, so the rendered glyphs never squash — the P's own rect
   stays 198x… and only translates. Reproducing the .90 here would
   have added a squash the reference does not actually paint. The
   faithful model is: the label box grows by (icon + gap) and, being
   centred in the button, therefore slides half of that to the right,
   while the icon slides in from its parked off-box position. Both
   verified against the live sample at 58% progress to 0.01px.
   --------------------------------------------------------------- */
:root {
  --hov-cta-dur-in:    .51s;
  --hov-cta-ease-in:   cubic-bezier(.23, .25, .25, 1.07);
  --hov-cta-dur-out:   .5s;
  --hov-cta-ease-out:  cubic-bezier(.4, .03, .1, .97);

  --hov-link-dur-in:   .15s;
  --hov-link-ease-in:  cubic-bezier(.42, .15, .23, 1.02);
  --hov-link-dur-out:  .155s;
  --hov-link-ease-out: cubic-bezier(.5, .75, .25, 1);

  /* The reference's 12x12 arrow glyph (`#svg115285041_401` /
     `#svg-2137699150_403`, same path) as a mask, for the one CTA the
     clone renders without a real <svg> child (.hm-pcta). Painted with
     `background: currentColor`, so it always matches its label. */
  --arrow-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M 0 6 L 12 6 M 6 0 L 12 6 L 6 12' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* Reduced motion: the hover STATES still apply (the fill still covers,
   the arrow still shows) — they just land instantly. Zeroing the four
   duration tokens does that for every rule that uses them, so no
   component file needs its own reduced-motion branch. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --hov-cta-dur-in:  0s;
    --hov-cta-dur-out: 0s;
    --hov-link-dur-in:  0s;
    --hov-link-dur-out: 0s;
  }
}

/* ---------------------------------------------------------------
   6. Appear animations (runtime in js/main.js)

   Contract:
   - An element opts in with  data-appear="slide-x | rise | zoom".
   - Until js/main.js marks the document ready, `html.js-appear` holds
     elements at their INITIAL transform/opacity.
   - main.js adds .is-in when the element should play; the transition then
     runs to the resting state.
   - ONLY transform + opacity are animated, so measured heights never move.
   - `rise` rests at opacity 1. (Historical note: the Framer appear-JSON
     `animate` value read `opacity: 0.7`, and this file used to hold that
     literally. QA measured the LIVE reference on 2026-08-16 — scrolled
     "What we can automate" (home, `.hm-areas__main`, rise preset) into
     view at 1440x900 and read `getComputedStyle(...).opacity` on the
     heading AND every ancestor up to <html>: every link in the chain
     reports `1`, effective opacity `1`. The 0.7 in the captured JSON
     never reaches the resting DOM — Framer must resolve it against
     another multiplier we don't have, or the value is stale in the
     export. Ground truth wins over the captured JSON; see
     KNOWN-DIFFERENCES.md for the full measurement.)
   --------------------------------------------------------------- */
[data-appear] {
  will-change: transform, opacity;
}

/* The transition-* declarations live on the .is-in rules, NOT the armed
   rules. If the armed rule carried them, adding `js-appear` would itself be
   transitioned (resting -> initial, over the full preset duration) whenever
   style had already been computed for the element; anything in the first
   viewport is flipped to .is-in ~2 frames later, so the two transitions
   cancel out and the animation silently no-ops (this is why the nav logo's
   slide-x never played). With the transition on .is-in only, arming snaps
   to the initial state instantly and the reveal is the only animated change. */

/* --- slide-x : initial x -392px, opacity 1 --- */
html.js-appear [data-appear="slide-x"] {
  transform: translateX(-392px);
  opacity: 1;
}
html.js-appear [data-appear="slide-x"].is-in {
  transform: none;
  opacity: 1;
  transition-property: transform, opacity;
  transition-duration: var(--dur-slide-x);
  transition-timing-function: var(--ease-spring0);
  transition-delay: var(--delay-slide-x);
}

/* --- rise : initial opacity .001 / y +20 (phone +50) -> opacity 1 --- */
html.js-appear [data-appear="rise"] {
  transform: translateY(var(--rise-y));
  opacity: 0.001;
}
html.js-appear [data-appear="rise"].is-in {
  transform: none;
  opacity: 1;
  transition-property: transform, opacity;
  transition-duration: var(--dur-rise);
  transition-timing-function: var(--ease-rise);
  transition-delay: var(--delay-rise);
}

/* --- zoom : initial opacity .001 / scale 1.2 -> opacity 1 / scale 1 --- */
html.js-appear [data-appear="zoom"] {
  transform: scale(1.2);
  opacity: 0.001;
}
html.js-appear [data-appear="zoom"].is-in {
  transform: none;
  opacity: 1;
  transition-property: transform, opacity;
  transition-duration: var(--dur-zoom);
  transition-timing-function: var(--ease-zoom);
  transition-delay: var(--delay-zoom);
}

/* Reduced motion: never move, land on the resting state immediately. */
@media (prefers-reduced-motion: reduce) {
  html.js-appear [data-appear],
  html.js-appear [data-appear].is-in {
    transition: none !important;
    transform: none !important;
  }
  html.js-appear [data-appear="slide-x"],
  html.js-appear [data-appear="zoom"]    { opacity: 1 !important; }
  html.js-appear [data-appear="rise"]    { opacity: 1 !important; }
}
