/* ============================================================================
 * 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-fill); color: var(--text-on-dark-fill); }
.fill-flare  { background-color: var(--flare-paint);    color: var(--flare-label); border: 2px solid var(--flare-edge); }

/* Screen-reader-only text: in the accessibility tree, off the visual layout (e.g. the
 * comparison table's row-label column header). The clip/1px pattern rather than
 * display:none, which would hide it from screen readers too. Floor-safe. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}


/* ============================================================================
 * 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. */
/* Rest is the true-blue PRIMITIVE, not --accent-primary: that role moved to dusk (2026-10-12),
 * which turned this outline purple at rest and blue on hover. Hover/press stay on the role
 * tokens, which are still the deeper true-blues — so the button keeps one hue throughout. */
.btn--accent-secondary {
    --btn-ring: var(--focus-ring);
    --btn-ring-color: var(--true-blue);
    --btn-ring-press: var(--accent-primary-active);
    background-color: var(--surface-elevated);
    border: 1px solid var(--true-blue);
    color: var(--true-blue);
}
.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(--btn-accent-fill));   /* fallback: the blue button fill (--accent-primary is dusk) */
    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(--btn-accent-fill)));
    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 compact Heliomath wordmark — on nearly every screen. On a dark surface,
 * add --on-dark for the brighter blue artwork.
 *
 * Sized by HEIGHT via --logo-height; the width follows from the 376x104 ratio.
 *
 * 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: 3.25rem;
    display: inline-block;
    height: var(--logo-height);
    width: calc(var(--logo-height) * 376 / 104);
    background: url("../img/brands/heliomath/logo-small.ed79375f763d.png") center / contain no-repeat;
}
.logo--on-dark {
    background-image: url("../img/brands/heliomath/logo-small-dark-mode.9cb4d359953d.png");
}
/* Hero A/B "sky" visitor (body.hero-sky — site_pages/hero_variant.py): Eric's wordmarks.
 * The light-surface logo is the 500x84 midday artwork. On dark it's the white wordmark
 * (810x132, letters only — no glow), which is NOT 500x84: its letters span the full width,
 * so the WIDTH stays the 500x84 formula (the letters come out as wide as the midday
 * wordmark's) and the HEIGHT is the artwork's at that width. The page using it sets the
 * block margins (see heliomath_learning_differences.css). */
body.hero-sky .logo {
    --logo-height: 2.25rem;
    width: calc(var(--logo-height) * 500 / 84);
    background-image: url("../img/logo-midday.e76aee391730.png");
}
body.hero-sky .logo--on-dark {
    height: calc(var(--logo-height) * 500 / 84 * 132 / 810);
    background-image: url("../img/brands/heliomath/heliomath-logo-med-no-solarbling.de941836257c.png");
    background-size: 100% auto;
}
/* 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.
 *
 * The hero A/B's "sky" visitor (body.hero-sky — site_pages/hero_variant.py) gets a
 * different bar, below. */
.header:not(.transparent) .navbar-nav .nav-link {
    color: var(--link);
}
.header:not(.transparent) .navbar-nav .nav-link:hover {
    color: var(--link-hover);
}

/* --- Hero A/B "sky" visitor: the sky-strip solid bar ------------------------------ */
/* SOLID nav bars (the fixed bar on the app/question/blog/report pages) — a NIGHT bar,
 * because the wordmark only works on dark (branding.py's nav_logo). The white ground
 * came from grokkoli_base's `.navbar-light` (0,1,0) and heliomath_header.css's restatement
 * of it; this is now the single place the bar is painted. NO bottom border: grokkoli_base's
 * 1px light-grey hairline read as a white line under the night bar, and merely making it
 * transparent still left a visible seam (the gradient repeats into the border strip).
 *
 * Links are the on-dark text colour, one colour for all of them. Left alone, they
 * inherit three different colours depending on which legacy sheet a page happens to
 * load: grokkoli_base's #0020b0 (question pages, product updates), Bootstrap's
 * rgba(0,0,0,.5) (blog, pilot reports), or nothing at all. (0,4,0) beats Bootstrap's
 * `.navbar-light .navbar-nav .nav-link` and grokkoli_base's `.navbar .navbar-nav
 * .nav-link`; :active is restated because grokkoli_base's press colour (#0020b0, 0,4,0)
 * would otherwise flash near-invisible navy on the night bar.
 *
 * ONLY the solid bar: `:not(.transparent)` leaves both overlay navs to their own
 * treatment — pills on the dark heroes (heliomath_nav.css) and --link on the light
 * ones (heliomath_marketing.css). The account DROPDOWN is its own white panel and keeps
 * --link (below). */
body.hero-sky .header:not(.transparent) {
    background: var(--top-nav-gradient);   /* the sky strip — see the token */
    border-bottom: none;
    /* No vertical padding: the logo spans the bar's full height. (The ported Bootstrap
     * `.navbar` padding below would otherwise add 8px above and below on the app pages.) */
    padding-top: 0;
    padding-bottom: 0;
}
body.hero-sky .header:not(.transparent) .navbar-nav .nav-link {
    color: var(--text-on-dark);
}
body.hero-sky .header:not(.transparent) .navbar-nav .nav-link:hover,
body.hero-sky .header:not(.transparent) .navbar-nav .nav-link:active {
    color: var(--white);
}
/* The account-access pages (login, signup, 2FA, reset, the post-signup funnel) use this
 * same regular nav with NO background: the base templates add .header--clear there, and
 * the dark field shows through. Everything else — logo size and position, link type and
 * colour — is the regular nav's. The hairline stays, invisible, so the bar's height matches.
 * The weight ladder is re-pointed back to the on-light rungs: the regular nav sits outside
 * any dark scope (its links inherit 300), but these pages' body.auth-page is one, which
 * would thin the links to 200. */
.header.header--clear {
    background-color: transparent;
    background-image: none;   /* the solid bar's --top-nav-gradient is an IMAGE, which background-color alone leaves painted */
    border-bottom-color: transparent;
    --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);
}
/* heliomath_base's `a:focus` ring is the light-surface one (true-blue @ 30%), which reads
 * as a dark box on night — e.g. around the account toggle while its menu is open.
 * The pill radius rounds the ring (a box-shadow follows border-radius), so the press /
 * focus ring on the account toggle and the quiet "Account Settings" link has the same
 * round ends as the other nav rings. Here, not in heliomath_app.css, because pages like
 * About render this nav without loading heliomath_app.css. */
body.hero-sky .header:not(.transparent) .navbar-nav .nav-link:focus {
    box-shadow: var(--focus-ring-link-on-dark);
    border-radius: var(--radius-pill);
}

/* The logged-OUT solid bar's two links (ToS, Privacy Policy, blog …) — and the logged-IN
 * account menu toggle (the family name + chevron), by class — as the SAME translucent
 * pill as the hero / account-access navs (heliomath_nav.css — keep the two in step). The two
 * links are scoped by id (they only exist in the anonymous nav), so any OTHER logged-in nav
 * link keeps its plain style; the account toggle is opted in by class. The class + the
 * :not() also out-rank Bootstrap's open-menu colour (`.navbar-light .navbar-nav .show >
 * .nav-link`, 0,4,0). Here, not in heliomath_nav.css, because the solid-bar pages don't
 * load that file. (2026-10-12) */
body.hero-sky .header:not(.transparent) .navbar-nav #header_learning_plans,
body.hero-sky .header:not(.transparent) .navbar-nav #header_login,
body.hero-sky .header:not(.transparent) .navbar-nav .nav-link.dropdown-toggle {
    color: var(--text-on-dark);
    background-color: rgba(255, 255, 255, 0.1);
    padding: 7px 20px;
    border-radius: var(--radius-pill);
    font-family: var(--font-family-base);
    font-size: 1.0625rem;   /* 17px */
    font-weight: var(--font-weight-regular);
    line-height: 1.5;
    letter-spacing: normal;
    text-decoration: none;
}
body.hero-sky .header:not(.transparent) .navbar-nav #header_learning_plans:hover,
body.hero-sky .header:not(.transparent) .navbar-nav #header_login:hover,
body.hero-sky .header:not(.transparent) .navbar-nav .nav-link.dropdown-toggle:hover {
    color: var(--white);
    background-color: rgba(255, 255, 255, 0.15);
}
body.hero-sky .header:not(.transparent) .navbar-nav #header_learning_plans:focus,
body.hero-sky .header:not(.transparent) .navbar-nav #header_learning_plans:active,
body.hero-sky .header:not(.transparent) .navbar-nav #header_login:focus,
body.hero-sky .header:not(.transparent) .navbar-nav #header_login:active,
body.hero-sky .header:not(.transparent) .navbar-nav .nav-link.dropdown-toggle:focus,
body.hero-sky .header:not(.transparent) .navbar-nav .nav-link.dropdown-toggle:active {
    outline: 2px solid var(--text-on-dark);
    outline-offset: 3px;
    box-shadow: none;
    padding-left: 20px;
    padding-right: 20px;
    margin-left: 0;
    margin-right: 0;
}

/* 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), 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.
 * (The auth-page nav gets the same treatment below.) The hero A/B's "sky" visitor gets
 * the bar's own rhythm instead (the body.hero-sky rules below).
 *
 * 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,
.header:not(.transparent) .navbar-collapse {
    flex-wrap: wrap-reverse;
    row-gap: 22px;   /* space between the wrapped rows; literal (no token lands at 22px) */
}

/* --- SOLID NAV, hero A/B "sky" visitor: wrap behaviour + row rhythm -------- *
 * The solid (non-hero) bar — blog, legal, scholarship, success stories, the app pages.
 * Not the clear (.header--clear) bars of the auth / signed-in hero pages: own layouts.
 * When it runs out of room the LINKS drop below the logo (plain `wrap`), and the
 * links' row is held to the same height as the logo's row (--nav-row-height, set on
 * .header below), with the pills centred in it — so the space above and below the
 * links is the same whether they sit beside the logo or under it. On one line it's a
 * no-op: the row is already exactly that tall.
 *
 * Two DOM shapes are covered: alpha/legacy 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). Whichever container isn't the wrapping one
 * holds a single child, so the rule is a no-op there. */
body.hero-sky .header:not(.transparent):not(.header--clear).navbar,
body.hero-sky .header:not(.transparent):not(.header--clear) .navbar-collapse {
    flex-wrap: wrap;
    row-gap: 0;
}
body.hero-sky .header:not(.transparent):not(.header--clear) .navbar-nav {
    min-height: var(--nav-row-height);
    align-items: center;
}
/* heliomath_header.css's <=475px left padding on every page, including the ones that
 * don't load it (product updates, work with us), so the logo sits in the same place. */
@media screen and (max-width: 475px) {
    body.hero-sky .header:not(.transparent):not(.header--clear).navbar {
        padding-left: 5px;
    }
}

/* --- SOLID NAV, "sky" visitor: the bar's size, and the content's clearance ---- *
 * (The variables are global, but only the body.hero-sky rules use them for layout.)
 * On :root so #main — a SIBLING of the header, not a child — can read them.
 *   --nav-logo-width    the wordmark's width (165px; fluid below 750px)
 *   --nav-row-height    one bar row: the logo's full box (its 230/474 footprint)
 *   --nav-height        the whole bar: one row, or two below 360px, where the links always
 *                       take their own row (forced below, so the height is known, not
 *                       guessed from wherever the links happen to wrap). From 360-460px
 *                       the pills shrink so logo + Learning Plans + Login share one row,
 *                       like the standard bar.
 *   --nav-content-gap   white space from the bar's bottom edge to the page's first content:
 *                       40px on phones -> 85px on a full desktop (calc + vw, capped by the
 *                       two media queries; clamp() is above the browser floor) */
:root {
    --nav-logo-width: 165px;
    --nav-row-height: calc(var(--nav-logo-width) * 230 / 474);
    --nav-height: var(--nav-row-height);
    --nav-content-gap: calc(1.455rem + 4.286vw);   /* 40px at 390px wide, 85px at 1440px (~75px at 1200px) */
}
@media screen and (max-width: 750px) {
    :root { --nav-logo-width: calc(83px + 10.9vw); }   /* grokkoli_base's small-screen logo step */
}
@media screen and (max-width: 460px) {
    /* Compact pills, so the links stay beside the logo (padding repeated on :focus/:active,
     * whose rule above re-sets it). */
    body.hero-sky .header:not(.transparent):not(.header--clear) .navbar-nav {
        gap: 8px;
        margin-left: auto;
    }
    /* Only the gap spaces the pills (grokkoli_base adds side margins to each item);
     * the first keeps its space from the logo. */
    body.hero-sky .header:not(.transparent):not(.header--clear) .navbar-nav .nav-item {
        margin: 0;
    }
    body.hero-sky .header:not(.transparent):not(.header--clear) .navbar-nav .nav-item:first-child {
        margin-left: 8px;
    }
    /* ...which is all the logo needs: drop the brand's own right margin (16px from
     * grokkoli_base on pages without heliomath_header.css, e.g. product updates). */
    body.hero-sky .header:not(.transparent):not(.header--clear) .navbar-brand {
        margin-right: 0;
    }
    /* Trimmed right padding, so 360px fits with a little room to spare. */
    body.hero-sky .header:not(.transparent):not(.header--clear).navbar {
        padding-right: 10px;
    }
    /* The links take the room beside the logo instead of wrapping by their max-content
     * size (on product updates / work with us that sizes past the free room, and the
     * links dropped to a second row with space to spare). Below 360px the rule further
     * down puts them on their own row. */
    body.hero-sky .header:not(.transparent):not(.header--clear) .navbar-collapse {
        flex: 1 1 0;
        min-width: 0;
    }
    body.hero-sky .header:not(.transparent):not(.header--clear) .navbar-nav #header_learning_plans,
    body.hero-sky .header:not(.transparent):not(.header--clear) .navbar-nav #header_login,
    body.hero-sky .header:not(.transparent):not(.header--clear) .navbar-nav .nav-link.dropdown-toggle {
        font-size: 0.875rem;   /* 14px */
        padding: 5px 10px;
    }
    body.hero-sky .header:not(.transparent):not(.header--clear) .navbar-nav .nav-link:focus,
    body.hero-sky .header:not(.transparent):not(.header--clear) .navbar-nav .nav-link:active {
        padding-left: 10px;
        padding-right: 10px;
    }
}
@media screen and (max-width: 359px) {
    :root { --nav-height: calc(2 * var(--nav-row-height)); }
    /* Links on their own row, right-aligned (both DOM shapes — see the wrap note above). */
    body.hero-sky .header:not(.transparent):not(.header--clear) .navbar-collapse,
    body.hero-sky .header:not(.transparent):not(.header--clear) .navbar-nav {
        flex-basis: 100%;
        justify-content: flex-end;
    }
}
@media screen and (max-width: 390px) { :root { --nav-content-gap: 40px; } }
@media screen and (min-width: 1440px) { :root { --nav-content-gap: 85px; } }

/* The bar is exactly its rows (--nav-height): no legacy min-height. grokkoli_base's
 * `.header { min-height: calc(4vh + 2.5rem) }` scales with the viewport HEIGHT, which made
 * the one-row bar taller than its row on short-and-narrow screens (and unknowable from
 * the width). The question pages keep it — their lesson bar is positioned from it. */
body.hero-sky:not(.question_page) .header:not(.transparent):not(.header--clear) {
    min-height: 0;
}

/* For the "sky" visitor, every solid-nav content page clears the bar here and nowhere else: the bar's height plus
 * the gap. The legacy per-page workarounds tuned for an 80px bar (.top-bar-spacer blocks,
 * grokkoli_base's #main padding, page/section top paddings) still serve everyone else, and
 * are switched off for this visitor (body.hero-sky rules beside each). Not the question pages (own layout around
 * the lesson bar), the auth pages, or the dark-hero pages (their hero sits under the nav). */
body.hero-sky:not(.question_page):not(.auth-page):not(.dark-hero-page) .header:not(.transparent):not(.header--clear) ~ #main {
    padding-top: calc(var(--nav-height) + var(--nav-content-gap));
}
body.hero-sky:not(.question_page):not(.auth-page):not(.dark-hero-page) .header:not(.transparent):not(.header--clear) ~ #main .top-bar-spacer {
    display: none;
}

/* --- AUTH-PAGE NAV (login / signup / reset): nav items stay on top, logo drops below.
 * `wrap-reverse` places the wrapped line ABOVE the first, so it flips the stack order
 * WITHOUT disturbing the single-line layout and needs no width breakpoint. */
.auth-page .header.navbar,
.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; }

/* NAV WORDMARK — the <img class="navbar-logo"> in every base template's top nav, for the
 * hero A/B's "sky" visitor (body.hero-sky): the white wordmark (810x132, letters only).
 * Everyone else gets the compact artwork, sized in heliomath_header.css / heliomath_nav.css
 * (0,3,0 at most, so `.header` is in the selector here to out-rank them).
 *
 * Sized by WIDTH (--nav-logo-width, on :root under SOLID NAV above: 165px, grokkoli_base's
 * tuned box, with its <750px step); heliomath_nav.css widens it to 214px on the dark-header
 * pages. The letters run the artwork's full width, so the name reads the same size.
 * The height follows the 132/810 ratio. Replaces grokkoli_base's 165x28 pair, which would
 * squash this artwork (it's pinned for a 500x84 image).
 *
 * The block margins keep the footprint of the earlier glow artwork (474x230, letters
 * vertically centred): 0.1611 = (230 / 474 - 132 / 810) / 2. So the bar height and the
 * letters' position are unchanged — the solid bar stays as tall as the logo box was, with
 * no margin or padding on the brand link (see the .navbar-brand rule below). */
body.hero-sky .header .navbar-logo {
    width: var(--nav-logo-width);
    height: calc(var(--nav-logo-width) * 132 / 810);
    margin-top: calc(var(--nav-logo-width) * 0.1611);
    margin-bottom: calc(var(--nav-logo-width) * 0.1611);
}
/* The brand link carries grokkoli_base's / Bootstrap's vertical margin + padding on the
 * question and app pages; zero them so the logo meets the top and bottom of the solid bar.
 * heliomath_header.css zeroes the same on the solid-nav content pages. Hero A/B "sky"
 * visitor only. */
body.hero-sky .header:not(.transparent) .navbar-brand {
    margin-top: 0;
    margin-bottom: 0;
    padding-top: 0;
    padding-bottom: 0;
}

/* 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);
    /* A LIGHT band, even on the dark account-access pages, whose body.auth-page puts the
     * whole page on the on-dark weight ladder — which thinned these links + the copyright
     * line to 100. Re-point the ladder back to the on-light rungs for the footer's subtree. */
    --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-light);   /* 200 — inherited by the links and the copyright line */
}

/* 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;
}
