/* Moneyweaver teaser site styles: design tokens, self-hosted type, and
   layout primitives. Palette and scale follow the approved "CreativeStudio"
   reference: light, airy, pastel — soft gradient blobs, pill buttons,
   flat pastel cards, near-zero hard shadows. */

/* ==========================================================================
   Palette tokens
   Every template and future component reads colour exclusively through
   these custom properties — never a literal hex outside this block — so
   site/contrast_test.go can keep a future edit honest by parsing the
   values straight out of this file.

   Contrast rule of thumb used throughout this file (see contrast_test.go
   for the numbers): --text-primary is safe as body/heading text on *any*
   surface in this palette (>= 11:1 against every token below). --text-muted
   is only verified >= 4.5:1 against --page-bg and --cream — it must not be
   used as text sitting on a pastel card fill (pink/lavender/amber/peach),
   where it dips as low as 4.2:1. Cards use --text-primary for all their
   text for that reason.
   ========================================================================== */
:root {
    --page-bg: #FEFEFE;         /* ground (dominant) */
    --cream: #FEF9F6;           /* warm wash, hero atmosphere */
    --peach: #FEE3C6;           /* blob accent */
    --accent: #FAC738;          /* CTA / pill button fill */
    --card-pink: #FFE5E6;
    --card-lavender: #E6E4FC;
    --card-amber: #FEEED5;
    --text-primary: #2B2B3D;    /* headline / primary text */
    --text-muted: #6B6B7B;      /* body / muted text -- page-bg & cream only, see above */
    --hairline: rgba(43, 43, 61, 0.08); /* whisper-soft separator, not a hard rule */
}

/* ==========================================================================
   Fonts: self-hosted Poppins (display) and Karla (body)

   Both are subset to Latin + general punctuation (the same range Google
   Fonts calls its "latin" subset) and served as WOFF2 from this origin —
   no third-party requests. See site/assets/fonts/LICENSES.md for
   provenance, versions, and checksums.

   Poppins ships from Google Fonts as static per-weight files rather than a
   variable font (unlike Karla). Every display-font usage in this design —
   h1/h2/h3, the wordmark, the principles <dt> labels — renders at exactly
   font-weight 700, so only the Bold static file is shipped; there is no
   axis-instancing step to document here the way there was for the old
   variable Fraunces file.
   ========================================================================== */
@font-face {
    font-family: 'Poppins';
    src: url('/assets/fonts/poppins-bold-latin.woff2') format('woff2');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F,
        U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Karla';
    src: url('/assets/fonts/karla-variable-latin.woff2') format('woff2');
    font-weight: 200 800;
    font-style: normal;
    font-display: swap;
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F,
        U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ---- Metric-matched fallbacks (avoid layout shift on the swap) ----------
   `font-display: swap` means the fallback renders first and gets replaced
   once the woff2 loads; if the fallback's line-box metrics and average
   glyph width differ from the webfont's, that swap reflows the page. Each
   of these declares a *named* fallback face — sized and shaped to match
   its webfont — that sits between the real font and the generic keyword
   in the stack below.

   We don't have Arial installed in this build sandbox to measure directly
   (`fc-list` finds neither; only DejaVu/Liberation are present), so the
   fallback-side numbers below are NOT measured in this environment. They
   come from the seek-oss/capsize metrics dataset (MIT-licensed,
   https://github.com/seek-oss/capsize, values extracted from the actual
   Arial font file: unitsPerEm 2048, ascent 1854, descent -434, lineGap 67,
   xAvgCharWidth 913) rather than a guess. Poppins/Karla's own metrics
   (unitsPerEm, hhea ascent/descent, xAvgCharWidth) were read directly from
   the subsetted files with fonttools. "system-ui" itself can't be targeted
   by `local()` — it resolves to a different actual font per platform
   (Segoe UI, San Francisco, Roboto, ...) — so both fallback faces are
   measured against Arial as the closest documented proxy and offered to
   the engine via a short list of common installed sans fonts; this is a
   known approximation, not exact on every platform.

   Formula (the standard technique documented by web.dev, "Overriding
   fallback font metrics", and implemented the same way by tools like
   Fontaine/next-font):
     sizeAdjust      = (webFont.xAvgCharWidth / webFont.unitsPerEm)
                        / (fallback.xAvgCharWidth / fallback.unitsPerEm)
     ascent-override = (webFont.ascent  / webFont.unitsPerEm) / sizeAdjust
     descent-override= (|webFont.descent| / webFont.unitsPerEm) / sizeAdjust
     line-gap-override = (webFont.lineGap / webFont.unitsPerEm) / sizeAdjust
   size-adjust scales the fallback's glyph widths (and, before the
   overrides below reassert them, its metrics) so an average character
   comes out close to the same width as the webfont at the same
   font-size; the override descriptors then fix the line-box metrics
   independently of that scaling, per the CSS Fonts Level 4 spec (the
   override percentages are relative to the *already size-adjusted* em).
   ---------------------------------------------------------------------- */
/* No unicode-range on either fallback face (deliberate, not an
   omission): this face stands in for the *entire* webfont while it
   loads, not one range of it, so restricting it to a subset would
   leave characters outside that subset rendered in an untuned system
   font — reintroducing the reflow this technique exists to prevent. */
@font-face {
    font-family: 'Poppins Fallback';
    src: local('Arial'), local('Helvetica Neue'), local('Segoe UI');
    size-adjust: 195.83%;
    ascent-override: 53.62%;
    descent-override: 17.87%;
    line-gap-override: 5.11%;
}

@font-face {
    font-family: 'Karla Fallback';
    src: local('Arial'), local('Helvetica Neue'), local('Segoe UI');
    size-adjust: 114.29%;
    ascent-override: 80.24%;
    descent-override: 22.05%;
    line-gap-override: 0%;
}

:root {
    --font-display: 'Poppins', 'Poppins Fallback', Arial, Helvetica, sans-serif;
    --font-body: 'Karla', 'Karla Fallback', system-ui, sans-serif;
}

/* ==========================================================================
   Type scale

   Fluid via clamp(), computed with the standard linear-interpolation
   technique (as documented by Utopia, https://utopia.fyi/type/calculator):
   for each step, a value at a small viewport and a value at a large
   viewport are connected by a straight line in viewport-width units, then
   clamped so it never goes below/above those two bounds.

     min viewport 375px, min base (step-0) 16px, min ratio 1.125
     max viewport 1440px, max base (step-0) 20px, max ratio computed so
     step-4's upper bound lands exactly on the spec value (3.815rem)

   The same min/max ratio pair (1.125 / 1.32174) is applied uniformly
   across every step from -1 to 4, so the whole scale is one coherent
   system rather than a special case per step. That uniformity does
   have a limit: one step further out, at step -2, the larger
   max-viewport ratio would shrink the negative exponent faster than
   the smaller min-viewport ratio and invert the clamp() (upper bound
   below the lower bound, which just freezes it at the lower value) —
   which is why the scale stops at step -1 rather than extending down
   to step -2.
   ========================================================================== */
:root {
    --step--1: clamp(0.8889rem, 0.8689rem + 0.0854vw, 0.9457rem); /* 14.2px -> 15.1px */
    --step-0: clamp(1rem, 0.912rem + 0.3756vw, 1.25rem);       /* 16px -> 20px */
    --step-1: clamp(1.125rem, 0.9394rem + 0.792vw, 1.6522rem); /* 18px -> 26.4px */
    --step-2: clamp(1.2656rem, 0.9423rem + 1.3793vw, 2.1837rem);
    --step-3: clamp(1.4238rem, 0.9089rem + 2.1972vw, 2.8863rem);
    --step-4: clamp(1.6018rem, 0.8225rem + 3.325vw, 3.815rem); /* 25.6px -> 61px */
}

/* ==========================================================================
   Spacing, radius and motion tokens
   ========================================================================== */
:root {
    --space-1: 0.25rem;  /* 4px */
    --space-2: 0.5rem;   /* 8px */
    --space-3: 0.75rem;  /* 12px */
    --space-4: 1rem;     /* 16px */
    --space-5: 1.5rem;   /* 24px */
    --space-6: 2rem;     /* 32px */
    --space-7: 3rem;     /* 48px */
    --space-8: 4rem;     /* 64px */

    --measure: 65ch;

    --radius-sm: 12px;
    --radius-card: 20px;
    --radius-pill: 999px;

    /* Reference language: "almost no hard shadows (at most a very soft
       diffuse one)" -- this is the one shadow in the whole system, used
       only on hover for cards/buttons, never at rest. */
    --shadow-soft: 0 20px 40px -20px rgba(43, 43, 61, 0.25);

    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
    --duration-fast: 0.2s;
    --duration-entrance: 0.7s;
    --duration-drift: 22s;
}

/* ==========================================================================
   Base layer
   ========================================================================== */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html {
    /* Prevents a visible reflow of the fallback -> webfont swap on the
       adjustable weight axis while keeping optical sizing on the fluid
       heading ramp above automatic (the UA default). */
    font-optical-sizing: auto;
}

body {
    background: var(--page-bg);
    color: var(--text-primary);
    font-family: var(--font-body);
    font-size: var(--step-0); /* >= 16px at the 375px floor */
    font-weight: 400;
    line-height: 1.6;
    /* Hero blobs intentionally bleed past their own section edge (see
       "Hero atmosphere" below); this is the backstop that guarantees that
       never turns into a horizontal scrollbar on the page itself. */
    overflow-x: hidden;
}

h1, h2, h3 {
    font-family: var(--font-display);
    font-weight: 700;
    line-height: 1.15;
    margin-block: 0 var(--space-4);
}

h1 { font-size: var(--step-4); }
h2 { font-size: var(--step-3); }
h3 { font-size: var(--step-2); }

p {
    margin-block: 0 var(--space-4);
}

small, .text-small {
    font-size: var(--step--1);
}

/* Inline prose links (mailto, "back to moneyweaver", ...): the accent
   amber fails text contrast outright (~1.6:1 on --page-bg), so links are
   --text-primary with an amber underline instead of amber text -- full
   contrast for the readable part, the accent colour still shows up, and
   the underline means the link is never signalled by colour alone. */
a {
    color: var(--text-primary);
    text-decoration: underline;
    text-decoration-color: var(--accent);
    text-decoration-thickness: 2px;
    text-underline-offset: 0.2em;
}

a:hover,
a:focus-visible {
    text-decoration-color: var(--text-primary);
}

img, svg, video, canvas {
    max-width: 100%;
    height: auto;
}

::selection {
    background: var(--accent);
    color: var(--text-primary);
}

/* Never remove this without a replacement — :focus-visible only fires for
   keyboard/non-pointer interaction, so this never adds a ring to a mouse
   click, but every keyboard user keeps one. */
:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

/* Standard clip-not-hide pattern: content stays in the accessibility
   tree and reachable by keyboard/screen reader, but occupies no visual
   space and cannot be scrolled to. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ==========================================================================
   Motion: entrance, drift, hover -- and the reduced-motion kill switch

   Three animated things in this design: a slow blob drift/float loop in
   the hero, a fade-and-rise entrance on each major section, and small
   hover lifts on the button and cards. All three are tasteful (long
   duration, small distance, no bounce/spin), and every one of them is
   fully neutralised below under `prefers-reduced-motion: reduce` -- not
   just slowed down.
   ========================================================================== */
@keyframes section-rise {
    from { opacity: 0; transform: translateY(20px); }
    to   { opacity: 1; transform: translateY(0); }
}

@keyframes blob-drift {
    0%   { transform: translate(0, 0) scale(1); }
    50%  { transform: translate(3%, -4%) scale(1.06); }
    100% { transform: translate(0, 0) scale(1); }
}

.section,
.hero__content {
    animation: section-rise var(--duration-entrance) var(--ease-out) both;
}

.hero__content { animation-delay: 0ms; }
.problems { animation-delay: 80ms; }
.principles { animation-delay: 140ms; }
.waitlist { animation-delay: 200ms; }

@media (prefers-reduced-motion: reduce) {
    /* Belt-and-braces: kill every animation and transition outright
       (rather than merely shortening them), then explicitly restore the
       end-state of anything an animation was responsible for revealing --
       so "reduced motion" can never mean "the section never appears".

       Every selector below must match real markup, not an aspirational
       class name: `.problems__list li` and `.principles__item` are the
       actual pastel cards (there is no `class="card"` anywhere in the
       templates -- that selector used to be dead here). Because these
       selectors match the element itself, not just its resting state,
       the !important above beats the (non-!important) hover rules for
       `.problems__list li:hover`, `.principles__item:hover`, and
       `.btn:hover`/`:focus-visible` regardless of the hover pseudo-class's
       higher specificity -- !important always wins ties over specificity
       -- so hover's transform/box-shadow is neutralised too, not just the
       entrance animation. */
    *, *::before, *::after {
        animation: none !important;
        transition: none !important;
        scroll-behavior: auto !important;
    }

    .section,
    .hero__content,
    .blob,
    .problems__list li,
    .principles__item,
    .btn {
        opacity: 1 !important;
        transform: none !important;
        box-shadow: none !important;
    }
}

/* ==========================================================================
   Layout primitives

   `.wrap` is the centred text column templates use for prose content
   (headings, paragraphs, forms) — capped at --measure (65ch) so a line
   never runs past a comfortable reading width, with side padding so it
   never touches the viewport edge on narrow screens. The problems and
   principles sections override this to a wider 1100px cap (see "Pastel
   card grid" below) since their content is a 3-up card grid, not prose.

   `.section` gives templates consistent vertical rhythm from the spacing
   scale. Unlike the old dark design, sections are separated by generous
   whitespace alone -- no hard rule between them -- to match the airy,
   borderless reference.

   Checked by reasoning about the rules (no headless browser available
   here) at 375 / 768 / 1024 / 1440px: --measure is a ch-based cap so it
   never forces a track wider than the viewport once side padding is
   subtracted; the type scale's clamp() bounds were computed above to
   stay within [min, max] at every width (no unbounded growth past
   1440px, no shrinkage below the 375px floor); spacing is a fixed rem
   scale, not viewport-relative, so it doesn't introduce overflow; the
   card grids collapse to one column below 768px so nothing overflows
   there either; and the hero blobs are absolutely positioned inside an
   `overflow: hidden` hero and backstopped by `overflow-x: hidden` on
   body, so their negative offsets can never create a horizontal
   scrollbar. This reasoning has not been confirmed against a rendered
   browser.
   ========================================================================== */
.wrap {
    max-width: var(--measure);
    margin-inline: auto;
    padding-inline: var(--space-4);
}

.section {
    padding-block: var(--space-8);
}

/* ==========================================================================
   Hero atmosphere: soft gradient blobs

   Replaces the old woven-loom signature. Two decorative, `aria-hidden`
   divs sit behind the hero copy: a large peach blob and a smaller
   lavender one, both soft-edged via a heavy blur and both bleeding off
   the hero's own edges. `.hero` clips them (`overflow: hidden`) so they
   never cause layout overflow; `blob-drift` moves them a few percent on
   a slow 22s loop for a gentle, ambient float -- never fast or large
   enough to be distracting, and fully disabled under reduced motion
   above.
   ========================================================================== */
.hero {
    position: relative;
    overflow: hidden;
    background: var(--cream);
}

.hero__content {
    position: relative;
    z-index: 1;
    padding-block: var(--space-8);
    text-align: center;
}

.hero__sub {
    font-size: var(--step-1);
    color: var(--text-muted);
    max-width: 50ch;
    margin-inline: auto;
}

.blob {
    position: absolute;
    z-index: 0;
    border-radius: 50%;
    filter: blur(60px);
    opacity: 0.8;
    animation: blob-drift var(--duration-drift) var(--ease-out) infinite;
}

.blob--peach {
    width: min(60vw, 620px);
    height: min(60vw, 620px);
    top: -18%;
    right: -12%;
    background: radial-gradient(circle at 35% 35%, #FFF1DE, var(--peach) 70%);
}

.blob--lavender {
    width: min(40vw, 420px);
    height: min(40vw, 420px);
    bottom: -20%;
    left: -10%;
    background: radial-gradient(circle at 35% 35%, #F3F2FF, var(--card-lavender) 70%);
    animation-delay: -8s; /* desync from the peach blob */
}

/* ---- Section headings ----------------------------------------------------
   "Eyebrow" captions: a small, centred, muted, letter-spaced line that
   introduces the problem beats and the principles grids -- the reference
   pattern of "a small muted eyebrow line above ... a 3-up card grid". Both
   headings already exist as copy-reviewed text; this only changes how
   they're presented (visible, styled), never the words themselves. */
.eyebrow {
    text-align: center;
    color: var(--text-muted);
    font-family: var(--font-body);
    font-size: var(--step--1);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    margin-block-end: var(--space-6);
}

.waitlist h2 {
    text-align: center;
}

.waitlist > .wrap > p {
    text-align: center;
    color: var(--text-muted);
}

/* ---- Pastel card grid ------------------------------------------------
   Shared by the problem beats and the principles: flat pastel fill,
   ~20px rounded corners, no border, centred content, a soft hover raise.
   Colour cycles pink -> lavender -> amber across the three cards, fixed
   order (never randomised) so a re-render never reshuffles which beat
   gets which colour. All in-card text is --text-primary, never
   --text-muted -- see the contrast note on the palette tokens above. */
/* These two sections carry a 3-up card grid, not prose, so their .wrap
   gets a wider cap than the 65ch reading measure everything else uses --
   otherwise three cards squeeze into a reading-width column and the
   reference's "very generous whitespace" reads as cramped instead. */
.problems .wrap,
.principles .wrap {
    max-width: 1100px;
}

.problems__list,
.principles__list {
    list-style: none;
    display: grid;
    gap: var(--space-5);
}

@media (min-width: 768px) {
    .problems__list,
    .principles__list {
        grid-template-columns: repeat(3, 1fr);
    }
}

.problems__list li,
.principles__item {
    border-radius: var(--radius-card);
    padding: var(--space-6) var(--space-5);
    color: var(--text-primary);
    transition: transform var(--duration-fast) var(--ease-out),
                box-shadow var(--duration-fast) var(--ease-out);
}

.problems__list li:hover,
.principles__item:hover {
    transform: translateY(-6px);
    box-shadow: var(--shadow-soft);
}

.problems__list li:nth-child(3n+1),
.principles__item:nth-child(3n+1) { background: var(--card-pink); }
.problems__list li:nth-child(3n+2),
.principles__item:nth-child(3n+2) { background: var(--card-lavender); }
.problems__list li:nth-child(3n+3),
.principles__item:nth-child(3n+3) { background: var(--card-amber); }

.problems__list li {
    font-family: var(--font-display);
    font-size: var(--step-1);
    line-height: 1.3;
    text-align: center;
}

.principles__item {
    text-align: center;
}

.principles__item dt {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: var(--step-1);
    color: var(--text-primary);
    margin-block-end: var(--space-2);
}

.principles__item dd {
    margin-inline-start: 0;
    color: var(--text-primary);
}

/* ==========================================================================
   Page chrome and content components

   Skip-link, header wordmark, footer identity block, and the waitlist
   form. All colour continues to come from the palette tokens at the top
   of this file -- nothing here introduces a new literal colour.
   ========================================================================== */

.skip-link {
    position: absolute;
    top: -3rem;
    left: var(--space-4);
    z-index: 10;
    background: var(--accent);
    color: var(--text-primary);
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-sm);
    font-weight: 700;
    text-decoration: none;
    transition: top 0.15s ease;
}

.skip-link:focus {
    top: var(--space-4);
}

.site-header {
    padding-block: var(--space-5);
}

.wordmark {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: var(--step-1);
    color: var(--text-primary);
    text-decoration: none;
}

.wordmark:hover,
.wordmark:focus-visible {
    color: var(--text-primary);
    text-decoration: underline;
    text-decoration-color: var(--accent);
}

/* ---- Waitlist form -------------------------------------------------------
   44px is the WCAG 2.5.5 / iOS HIG minimum touch target; the submit
   button's min-height/min-width enforce it regardless of content size.
   Inputs follow the reference's "flat pastel fills, no borders" language:
   a soft cream fill stands in for a border, with the shared focus ring
   above providing the visible focus state. */

.form-field {
    margin-block-end: var(--space-4);
}

.form-field label {
    display: block;
    margin-block-end: var(--space-2);
}

.waitlist-form input[type="email"],
.waitlist-form input[type="text"] {
    width: 100%;
    max-width: 40ch;
    padding: var(--space-3);
    background: var(--cream);
    border: none;
    border-radius: var(--radius-sm);
    color: var(--text-primary);
    font-family: var(--font-body);
    font-size: var(--step-0);
}

.form-error {
    border-radius: var(--radius-sm);
    background: var(--card-pink);
    padding: var(--space-4);
    margin-block-end: var(--space-4);
    color: var(--text-primary);
}

.form-confirm {
    border-radius: var(--radius-sm);
    background: var(--card-amber);
    padding: var(--space-4);
    margin-block-end: var(--space-4);
    color: var(--text-primary);
}

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    min-width: 44px;
    padding-inline: var(--space-6);
    background: var(--accent);
    color: var(--text-primary);
    border: none;
    border-radius: var(--radius-pill);
    font-family: var(--font-body);
    font-weight: 700;
    font-size: var(--step-0);
    cursor: pointer;
    transition: transform var(--duration-fast) var(--ease-out),
                box-shadow var(--duration-fast) var(--ease-out),
                filter var(--duration-fast) var(--ease-out);
}

.btn:hover,
.btn:focus-visible {
    transform: translateY(-3px);
    box-shadow: var(--shadow-soft);
    filter: brightness(0.97);
}

/* ---- Footer ------------------------------------------------------------
   A whisper-soft hairline stands in for the old hard rule -- present
   enough to separate the footer from the page, faint enough to keep the
   airy, borderless reference feel. */

.site-footer {
    padding-block: var(--space-7);
    border-top: 1px solid var(--hairline);
}

.site-footer__identity {
    font-style: normal;
    color: var(--text-muted);
    margin-block-end: var(--space-4);
}

.site-footer__disclaimer {
    color: var(--text-muted);
    max-width: var(--measure);
}
