/* ============================================================================
 * Heliomath COMPONENTS — reusable, class-based UI built on the tokens.
 *
 * The components layer. Unlike base.css (bare-element defaults, global by
 * nature), everything here is a CLASS selector, so it only styles elements that
 * opt in by using the class. Load order: tokens -> base -> components -> utilities.
 *
 * TOKENS: colour / type / structure come from heliomath_tokens.css via var().
 * No hard-coded hex or raw px type sizes.
 *
 * NAMING: BEM. Buttons are `.btn` (no prefix — this is THE button) + `--variant`
 * modifiers, mirroring the .mp-ld-*__el / --modifier vocabulary elsewhere.
 *
 * ⚠ `.btn` COLLIDES WITH BOOTSTRAP 4 (loaded globally in external_base.html, which
 * also owns `.btn`), and until Heliomath sheds that ported Bootstrap/Grokkoli stack
 * this file may load on pages that still carry legacy `.btn-grokkoli-*` buttons.
 * Every bare `.btn` selector below therefore USED TO carry a
 * `:not(.btn-grokkoli-primary):not(.filter)` guard, so the base skipped them.
 *
 * ⚠ THOSE GUARDS ARE CURRENTLY REMOVED (under evaluation) — stripped from all 14
 * selectors, so the base now applies to every `.btn`, legacy ones included. Two
 * consequences, both measured: legacy `.btn-grokkoli-*` buttons take the
 * design-system geometry, AND the base drops from specificity (0,3,0) to (0,1,0),
 * so page-level rules such as grokkoli_system's `.flp-buttons .btn` (0,2,0) now beat
 * it — which shrinks some buttons that were previously correct. To restore, re-add
 * `:not(.btn-grokkoli-primary):not(.filter)` to every bare `.btn` selector in this
 * file. Our own buttons always carry a `.btn--variant`, so they are unaffected
 * either way, and when the ported stack is retired the question goes away: the
 * pristine `.btn` base is simply correct. (`--btn-ring` lives on the variants, not
 * the base, so under neither arrangement can the base override a per-variant ring.)
 *
 * This file starts with BUTTONS; further components (logo below; cards, inputs…)
 * append under their own banners.
 * ========================================================================== */


/* ============================================================================
 * COLOUR ROLES  —  accent (primary) + flare (secondary), as utilities
 *
 * Two accent roles, meant to be used LIBERALLY on headers, marks, chips, etc.
 * (and as the button variants below):
 *   accent  ->  --accent-primary (cornflower) — the primary accent
 *   flare   ->  the SECONDARY accent (--flare; coral/peach today). The name is the
 *               ROLE, not the hue, so it survives a future swap (e.g. green/cyan).
 *
 *   .text-*   colours FOREGROUND (text). Use flare on LARGE text only on light
 *             surfaces — coral fails WCAG at body sizes.
 *   .fill-*   paints a BACKGROUND *and* its contrasting label together — never a
 *             bare background (that is how you get an unreadable label).
 *
 * SURFACE ADAPTATION (defined ONCE, here): the flare COLOUR (and its focus ring)
 * brightens to peach on dark bands — .text-flare / .fill-flare / .btn--flare read
 * the --flare-* context tokens, so they adapt with NO per-component dark override.
 * The flare LABEL token (--flare-label) adapts too, but only .fill-flare uses it:
 * the BUTTONS carry a fixed dark (midnight) label, which clears contrast on solar,
 * sunbeam AND cornflower, so it needs no surface flip. Accent is surface-stable — but
 * its FILL is dark enough (true-blue) that .btn--accent flips its own label to white.
 * ========================================================================== */
:root {
    --flare-paint:        var(--flare);              /* the flare colour, in context */
    --flare-paint-hover:  var(--flare-hover);
    --flare-paint-active: var(--flare-active);
    --flare-label:        var(--text-primary);       /* label on a flare fill: MIDNIGHT on solar (10.76:1). Was white, which is 1.72:1 on solar — the old coral could carry white, solar cannot. */
    --flare-ring:         var(--focus-ring-flare);
    /* Flare as TEXT, kept separate from the FILL paint above: text takes the -deep tier
     * (solar-deep), a step down from the fill's solar, because a fill only has to be seen
     * while a glyph has to be read. ⚠ On a light surface it is still nowhere near
     * readable — 1.83:1 on beige, against solar's 1.57:1 — so this is the better of two
     * unusable options, not a fix. Warm TEXT on light has no good answer in this family;
     * that is what --accent-ink exists for. */
    --flare-ink:          var(--flare-hover);
    /* The 2px edge a solar fill needs on a LIGHT surface: the fill itself is only
     * 1.57:1 against beige, below the WCAG 1.4.11 non-text minimum, so the boundary
     * has to carry the contrast (--solar-edge is 3.35:1). Transparent on the dark
     * bands below — sunbeam on midnight is 11.49:1 and needs no help. */
    --flare-edge:         var(--flare-border);
}
/* THE SHARED DARK-CONTEXT BLOCK — what a surface re-points when it goes dark.
 *
 * Page-local dark bands can't join this list: this file loads on every Heliomath page
 * while they are page-scoped, and .light-hero is a DARK surface on pricing/about but a
 * LIGHT one on home. They repeat these declarations in their own page file — grep
 * `--font-weight-bold-on-dark` to find every dark scope. */
.mp-section--dark,
.mp-section--dark-alt,
.dark-blue-background,
body.auth-page {
    --flare-paint:        var(--flare-on-dark);      /* peach */
    --flare-paint-hover:  var(--flare-on-dark-hover);
    --flare-paint-active: var(--flare-on-dark-active);
    --flare-label:        var(--text-on-light-fill); /* night — white fails on sunbeam (1.66:1) */
    --flare-ring:         var(--focus-ring-flare-on-dark);
    /* On dark the fill tone IS the readable one (sunbeam, 11.49:1 on midnight), so text
     * and fill share it — no step down, which would only dim it. */
    --flare-ink:          var(--flare-on-dark);
    --flare-edge:         transparent;                /* sunbeam on midnight is 11.49:1 — the fill carries its own boundary */

    --font-weight-light:    var(--font-weight-light-on-dark);
    --font-weight-regular:  var(--font-weight-regular-on-dark);
    --font-weight-medium:   var(--font-weight-medium-on-dark);
    --font-weight-semibold: var(--font-weight-semibold-on-dark);
    --font-weight-bold:     var(--font-weight-bold-on-dark);
    /* BOTH HALVES ARE NEEDED. The token re-point above does not reach plain body copy:
     * `body, #main` in heliomath_base.css resolves var(--font-weight-regular) to 300 AT
     * THE BODY, and inheritance passes that computed 300 down, so <p>/<td>/<li> keep it
     * whatever the tokens say here. Restating it re-resolves against this block's ladder.
     * Only elements declaring their own `font-weight: var(...)` thin without this. */
    font-weight: var(--font-weight-regular);
}

/* LIGHT ISLANDS inside a dark band take the base ladder back.
 * SPECULATIVE: no template currently nests one of these inside a dark section. */
.mp-section--dark .mp-section--surface,
.mp-section--dark .mp-section--elevated,
.mp-section--dark-alt .mp-section--surface,
.mp-section--dark-alt .mp-section--elevated,
.dark-blue-background .card,
.mp-section--dark .card,
.mp-section--dark-alt .card {
    --font-weight-light:    var(--font-weight-light-on-light);
    --font-weight-regular:  var(--font-weight-regular-on-light);
    --font-weight-medium:   var(--font-weight-medium-on-light);
    --font-weight-semibold: var(--font-weight-semibold-on-light);
    --font-weight-bold:     var(--font-weight-bold-on-light);
    font-weight: var(--font-weight-regular);
}

/* Foreground (text): headers, ✓/✗ marks, inline emphasis. */
.text-accent { color: var(--accent-primary); }
.text-flare  { color: var(--flare-ink); }   /* the TEXT tier, not the fill — see --flare-ink */
/* The link blue (azure) as a foreground utility for NON-link emphasis text. Same
 * family as .text-accent but darker/more saturated, so it clears WCAG at BODY size
 * where accent (cornflower) doesn't — reach for this on small/body-size coloured
 * emphasis (e.g. the bold inline labels in a dense list). Always underlined <a>
 * links get --link from base.css already; this is for text that isn't a link. */
.text-link   { color: var(--link); }
/* The TEXT accent — a title/eyebrow/emphasis colour for LIGHT surfaces, where
 * .text-flare is unusable (solar is 1.57:1 on beige). Currently --jade #009990 (on
 * trial): 3.52:1 on white, 3.22:1 on beige, 2.93:1 on the sunken sand band — DISPLAY
 * AND HEADING SIZES ONLY, and under even AA-large on sand. See the --teal and --jade
 * notes in heliomath_tokens.css. NOT surface-adaptive by declaration, though this value
 * does happen to read on dark (5.24:1 on midnight). */
.text-accent-ink { color: var(--accent-ink); }

/* Fill: a painted element that carries its own contrasting label. */
.fill-accent { background-color: var(--accent-primary); color: var(--text-on-dark-fill); }
.fill-flare  { background-color: var(--flare-paint);    color: var(--flare-label); border: 2px solid var(--flare-edge); }


/* ============================================================================
 * BUTTONS  —  .btn (structural base) + a --variant skin.
 *
 *   .btn           structural base — layout + the shared dark (midnight) label
 *   .btn--accent   + cornflower fill — any surface
 *   .btn--flare    + secondary-accent fill (coral on light, peach on dark, via the
 *                  --flare-* context tokens above)
 *   .btn--accent-secondary / .btn--flare-secondary
 *                  the OUTLINE peers of the two fills — white ground, 1px border and
 *                  label in the accent. Same shape/size as the fills.
 *   .btn--block    width modifier — the button spans its container.
 *
 * Each variant supplies only its FILL, hover/active shades and matching focus ring;
 * the base owns the label. Variant colours reuse the accent / flare roles above.
 * Works on <button> and <a>. Pill by default.
 * ========================================================================== */

/* Generic .btn box geometry — ported from base.css (which Heliomath no longer loads).
 * MUST sit BEFORE the guarded base + the .btn--* variants below: it is a single-class
 * selector like them, so on equal specificity SOURCE ORDER decides — the variants'
 * fills (background-color: var(--flare-paint) etc.) have to come later to win. (base.css
 * loaded before this file, giving that order for free; here we recreate it.) Heliomath's
 * own buttons layer the guarded base + a variant on top; this bare rule mainly gives the
 * LEGACY .btn-grokkoli-* buttons (e.g. the About contact submit) their box. Its #212529
 * colour is always overridden downstream, so it never paints. */
.btn {
    display: inline-block;
    font-weight: var(--font-weight-medium);
    color: #212529;
    text-align: center;
    vertical-align: middle;
    -webkit-user-select: none;
    user-select: none;
    background-color: transparent;
    border: 1px solid transparent;
    padding: 0.375rem 0.75rem;
    font-size: 1rem;
    line-height: 1.5;
    border-radius: 0.25rem;
    transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out;
}
.btn:not(:disabled):not(.disabled) { cursor: pointer; }

/* --- Structural base (layout + the shared dark label; FILL + ring come from the
 *     variant) --- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5em;                        /* room for an icon beside the label */
    margin: 0;
    padding: 0.85rem 1.7em;             /* 15px / 28px at the 18px label floor; scales with the label */
    border: 1px solid transparent;     /* keeps fills and outline variants the same box size */
    border-radius: var(--radius-pill);
    /* Fluid label from the token (--type-control-size): floor 18px, cap 24px. Our
     * button fills all take a dark midnight label (set here, so a variant needn't
     * repeat it); the token holds the contrast note — cornflower is the tightest. */
    font-family: var(--font-family-base);
    font-size: var(--type-control-size);
    font-weight: var(--font-weight-bold);
    color: var(--text-primary);        /* dark (midnight) label — shared by every variant */
    line-height: 1.2;
    text-align: center;
    text-decoration: none;             /* for <a class="btn"> */
    white-space: nowrap;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
    transition: background-color 0.18s ease, color 0.18s ease,
                box-shadow 0.18s ease, transform 0.18s ease;
}
.btn:hover {
    opacity: 1;
}

/* Pressed: a subtle downward nudge PLUS the variant's own colour ring — a solid
 * 2px OUTLINE held 3px off the edge (outline-offset), so it reads as a detached ring
 * that follows the pill contour, NOT the old translucent box-shadow that hugged the
 * button. The press ring is the variant's PRESSED fill (--btn-ring-press), so ring and
 * background are the same colour at the moment of the press; it falls back to the rest
 * fill for any variant that hasn't declared one. Unlike the focus ring below, this
 * fires on ANY press — including the hero CTA, which is a non-focusable
 * <div class="btn"> that never gets :focus but does get :active. */
.btn:active {
    transform: translateY(1px);
    outline: 2px solid var(--btn-ring-press, var(--btn-ring-color));
    outline-offset: 3px;
    box-shadow: none;    /* kill base.css a:focus azure link ring on <a class="btn"> */
}

/* Focus ring — the same detached fill-colour outline, shown on ANY focus INCLUDING
 * a mouse click/press: deliberately NOT gated to :focus-visible, so the ring appears
 * on press and PERSISTS until blur — the style-guide button behaviour. Needs a
 * focusable element: a <button> / <a class="btn"> gets it; a non-focusable
 * <div class="btn"> can only ever show the transient :active ring above. Floor-safe
 * (plain :focus, no :focus-visible dependency). */
.btn:focus {
    outline: 2px solid var(--btn-ring-color);
    outline-offset: 3px;
    box-shadow: none;    /* kill base.css a:focus azure link ring on <a class="btn"> */
}
/* A mouse press makes a focusable button :active AND :focus at once, and the two rules
 * above are both (0,2,0) — so without this the LATER one (focus) would paint, and the
 * press ring would never be seen on a <button> or <a class="btn">. Re-state the press
 * colour after it so the press wins while the button is held, then the focus ring takes
 * over on release and persists until blur. */
.btn:active {
    outline-color: var(--btn-ring-press, var(--btn-ring-color));
}

/* Disabled: dim, no press. (For <a>, use aria-disabled and stop navigation in
 * JS — a link can't be natively disabled.) */
.btn:disabled,
.btn[aria-disabled="true"] {
    opacity: 0.5;
    cursor: not-allowed;
    transform: none;
}

/* --- Variant: accent (cornflower) — any surface ------------------------- */
/* Runs the accent BUTTON ramp (--btn-accent-*), which goes DARK -> LIGHT: darkest at
 * rest, middle on hover, lightest on press — the reverse of the link ramp. See the
 * note on those tokens in heliomath_tokens.css.
 *
 * Also the ONE fill that overrides the base's shared midnight label: every tone in
 * this ramp is a deep blue, and midnight on the lightest of them is 2.27:1. White is
 * 8.12:1 there and higher on the two darker tones. Its on-dark peer below keeps
 * midnight — cornflower-soft is light enough that midnight reads 5.60:1 and white
 * would only reach 2.9:1. */
.btn--accent {
    --btn-ring: var(--focus-ring);              /* accent ring (soft box-shadow, legacy) */
    --btn-ring-color: var(--btn-accent-fill);   /* detached outline ring — exact rest fill, full opacity */
    --btn-ring-press: var(--btn-accent-press);  /* …and the pressed fill while held */
    background-color: var(--btn-accent-fill);
    color: var(--text-on-dark-fill);
}
.btn--accent:hover  { background-color: var(--btn-accent-hover); }
.btn--accent:active { background-color: var(--btn-accent-press); }

.btn--accent-on-dark {
    --btn-ring: var(--focus-ring);              /* cornflower ring (soft box-shadow, legacy) */
    --btn-ring-color: var(--accent-primary-on-dark);    /* detached outline ring — exact fill colour, full opacity */
    --btn-ring-press: var(--accent-primary-on-dark-active);  /* …and the pressed fill while held */
    background-color: var(--accent-primary-on-dark);
}
.btn--accent-on-dark:hover  { background-color: var(--accent-primary-on-dark-hover); }
.btn--accent-on-dark:active { background-color: var(--accent-primary-on-dark-active); }


/* --- Variant: flare (secondary accent) — fill adapts to its surface ------ *
 * coral fill on light, peach fill on dark, plus the matching focus ring — all via
 * the --flare-* context tokens. (The label is the base .btn's midnight, on both.) */
/* 2px --flare-edge instead of the base's 1px transparent, because a solar fill on a
 * light surface is 1.57:1 and needs its boundary to carry the contrast. The padding
 * gives back the 1px the thicker border takes, so a flare button stays exactly the
 * same size as an accent one beside it. On dark, --flare-edge is transparent — same
 * box, no visible edge. */
.btn--flare {
    --btn-ring: var(--flare-ring);
    --btn-ring-color: var(--flare-paint);       /* detached outline ring — solar on light / sunbeam on dark, full opacity */
    --btn-ring-press: var(--flare-paint-active);  /* …and the pressed fill while held */
    background-color: var(--flare-paint);
    border: 2px solid var(--flare-edge);
    padding: calc(0.85rem - 1px) calc(1.7em - 1px);
}
.btn--flare:hover  { background-color: var(--flare-paint-hover); }
.btn--flare:active { background-color: var(--flare-paint-active); }

/* --- Fill variants: hold the LABEL and the BOX through hover/focus/press ----- *
 * The three fills above set only their `background-color` per state, leaving the
 * midnight label and the `border: 1px solid transparent` to the structural base —
 * which is (0,1,0). That is enough for a pure design-system button, but NOT for one
 * that also wears a legacy class: grokkoli_base.css's `.btn-grokkoli-primary:hover`
 * and `:focus, :active` are (0,2,0), so on a button carrying BOTH (learner_profiles'
 * "Start Lesson" is `.btn .btn-grokkoli-primary .btn--accent`) they outrank the base
 * and take over two properties the variant never meant to hand over:
 *
 *   1. `color: var(--white)` — the label flipped to white on hover/press, against
 *      the fills' shared midnight at rest.
 *   2. `border: 1px var(--main-blue-hover)` — a shorthand with NO style keyword, so
 *      border-style resets to `none` and the 1px border computes to 0. The button
 *      lost 2px of height mid-hover, and inside the flip-card's
 *      `justify-content: space-around` column that re-flowed its SIBLINGS: the
 *      "correct answers" line above it crept down by a subpixel that rounds to 1px
 *      on screen. Hovering a button must not move anything but the button.
 *
 * Re-assert both at (0,2,0) so the variant keeps ownership of its own states. This
 * file loads after the legacy grokkoli_* stack (an invariant of the foundation head),
 * so the tie resolves here. FILL is untouched — each variant's own per-state
 * background above still wins, for the same reason. */
.btn--accent:hover, .btn--accent:focus, .btn--accent:active {
    color: var(--text-on-dark-fill);   /* white — see the note on .btn--accent above */
    border: 1px solid transparent;
}
.btn--accent-on-dark:hover, .btn--accent-on-dark:focus, .btn--accent-on-dark:active {
    color: var(--text-primary);
    border: 1px solid transparent;
}
/* Flare re-states its 2px edge, not the base's 1px: dropping to 1px here would shrink
 * the button by 2px mid-hover — the exact box collapse this block exists to prevent. */
.btn--flare:hover, .btn--flare:focus, .btn--flare:active {
    color: var(--text-primary);
    border: 2px solid var(--flare-edge);
}

/* --- Variants: SECONDARY (outline) — .btn--accent-secondary / .btn--flare-secondary *
 * The low-emphasis peer of each fill: white ground, 1px border and label in the
 * accent, same shape/size/weight as the fills. Ported from the style-guide
 * prototypes (style_guide.css:658 and :686), which is where the treatment was
 * designed.
 *
 * NOT `.btn-secondary` — Bootstrap already owns that class (a #6c757d grey fill) and
 * bootstrap.min.css is loaded on the alpha_base pages, so the two would fight.
 *
 * Written at the same specificity as the structural base
 * (`.btn`, 0-3-0) because that base sets
 * `border: 1px solid transparent` and the shared midnight `color`. A plain
 * single-class variant would win the background but lose both of those, which is the
 * whole visual. Later in the file, so equal specificity resolves this way.
 *
 * The flare pair reads --flare-paint, so it follows its surface the way .btn--flare
 * does: coral on light, peach on the dark bands. Accent is surface-stable, matching
 * .btn--accent. */
/* The two OUTLINE variants are the one exception to "press ring = pressed background":
 * their pressed background is the white surface, and a white ring on a white page is
 * no ring at all. They take their pressed BORDER/label colour instead — which is the
 * colour the button actually changes to on press, and the same tone its filled peer
 * would be showing. */
.btn--accent-secondary {
    --btn-ring: var(--focus-ring);
    --btn-ring-color: var(--accent-primary);
    --btn-ring-press: var(--accent-primary-active);
    background-color: var(--surface-elevated);
    border: 1px solid var(--accent-primary);
    color: var(--accent-primary);
}
.btn--accent-secondary:hover {
    border-color: var(--accent-primary-hover);
    color: var(--accent-primary-hover);
    background-color: var(--surface-elevated);
}
.btn--accent-secondary:active {
    border-color: var(--accent-primary-active);
    color: var(--accent-primary-active);
    background-color: var(--surface-elevated);
}

.btn--flare-secondary {
    --btn-ring: var(--flare-ring);
    --btn-ring-color: var(--flare-paint);
    --btn-ring-press: var(--flare-paint-active);
    background-color: var(--surface-elevated);
    border: 1px solid var(--flare-paint);
    color: var(--flare-paint);
}
.btn--flare-secondary:hover {
    border-color: var(--flare-paint-hover);
    color: var(--flare-paint-hover);
}
.btn--flare-secondary:active {
    border-color: var(--flare-paint-active);
    color: var(--flare-paint-active);
}

/* --- Modifier: block — the button spans its container --------------------- *
 * Opt-in, on any variant. Used on the entry forms (add-learner today; the
 * credential-entry forms as they migrate off .btn-grokkoli-primary), where the
 * submit button reads better spanning the field column.
 *
 * Width only: the structural base above sets display: inline-flex, and an
 * inline-flex box honours width: 100% while keeping the flex centring of the
 * label — so there is no display to override, and nothing else here sets width.
 *
 * Deliberately a MODIFIER rather than a container-scoped rule (e.g.
 * `.credential-entry-container .btn`): the two entry-form families share no
 * stylesheet except this one, and a container rule would silently catch every
 * button later dropped into those wrappers. */
.btn--block {
    width: 100%;
}

/* No underline on button CTAs. base.css underlines every <a>; when an <a> WRAPS a
 * .btn (<a class="main-cta"><div class="btn">…), that line is painted across the button
 * label — and a descendant's text-decoration:none CANNOT remove a line drawn by an
 * ancestor, so it must be killed on the <a> itself. Global — every .main-cta CTA, on any
 * page. (An <a class="btn"> needs no help: there the decoration originates on the link,
 * so the .btn base already clears it.) */
a.main-cta { text-decoration: none; }
a .btn { text-decoration: none; }  /* also clear it on a directly-inline .btn child */

/* .main-cta is an <a> that merely WRAPS a .btn (the hero + pricing "See Learning
 * Plans" CTAs). base.css gives every <a> a focus/press ring, so it appears *in
 * addition* to the button's own ring — a confusing double ring around the CTA.
 * Suppress the link's ring and instead surface the BUTTON's ring while the link is
 * focused/pressed. (It no longer has to neutralise pill padding/margins either — that
 * geometry now sits on heliomath_base's :not() selector, which excludes .main-cta.) The inner <div class="btn"> can't be focused itself, so
 * forwarding the ring here is what preserves a visible keyboard-focus indicator
 * (WCAG 2.4.7) — and it matches the persist-until-blur behaviour of a standalone
 * button. Falls back to the default ring for any wrapped button without --btn-ring. */
a.main-cta:focus,
a.main-cta:active {
    box-shadow: none;
    outline: none;
}
a.main-cta:focus .btn {
    outline: 2px solid var(--btn-ring-color, var(--accent-primary));
    outline-offset: 3px;
}
/* Measured: pressing the CTA normally DOES put the inner .btn in :active too — the
 * pointer is over the button, so it is the activated element and the <a> is merely an
 * ancestor — and its own press colours fire. This rule covers the case the button's
 * own can't: a press that lands on the wrapper OUTSIDE the button box. Reading
 * --btn-ring-press off the element (the variant declares it at rest, not inside
 * :active) is what lets the ring still match the pressed fill there. */
a.main-cta:active .btn {
    outline: 2px solid var(--btn-ring-press, var(--btn-ring-color, var(--accent-primary)));
    outline-offset: 3px;
}

/* ============================================================================
 * CARD  —  .card (+ modifiers)
 *
 * The generic surface tile for marketing content — testimonial quotes,
 * success-story items, the closing-CTA panel, and so on. An elevated white
 * surface sitting on the light/beige field, separated from it by a hairline
 * border AND a soft shadow: white-on-beige is only ~1.05:1, so a card ALWAYS
 * needs both to read as a distinct surface (see --border-hairline / the elevated
 * shadows in the tokens). Radius comes from --radius-card, so every card in the
 * system rounds the same amount.
 *
 * SHELL ONLY. A card owns its own surface, border, radius, shadow and inset
 * (padding) — and NOTHING about how its contents are arranged. The consumer lays
 * out the inside (flex/grid, gaps, alignment) via its own section-scoped rule,
 * which sits at higher specificity and rides on top of this shell.
 *
 * TUNABLE: --card-padding exposes the inset so a consumer can retune it WITHOUT
 * re-declaring the shell — set it on the element (e.g. the roomier CTA panel).
 *
 *   .card              the shell
 *   .card--interactive adds a hover lift (rise + deeper shadow) for a card that
 *                      is, or wraps, a tappable target. Reduced-motion users keep
 *                      the shadow cue but lose the transform.
 * ========================================================================== */
.card {
    --card-padding: var(--space-lg);
    background-color: var(--surface-elevated);
    border: 1px solid var(--border-warm);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-elevated);
    padding: var(--card-padding);
}

/* Hover lift — only for cards that invite interaction. */
.card--interactive {
    transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.card--interactive:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-elevated-hover);
}
@media (prefers-reduced-motion: reduce) {
    .card--interactive:hover { transform: none; }
}


/* ============================================================================
 * LOGO  —  .logo (+ --on-dark)
 *
 * The Heliomath wordmark — on nearly every screen. Two artwork files
 * (logo-midday.png / logo-midday-on-dark.png, both 500x84) swapped by CSS: the
 * default is the light-surface logo; add --on-dark on a DARK background. As a
 * background image, only the artwork for the MATCHED class is fetched — no double
 * download.
 *
 * Sized by HEIGHT via --logo-height; the width follows from the 500x84 ratio via
 * calc() (floor-safe — sidesteps aspect-ratio, which is above our browser floor).
 * PNG source is 500px wide, so keep the displayed width <=~250px to stay crisp on
 * 2x screens (the 2.5rem default is ~238px — fine).
 *
 * Give it an accessible name in markup (it's a background image, so no alt):
 *   as a link:  <a href="/" class="logo" aria-label="Heliomath"></a>
 *   standalone: <span class="logo" role="img" aria-label="Heliomath"></span>
 *   on dark:    add --on-dark, e.g. class="logo logo--on-dark"
 * ========================================================================== */
.logo {
    --logo-height: 2.25rem;                      /* 36px — override to resize; width follows */
    display: inline-block;
    height: var(--logo-height);
    width: calc(var(--logo-height) * 500 / 84);  /* preserve the 500x84 aspect ratio */
    background: url("../img/logo-midday.e76aee391730.png") center / contain no-repeat;
}
.logo--on-dark {
    background-image: url("../img/logo-midday-on-dark.6a2294ec5cef.png");
    /* Full opacity — no dimming. (Was 0.9, which made the LD hero wordmark read
     * darker/more muted than the same artwork on the account-access pages, where
     * it's a plain <img> at full strength.) */
}
/* Logo LINKS — every anchor that wraps the wordmark: the LD-hero wordmark (.logo as an <a>),
 * the top-nav wordmark (.navbar-brand, the <a> around the .navbar-logo img), and the bare hero
 * logo anchors on the plans + about pages (.logo-link, an <a> wrapping the wordmark <img>).
 * A logo isn't a text link — it carries no focus pill, so suppress the ring.
 *
 * This rule used to ZERO horizontal padding/margin too, to cancel the ±0.4em pill geometry
 * from heliomath_base's `a:focus`. That cure was worse than the disease: .navbar-brand has
 * a real 8px left margin of its own (grokkoli_base / heliomath_header), and zeroing it on
 * focus yanked the wordmark 8px LEFT the moment it was clicked. The pill geometry now lives
 * on heliomath_base's `:not(.logo):not(.navbar-brand):not(.logo-link)…` selector — the same
 * exclusion list that already skipped the fill — so it never reaches these anchors and there
 * is nothing here to cancel. Set no geometry: whatever layout each logo has at rest, it
 * keeps while focused. */
.logo:focus,
.logo:active,
.navbar-brand:focus,
.navbar-brand:active,
.logo-link:focus,
.logo-link:active {
    outline: none;
    box-shadow: none;
}

/* Chrome links are NOT body links — no underline. heliomath_base's `a` rule underlines
 * every anchor (the design-system treatment for body copy), which is right for prose and
 * wrong for the top nav, the dropdown menu and the wordmark. Same element list
 * heliomath_base already excludes from the a:focus link-fill, kept in step with it.
 * Two classes vs the bare `a`, so it wins comfortably; hover/active set no decoration of
 * their own, so it stays off in every state. */
.navbar-nav .nav-link,
.navbar-nav .dropdown-item,
.navbar-brand,
.logo,
.logo-link {
    text-decoration: none;
}

/* SOLID nav bars (the white bar on the app/blog/report pages) — one link blue.
 * Left alone, these inherit three different colours depending on which legacy sheet a
 * page happens to load: grokkoli_base's #0020b0 (product updates, work-with-us),
 * Bootstrap's rgba(0,0,0,.5) (blog, pilot reports), or nothing at all. (0,3,0) so it
 * beats Bootstrap's own `.navbar-light .navbar-nav .nav-link`, and this file loads
 * after it.
 *
 * ONLY the solid bar: `:not(.transparent)` leaves both overlay navs to their own
 * treatment — cream on the dark heroes (heliomath_nav.css) and --link on the light
 * ones (heliomath_marketing.css). True-blue over a dark hero would be 2.27:1. */
.header:not(.transparent) .navbar-nav .nav-link {
    color: var(--link);
}
.header:not(.transparent) .navbar-nav .nav-link:hover {
    color: var(--link-hover);
}

/* Bootstrap hardcodes `.dropdown-item { font-weight: 400 }`; re-point the account menu
 * onto the ladder. Scoped to .navbar-nav so it can't reach a non-nav dropdown later. */
.navbar-nav .dropdown-item {
    font-weight: var(--font-weight-regular);
}

/* …and the same one link blue as the nav bar above. The account menu's rows were
 * grokkoli_base's legacy #0020b0 (`.navbar .dropdown-menu a`, 0-2-1), which reaches every
 * Heliomath page that still loads that sheet — so the fix has to live HERE, in the file
 * every Heliomath page loads, not in heliomath_header.css (which the app + product-update
 * pages never load). Both selectors are listed because the menu's rows are <a> elements
 * that may or may not carry .dropdown-item. */
.navbar .dropdown-menu a,
.navbar-nav .dropdown-item {
    color: var(--link);
}
.navbar .dropdown-menu a:hover,
.navbar-nav .dropdown-item:hover {
    color: var(--link-hover);
}


/* ============================================================================
 * HEADER / NAV LAYOUT
 *
 * Small overrides for the shared Bootstrap header on Heliomath pages — scoped by
 * controlled loading (this file loads only on Heliomath pages, never Grokkoli).
 * Sit the logo (in .navbar-brand) and the nav items (login, etc.) on one vertical
 * centre line. .navbar-brand is made a centred flex box so the inline-block logo
 * doesn't ride the text baseline.
 * ========================================================================== */
.navbar .navbar-collapse {
    align-items: center;
}
.navbar .navbar-brand {
    display: inline-flex;
    align-items: center;
    margin-top: 0;
    margin-bottom: 0;
}

/* The transparent-header chrome for the dark-header pages (logo insets + size,
 * vertical centring, pill gap) lives in heliomath_nav.css now — one definition
 * shared by the learning-differences page AND the account-access flow, so they
 * line up pixel-for-pixel. The generic brand-centring above still applies to every
 * Heliomath page (incl. home). */


/* ============================================================================
 * LAYOUT COMPAT — the Bootstrap-4 STRUCTURAL rules the shared nav markup relies
 * on, ported verbatim from base.css so Heliomath pages need not load it. The nav
 * markup (external/legacy/home_base) is shared with Grokkoli, so its Bootstrap
 * classes must stay in the HTML; here we supply only their box/flex STRUCTURE —
 * the skin is in the sections below + heliomath_nav / heliomath_marketing.
 * base.css's grid (.container/.row/.col-*), heading-size classes and :root
 * Bootstrap vars are UNUSED on Heliomath and were dropped, not ported. Retire this
 * whole block once the nav markup stops using the Bootstrap navbar classes.
 * ========================================================================== */

/* Flex nav bar + fixed positioning (base.css .navbar + .fixed-top). The transparent
 * hero nav is re-pointed to position:absolute by heliomath_marketing.css; the solid
 * nav keeps fixed. Per-page padding/skin overrides this in the sections below. */
.navbar {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    padding: 0.5rem 1rem;
}

/* --- Wrap behaviour: nav items stay on top, logo drops below ------------ *
 * On the solid content-page nav (terms / success-stories / legal) and the
 * auth-page nav (login / signup / reset), when the bar runs out of room the nav
 * items should hold the top line and the LOGO should be what's forced to the
 * second line — the same rhythm the hero pages have. `wrap-reverse` places the
 * wrapped line ABOVE the first, so it flips the stack order WITHOUT disturbing
 * the single-line desktop layout (logo left, buttons right) and needs no width
 * breakpoint — it triggers exactly at the natural wrap point. The transparent
 * HERO navs (about / plans / LD) are excluded: they hide their nav logo, so
 * there's nothing to reflow there.
 *
 * Two DOM shapes are covered: legacy/alpha bases make the logo + nav separate
 * children of .navbar (so .navbar is what wraps); external_base nests both inside
 * .navbar-collapse (so the collapse wraps). Setting wrap-reverse on both is safe
 * — whichever container isn't the wrapping one holds a single child, so it's a
 * no-op there. */
.header:not(.transparent).navbar,
.auth-page .header.navbar,
.header:not(.transparent) .navbar-collapse,
.auth-page .header .navbar-collapse {
    flex-wrap: wrap-reverse;
    /* Breathing room BETWEEN the wrapped rows only. row-gap applies to the space
     * between flex lines, so on the single-line desktop bar (one row) it has no
     * effect — the desktop spacing is unchanged. Only appears once the bar wraps. */
    row-gap: 22px;   /* space between the wrapped rows; literal (no token lands at 22px) */
}

/* Guarantee a gap between the logo and the leftmost nav item so they can't touch
 * when the single-line bar gets tight (e.g. ~344px, before it wraps): space-between
 * has run out of slack and the auth pages zero the brand's margins. This margin sits
 * to the LEFT of the first pill; on desktop the ml-auto push swallows it (plenty of
 * slack there), so the wide-bar spacing is unchanged. Solid + auth navs only — the
 * hero navs hide their logo, so there's nothing for the pill to touch. */
.header:not(.transparent) .navbar-nav .nav-item:first-child,
.auth-page .header .navbar-nav .nav-item:first-child {
    margin-left: var(--space-sm);
}
.fixed-top {
    position: fixed;
    top: 0;
    right: 0;
    left: 0;
    z-index: 1030;
}
.navbar-brand {
    display: inline-block;
    padding-top: 0.3125rem;
    padding-bottom: 0.3125rem;
    margin-right: 1rem;
    font-size: 1.25rem;
    line-height: inherit;
    white-space: nowrap;
}
.navbar-nav {
    display: flex;
    flex-direction: column;
    padding-left: 0;
    margin-bottom: 0;
    list-style: none;
}
.nav-link {
    display: block;
    padding: 0.5rem 1rem;
}
.navbar-nav .nav-link {
    padding-right: 0;
    padding-left: 0;
}
.navbar-collapse {
    flex-basis: 100%;
    flex-grow: 1;
    align-items: center;
}
.navbar-toggler {
    padding: 0.25rem 0.75rem;
    font-size: 1.25rem;
    line-height: 1;
    background-color: transparent;
    border: 1px solid transparent;
    border-radius: 0.25rem;
}
/* .navbar-expand (unconditional, present in the markup alongside -lg) keeps the bar
 * horizontal at every width and hides the toggler — the Heliomath nav never collapses
 * to a hamburger, so base.css's .collapse / .navbar-expand-lg / hamburger-icon rules
 * never take effect and were not ported. */
.navbar-expand { flex-flow: row nowrap; justify-content: flex-start; }
.navbar-expand .navbar-nav { flex-direction: row; }
.navbar-expand .navbar-nav .nav-link { padding-right: 0.5rem; padding-left: 0.5rem; }
.navbar-expand .navbar-collapse { display: flex !important; flex-basis: auto; }
.navbar-expand .navbar-toggler { display: none; }

/* ...BUT the header nav must still be allowed to WRAP. .navbar-expand forces row
 * NOWRAP (flex-flow), which on narrow phones makes the hero navs overflow the
 * viewport — worst on the LD page, which adds a "Learning Plans" pill beside Login:
 * the links can't wrap, so they clip and push Login off-screen, forcing ~100px of
 * horizontal scroll. Re-enable wrap so the links drop below the logo instead.
 * (These out-specify .navbar-expand; desktop is unaffected — wrap only triggers when
 * the row doesn't fit.)
 *
 * BOTH selectors are needed because the two base templates nest the logo
 * differently: legacy_base puts .navbar-brand as a DIRECT child of .navbar (wrapped
 * by the first selector), while external_base nests brand + links INSIDE
 * .navbar-collapse (wrapped by the second). Without the collapse rule the LD/hero
 * nav on external_base can't wrap — .navbar has a single child — and overflows. */
.header.navbar,
.header .navbar-collapse { flex-wrap: wrap; }

/* Utilities still used by the shared markup (nav logo + #main). */
.align-top { vertical-align: top !important; }
.d-inline-block { display: inline-block !important; }
.ml-auto { margin-left: auto !important; }
.clearfix::after { display: block; clear: both; content: ""; }


/* ============================================================================
 * STANDARD (solid) NAV — REPLACED by heliomath_header.css.
 *
 * This file used to paint the solid alpha_base header on the Sunset tokens (Poppins,
 * a 36px height-sized wordmark, navy menu rows). That made the ported pages diverge
 * from the QUESTION pages, whose header comes from grokkoli_base and is the tuned
 * reference. heliomath_header.css now re-states the grokkoli_base nav for the pages
 * that dropped it, and _solid_nav_head.html links it right after this file.
 *
 * Nothing solid-nav belongs here any more. The transparent hero navs are unaffected —
 * they were never painted by this block (every rule was :not(.transparent)).
 * ========================================================================== */


/* ============================================================================
 * FOOTER  —  #footer (the shared public footer)
 *
 * Codifies templates/external_footer.html onto the tokens: a full-width band on
 * the sunken surface with columns of nav links + a copyright line, content
 * centred to the page width. Targets the existing #footer / .footer-content /
 * .links / .link-group markup and overrides the legacy grokkoli_base footer.
 * (The broccoli mascot is Grokkoli-only and hidden for other brands.)
 * ========================================================================== */
#footer {
    background-color: var(--surface-sunken);
    border-top: 1px solid var(--border-on-sunken);
    padding: var(--space-lg) var(--space-gutter);
    font-weight: var(--font-weight-light);   /* 300 */
}

/* Content centred to the page width (= the standard --mp-content-max; keep in sync). */
#footer .footer-content {
    width: 100%;
    max-width: 68rem;
    margin-left: auto;
    margin-right: auto;
}

/* Link columns — flex-wrap so they stack as the viewport narrows. */
#footer .links {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: var(--space-md) var(--space-md);   /* row-gap  column-gap */
    margin-bottom: var(--space-lg);
}
#footer .link-group {
    display: flex;
    flex-direction: column;
    /* Size each link to its own text, not the column's widest item. A flex column
     * defaults to align-items:stretch, which stretched every <a> to the widest link's
     * width — so a short link's box (and its pressed pill fill) spanned the whole
     * column. flex-start lets each link be content-width and left-aligned. */
    align-items: flex-start;
    gap: var(--space-xs);
    margin: 0;
}

/* Footer links: understated dark ink, underline on hover. */
#footer a {
    color: var(--text-lively);
    font-size: var(--type-caption-size);   /* 14–16px — footer links needn't be large */
    line-height: 1.4;
    text-decoration: none;
    /* Pill breathing room around the text — present in ALL states, with an equal negative
     * margin cancelling it so the box the flex column lays out (the MARGIN box) is
     * unchanged: text stays put, links keep their spacing, columns keep their width. And
     * because these hold at rest, they hold on press too — `#footer a` (an id) out-
     * specifies base.css's `a:focus`, so its focus padding/negative-margin can't touch
     * these and can't shift the link. Result: a proper padded pill, zero movement.
     * (base.css's own cancel works for inline links but misbehaves on these flex items.) */
    padding: 3px 10px;
    margin: -3px -10px;   /* cancels the padding for layout; also clears legacy link margins */
    max-width: none;    /* clear the legacy link max-width */
    min-width: 0;       /* clear the legacy link min-width */
}
#footer a:hover  { color: var(--link-hover);  text-decoration: underline; }
#footer a:active { color: var(--link-active); }
/* On PRESS the pill fill is the affordance, so drop the underline while focused. Needed
 * because a pointer press is ALSO a hover, and #footer a:hover's underline (1,1,1)
 * out-specifies base.css's a:focus text-decoration:none (0,1,1) — this restates it at
 * matching specificity, later in source order, so it wins during a press but leaves the
 * plain hover underline intact. */
#footer a:focus { text-decoration: none; }
/* Focus ring is NOT defined here — base.css owns link focus site-wide. */

/* Copyright line. */
#footer p {
    color: var(--text-lively);
    font-size: var(--type-caption-size);
    margin: 0;
}


/* ============================================================================
 * LEGACY SECTION SYSTEM — the full-bleed band + centred module pair, ported from
 * grokkoli_public.css (which Heliomath never loads: the brand gate in alpha_base.html
 * and external_base.html skips it, and no heliomath_public.css was ever written).
 *
 * Unscoped on purpose — these classes are shared markup across many pages, so scoping
 * them per page would mean re-solving them each time. Page files load AFTER this one,
 * so any page that re-themes its own band still wins.
 * ========================================================================== */
.standard-brochure-module-container {
    padding: calc(3rem + 2vw) calc(7vw + 10px);
}
.standard-brochure-module {
    max-width: 930px;
    margin-left: auto;
    margin-right: auto;
}

/* .dark-blue-background already appears in the dark-context block near the top of this
 * file, which re-points the weight ladder ON THE ASSUMPTION the band is dark. Without
 * grokkoli_public.css nothing was painting it, so that assumption was false and the
 * band rendered as thinned dark text on beige. This is the paint that makes it true. */
.dark-blue-background {
    background-color: var(--surface-dark);
    color: var(--text-on-dark);
}
.dark-blue-background h2,
.dark-blue-background h3,
.dark-blue-background p,
.dark-blue-background li,
.dark-blue-background th,
.dark-blue-background td p {
    color: var(--text-on-dark);
}
.dark-blue-background a        { color: var(--link-on-dark); }
.dark-blue-background a:hover  { color: var(--link-on-dark-hover); }

/* Section titles marked with the legacy .accent-color-light-winter-green class. The class
 * is in SHARED markup (beta_limitations.html, the two comparison partials) so it can't be
 * removed, and grokkoli_base.css:365 sets it `!important` — hence the !important here,
 * which is the only way to out-rank it. Mint #5ddaa5 has no place in Sunset; this is the
 * same paint heliomath_home.css:455 already gives the home comparison title, via the
 * --link-on-dark alias. Scoped to h2 inside a dark band: the class also marks ✓ marks and
 * table cells, which are a separate decision. */
.dark-blue-background h2.accent-color-light-winter-green {
    color: var(--accent-primary-on-dark) !important;
}
