/* ============================================================
   public/css/style.css
   Phase 5 — full design system for Rock County Fitness Hub.

   Look & feel:
     - Deep charcoal / dark-navy base
     - Lighter elevated surfaces for cards / nav
     - Warm-orange accents (used sparingly so it stays premium)
     - Off-white primary text, muted gray secondary
     - Rounded corners, soft shadows, subtle borders
     - Chart-ready accent palette for Phase 7 dashboards

   Sections:
     1.  Reset
     2.  Design tokens (CSS variables)
     3.  Base typography + links
     4.  Layout primitives (.container, .site-main)
     5.  Sticky site header + nav (+ mobile hamburger)
     6.  Site footer
     7.  Buttons (.btn / variants)
     8.  Forms + auth card (Phase 4 carryover, retinted)
     9.  Flash messages + error box
     10. Stub / placeholder card (Phase 4 carryover)
     11. Landing page sections (hero, BMI, features, quote)
     12. Responsive tweaks
   ============================================================ */


/* ---------- 1. Reset ---------- */
*,
*::before,
*::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

/* The HTML `hidden` attribute should always hide an element. Some
   component classes set their own `display:` (e.g. `.btn` →
   inline-flex, `.form-grid` → grid) which would otherwise win against
   the user-agent default `[hidden] { display: none }` because they
   share specificity but our rules come later in the cascade.
   `!important` is idiomatic for `[hidden]` and avoids per-class fixes. */
[hidden] { display: none !important; }

/* Honor the user's reduced-motion preference: kill transitions and
   animations across the board so the new button lifts, card hovers,
   and slide-down menu don't trigger vestibular discomfort. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}


/* ---------- 2. Design tokens ---------- */
:root {
    /* Backgrounds — dark, layered, never pure black */
    --bg:           #0f1218;   /* page background */
    --bg-elev:      #161a24;   /* sticky nav, footer */
    --bg-card:      #1b2030;   /* cards, panels */
    --bg-card-2:    #20263a;   /* hover / inner panel */
    --bg-input:     #11151e;   /* form inputs */

    /* Borders */
    --border:       rgba(255, 255, 255, 0.08);
    --border-soft:  rgba(255, 255, 255, 0.04);
    --border-hot:   rgba(255, 138, 56, 0.45);

    /* Text — every token clears WCAG AA (4.5:1) on the page bg
       and on cards. text-faint was bumped from #6f7280 (3.99:1,
       just under AA) to #8c91a0 (5.86:1) so hints and placeholders
       stay legible for low-vision users. */
    --text:         #ecedf2;   /* primary off-white */
    --text-dim:     #a4a8b5;   /* secondary gray */
    --text-faint:   #8c91a0;   /* tertiary / hints */

    /* Brand — warm orange */
    --primary:      #ff7a1a;   /* base brand orange */
    --primary-hot:  #ff9447;   /* hover / lighter */
    --primary-deep: #e65f00;   /* gradient anchor */
    --primary-soft: rgba(255, 122, 26, 0.12);
    --primary-ring: rgba(255, 122, 26, 0.30);

    /* Status */
    --danger:       #f87171;
    --danger-bg:    rgba(248, 113, 113, 0.12);
    --success:      #34d399;
    --success-bg:   rgba(52, 211, 153, 0.12);
    --warning:      #fbbf24;
    --warning-bg:   rgba(251, 191, 36, 0.12);

    /* Chart palette — bright on dark, used in Phase 7 */
    --chart-1: #ff7a1a;   /* orange  */
    --chart-2: #34d399;   /* mint    */
    --chart-3: #60a5fa;   /* sky     */
    --chart-4: #c084fc;   /* lilac   */
    --chart-5: #fbbf24;   /* amber   */

    /* Shape */
    --radius-sm:    8px;
    --radius:       12px;
    --radius-lg:    16px;
    --radius-xl:    20px;

    /* Shadows */
    --shadow-sm:    0 1px 2px rgba(0, 0, 0, 0.30);
    --shadow:       0 8px 24px rgba(0, 0, 0, 0.35);
    --shadow-lg:    0 16px 40px rgba(0, 0, 0, 0.45);
    --shadow-glow:  0 12px 30px rgba(255, 122, 26, 0.18);

    /* Layout */
    --container:    1120px;
    --nav-h:        64px;
}


/* ---------- 3. Base ---------- */
html, body { height: 100%; }

body {
    font-family: 'Inter', system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
    background: var(--bg);
    color: var(--text);
    line-height: 1.55;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { line-height: 1.2; letter-spacing: -0.01em; }
h1 { font-size: clamp(1.75rem, 5vw, 2.75rem); font-weight: 800; }
h2 { font-size: clamp(1.5rem, 2.5vw, 2rem);  font-weight: 700; }
h3 { font-size: 1.15rem; font-weight: 600; }

p   { color: var(--text-dim); }
a   { color: var(--primary);     text-decoration: none; transition: color .15s ease; }
a:hover { color: var(--primary-hot); }

::selection { background: var(--primary); color: #fff; }


/* ---------- 4. Layout primitives ---------- */
.container {
    width: 100%;
    max-width: var(--container);
    margin-inline: auto;
    padding-inline: 1.25rem;
}

/* Wider variant — used on data-dense pages like the dashboard where
   more horizontal real estate helps the snapshot grid + charts
   breathe on large monitors. Stays the same on tablet/phone since
   the inner padding caps the width below the breakpoint. */
.container--wide {
    max-width: 1400px;
}

/* Page entry — content fades + rises 12px into place over 350ms
   when the page first paints. Header and footer appear instantly
   so the structural chrome feels solid; only the per-page content
   materializes. Reduced-motion users get nothing via the global rule. */
@keyframes pageEntry {
    from { opacity: 0; transform: translateY(12px); }
    to   { opacity: 1; transform: translateY(0); }
}
.site-main {
    flex: 1;
    display: flex;
    flex-direction: column;
    animation: pageEntry .35s ease-out both;
}
/* Skip the page-entry fade for one render — set by controllers via
   flash('skip_page_entry', '1') so toggle-style actions (calorie
   goal pills) feel instant instead of re-animating the whole page. */
.site-main.no-page-entry { animation: none; }


/* ---------- 5. Sticky site header + nav ---------- */
/* Skip-to-main link — invisible until a keyboard user tabs onto it,
   then slides into view. Lets screen-reader / keyboard users bypass
   the nav. Targets <main id="main" tabindex="-1"> in header.php. */
.skip-link {
    position: absolute;
    top: -100px;
    left: 0;
    z-index: 100;
    padding: 0.65rem 1rem;
    background: var(--primary);
    /* Dark text on the orange clears 6.8:1 contrast (AAA). White
       text on the same orange was only 2.7:1 — failed AA. */
    color: var(--bg);
    font-weight: 700;
    border-radius: 0 0 var(--radius-sm) 0;
    text-decoration: none;
    transition: top .15s ease;
}
.skip-link:focus,
.skip-link:focus-visible {
    top: 0;
    color: var(--bg);
    outline: none;
}
/* Suppress the focus ring when JS or the skip link programmatically
   focuses <main> — it's a target, not an interactive element. */
main:focus { outline: none; }

.site-header {
    position: sticky;
    top: 0;
    z-index: 50;
    background: rgba(15, 18, 24, 0.85);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--border-soft);
    transition: box-shadow .2s ease, background-color .2s ease;
}
.site-header.is-scrolled {
    box-shadow: var(--shadow-sm);
    background: rgba(15, 18, 24, 0.95);
}

.nav-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: var(--nav-h);
    gap: 1rem;
}

.brand {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    font-weight: 800;
    font-size: 1.05rem;
    letter-spacing: 0.01em;
    color: var(--text);
    white-space: nowrap;
}
.brand:hover { color: var(--text); }
.brand:focus-visible {
    outline: 2px solid var(--primary-hot);
    outline-offset: 4px;
    border-radius: var(--radius-sm);
}
.brand-mark {
    display: inline-grid;
    place-items: center;
    width: 32px;
    height: 32px;
    border-radius: 9px;
    background: linear-gradient(135deg, var(--primary-deep), var(--primary-hot));
    color: #fff;
    font-weight: 800;
    font-size: 0.85rem;
    box-shadow: 0 4px 14px rgba(255, 122, 26, 0.35);
    overflow: hidden;
}
/* Logo image fills the rounded brand square. cover (not contain)
   so the artwork reaches the rounded corners with no padding ring. */
.brand-mark img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.site-nav {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}
.nav-link {
    position: relative;
    color: var(--text-dim);
    font-weight: 500;
    font-size: 0.95rem;
    padding: 0.5rem 0.85rem;
    border-radius: var(--radius-sm);
    transition: color .15s ease, background-color .15s ease;
}
/* Animated underline — sits at the bottom of every nav link, scaled
   horizontally to 0 by default and snapped to 1 on hover / focus /
   active. Grows from the center via transform-origin so the swipe
   reads as deliberate rather than a slide-in from the side. Inset
   left/right matches the horizontal padding so the underline lives
   under the text rather than spanning the full link box. */
.nav-link::after {
    content: "";
    position: absolute;
    left: 0.85rem; right: 0.85rem; bottom: -2px;
    height: 2px;
    border-radius: 2px;
    background: var(--primary);
    transform: scaleX(0);
    transform-origin: center;
    transition: transform .25s cubic-bezier(0.4, 0, 0.2, 1);
}
.nav-link:hover                { color: var(--text); background: rgba(255, 255, 255, 0.03); }
.nav-link:hover::after,
.nav-link.is-active::after,
.nav-link:focus-visible::after { transform: scaleX(1); }
.nav-link:focus-visible {
    outline: 2px solid var(--primary-hot);
    outline-offset: 3px;
    color: var(--text);
}
.nav-link.is-active            { color: var(--primary); }

.nav-cta {
    background: linear-gradient(135deg, var(--primary-deep), var(--primary));
    color: #fff;
    padding: 0.55rem 1.05rem;
    border-radius: 999px;
    font-weight: 600;
    box-shadow: 0 6px 16px rgba(255, 122, 26, 0.25);
    transition: filter .15s ease, transform .15s ease, box-shadow .15s ease, color .15s ease;
}
.nav-cta:hover {
    color: #fff;
    filter: brightness(1.12);
    transform: translateY(-2px);
    box-shadow: 0 10px 22px rgba(255, 122, 26, 0.40);
}
.nav-cta:active {
    transform: translateY(0);
    box-shadow: 0 4px 12px rgba(255, 122, 26, 0.25);
}
.nav-cta:focus-visible {
    outline: 2px solid var(--primary-hot);
    outline-offset: 3px;
}

.nav-hello   { color: var(--text-dim); font-size: 0.9rem; padding: 0 0.4rem; }
.nav-logout  { display: inline; }
.as-button {
    background: none;
    border: none;
    font: inherit;
    color: inherit;
    cursor: pointer;
    padding: 0;
}

/* Mobile hamburger — hidden on desktop, revealed in @media block */
.nav-toggle {
    display: none;
    width: 40px;
    height: 40px;
    border: 1px solid var(--border);
    background: var(--bg-card);
    border-radius: var(--radius-sm);
    color: var(--text);
    align-items: center;
    justify-content: center;
    cursor: pointer;
}
.nav-toggle:hover { background: var(--bg-card-2); }
.nav-toggle svg   { width: 20px; height: 20px; }
.nav-toggle .icon-close { display: none; }
.nav-toggle[aria-expanded="true"] .icon-open  { display: none; }
.nav-toggle[aria-expanded="true"] .icon-close { display: inline-block; }


/* ---------- 6. Site footer ---------- */
.site-footer {
    margin-top: 4rem;
    padding: 2rem 0 1.5rem;
    background: var(--bg-elev);
    border-top: 1px solid var(--border-soft);
    color: var(--text-faint);
}
.footer-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem 2rem;
}
.footer-links {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 1.25rem;
    list-style: none;
}
.footer-links a {
    color: var(--text-dim);
    font-size: 0.9rem;
}
.footer-links a:hover { color: var(--primary); }
.footer-links a:focus-visible {
    outline: 2px solid var(--primary-hot);
    outline-offset: 3px;
    border-radius: var(--radius-sm);
}
.footer-copy { font-size: 0.85rem; }


/* ---------- 7. Buttons ---------- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.8rem 1.1rem;
    border: none;
    border-radius: var(--radius);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    background: linear-gradient(135deg, var(--primary-deep), var(--primary));
    color: #fff;
    box-shadow: 0 6px 18px rgba(255, 122, 26, 0.25);
    transition: filter .15s ease, transform .15s ease, box-shadow .15s ease;
    text-decoration: none;
}
.btn:hover {
    filter: brightness(1.12);
    color: #fff;
    transform: translateY(-2px);
    box-shadow: 0 12px 26px rgba(255, 122, 26, 0.40);
}
.btn:active {
    transform: translateY(0);
    box-shadow: 0 4px 12px rgba(255, 122, 26, 0.25);
}
.btn:focus-visible {
    outline: 2px solid var(--primary-hot);
    outline-offset: 3px;
}

/* Subtle CTA pulse — soft orange halo that breathes in and out on
   the marketing primary buttons (home hero, about CTA card, 404
   "back to home"). Auth submits, profile saves, tracker forms, and
   anything tagged .btn-inline / .btn-secondary stay calm. The halo
   layers on top of the base lift shadow so the button itself doesn't
   move, only the halo intensity. Pause on hover so the :hover declared
   shadow takes over cleanly, and skip while the submit-spinner is
   running (.is-loading). Gated behind prefers-reduced-motion. */
@media (prefers-reduced-motion: no-preference) {
    @keyframes ctaGlowPulse {
        0%, 100% {
            box-shadow:
                0 6px 18px rgba(255, 122, 26, 0.25),
                0 0 0 0 rgba(255, 122, 26, 0);
        }
        50% {
            box-shadow:
                0 6px 18px rgba(255, 122, 26, 0.25),
                0 0 28px 4px rgba(255, 122, 26, 0.28);
        }
    }
    .hero-cta   .btn:not(.btn-secondary):not(.btn-inline):not(.is-loading),
    .cta-card   .btn:not(.btn-secondary):not(.btn-inline):not(.is-loading),
    .error-actions .btn:not(.btn-secondary):not(.btn-inline):not(.is-loading) {
        animation: ctaGlowPulse 3.6s ease-in-out infinite;
    }
    .hero-cta   .btn:hover,
    .cta-card   .btn:hover,
    .error-actions .btn:hover {
        animation: none;
    }
}

.btn-inline { width: auto; }

/* Submit-in-flight state — set by main.js when a POST form is
   submitted so the user gets feedback while the network round-trips.
   The button text is replaced with "Working…" (overridable via
   data-loading-text) and a small spinner is prepended.
   Pointer-events:none prevents accidental double-submits. */
.is-loading {
    opacity: 0.85;
    cursor: wait;
    pointer-events: none;
}
.btn-spinner {
    display: inline-block;
    width: 0.95rem;
    height: 0.95rem;
    border-radius: 50%;
    border: 2px solid currentColor;
    border-top-color: transparent;
    animation: btnSpin .7s linear infinite;
    vertical-align: -0.15em;
}
@keyframes btnSpin {
    to { transform: rotate(360deg); }
}
/* Reduced-motion users get the loading label only — no spinning ring. */
@media (prefers-reduced-motion: reduce) {
    .btn-spinner { display: none; }
}

.btn-secondary {
    background: transparent;
    color: var(--text);
    border: 1px solid var(--border);
    box-shadow: none;
}
/* Hover swaps the bg-tint for a soft orange tint + an orange border
   and lifts the button — much more visible than the previous
   bg-card-2 swap which was nearly invisible on dark surfaces. */
.btn-secondary:hover {
    background: var(--primary-soft);
    color: var(--text);
    border-color: var(--border-hot);
    filter: none;
    transform: translateY(-2px);
    box-shadow: 0 8px 18px rgba(0, 0, 0, 0.25);
}
.btn-secondary:active {
    transform: translateY(0);
    box-shadow: none;
}

.btn-ghost {
    background: transparent;
    color: var(--text-dim);
    box-shadow: none;
    padding: 0.55rem 0.85rem;
    width: auto;
}
.btn-ghost:hover { color: var(--text); background: rgba(255,255,255,.04); }


/* ---------- 8. Forms + auth card ---------- */
.auth-shell {
    display: grid;
    place-items: center;
    flex: 1;
    padding: 3rem 1.25rem;
}
.auth-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: 2.25rem;
    width: 100%;
    max-width: 440px;
}
.auth-card h1 {
    font-size: 1.6rem;
    font-weight: 700;
    margin-bottom: 0.35rem;
    letter-spacing: -0.01em;
}
.auth-card .lede {
    color: var(--text-dim);
    font-size: 0.95rem;
    margin-bottom: 1.5rem;
}
.auth-footer {
    margin-top: 1.25rem;
    text-align: center;
    font-size: 0.9rem;
    color: var(--text-dim);
}
.auth-footer a { font-weight: 500; }

/* Atmospheric photo background for /login and /register. The image
   fills the area outside the card (like a hero background) so the
   page feels like a place, not a form. A dark vignette overlay sits
   on top so the card stays readable, and the card itself picks up a
   warm orange halo so it lifts cleanly off the photo. */
.auth-shell--photo {
    position: relative;
    isolation: isolate;
    overflow: hidden;
}
.auth-shell--photo::before {
    content: "";
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    z-index: -2;
}
.auth-shell--photo::after {
    content: "";
    position: absolute;
    inset: 0;
    background:
        radial-gradient(ellipse at center,
            rgba(15, 18, 24, 0.42) 0%,
            rgba(15, 18, 24, 0.68) 70%,
            rgba(15, 18, 24, 0.86) 100%);
    z-index: -1;
}
.auth-shell--login::before  { background-image: url('../images/loginbackground.png'); }
.auth-shell--signup::before { background-image: url('../images/signupbackground.png'); }

/* Ken Burns — slow scale + drift on the auth background photo so the
   page feels alive even when the user is just typing a password. The
   parent has overflow: hidden, so the scaled pseudo never reveals its
   edges. 28s round-trip with ease-in-out alternate means there's no
   pop at the loop point. Skipped under prefers-reduced-motion. */
@media (prefers-reduced-motion: no-preference) {
    @keyframes authPhotoKenBurns {
        from { transform: scale(1.00) translate(0, 0); }
        to   { transform: scale(1.08) translate(-1.5%, -1%); }
    }
    .auth-shell--photo::before {
        animation: authPhotoKenBurns 28s ease-in-out infinite alternate;
        will-change: transform;
        transform-origin: center center;
    }
}

.auth-shell--photo > .auth-card {
    position: relative;
    z-index: 1;
    border-color: rgba(255, 138, 56, 0.28);
    box-shadow:
        0 0 0 1px rgba(255, 122, 26, 0.18),
        0 0 50px -8px rgba(255, 122, 26, 0.42),
        0 0 130px -20px rgba(255, 122, 26, 0.28),
        var(--shadow-lg);
}

.field { margin-bottom: 1rem; }
.field label {
    display: block;
    font-size: 0.85rem;
    color: var(--text-dim);
    margin-bottom: 0.4rem;
    font-weight: 500;
}
.field input[type="text"],
.field input[type="email"],
.field input[type="password"],
.field input[type="number"],
.field input[type="date"] {
    width: 100%;
    padding: 0.7rem 0.9rem;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text);
    font: inherit;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.field input::placeholder { color: var(--text-faint); }
.field input:focus {
    outline: none;
    border-color: var(--primary-hot);
    box-shadow:
        0 0 0 4px var(--primary-ring),
        0 1px 2px rgba(0, 0, 0, 0.20) inset;
}

/* Date inputs: opt into the browser's dark calendar popup
   (color-scheme: dark is honored by Chrome/Edge/Firefox/Safari) and
   paint a custom inline-SVG calendar icon on the right edge so the
   icon matches the chevron used on .field select rather than the
   browser's default glyph. Modern Chromium opens the picker on a
   click anywhere inside the input, so hiding the native indicator
   doesn't break the affordance. Safari may still render its own
   indicator on top — that's acceptable degradation. */
.field input[type="date"] {
    color-scheme: dark;
    cursor: pointer;
    font-variant-numeric: tabular-nums;
    background-image: url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 24 24' fill='none' stroke='%23a4a8b5' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='3' y='5' width='18' height='16' rx='2'/%3E%3Cline x1='3' y1='10' x2='21' y2='10'/%3E%3Cline x1='8' y1='3' x2='8' y2='7'/%3E%3Cline x1='16' y1='3' x2='16' y2='7'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.85rem center;
    background-size: 18px;
    padding-right: 2.5rem;
}
.field input[type="date"]:hover {
    border-color: var(--border-hot);
    background-image: url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 24 24' fill='none' stroke='%23ff9447' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='3' y='5' width='18' height='16' rx='2'/%3E%3Cline x1='3' y1='10' x2='21' y2='10'/%3E%3Cline x1='8' y1='3' x2='8' y2='7'/%3E%3Cline x1='16' y1='3' x2='16' y2='7'/%3E%3C/svg%3E");
}
.field input[type="date"]::-webkit-calendar-picker-indicator {
    display: none;
}
.field-hint {
    display: block;
    color: var(--text-faint);
    font-size: 0.78rem;
    margin-top: 0.35rem;
}

/* Custom <select> — strips the browser default via appearance: none
   and re-paints with the same look as text inputs (so a form mixing
   the two reads as one cohesive control surface). The dropdown
   chevron is an inline SVG data URI rendered as a background image
   on the right. The <option> elements still use the browser's
   native popup since CSS can't reliably style those across browsers;
   we set a dark background on them so at least Chrome/Firefox/Edge
   render the list on a dark surface instead of system white. */
.field select {
    width: 100%;
    padding: 0.7rem 2.5rem 0.7rem 0.9rem;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text);
    font: inherit;
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
    background-image: url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 24 24' fill='none' stroke='%238c91a0' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round' xmlns='http://www.w3.org/2000/svg'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.85rem center;
    background-size: 16px;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.field select:hover  { border-color: var(--border-hot); }
.field select:focus {
    outline: none;
    border-color: var(--primary-hot);
    box-shadow:
        0 0 0 4px var(--primary-ring),
        0 1px 2px rgba(0, 0, 0, 0.20) inset;
}
.field select option {
    background: var(--bg-elev);
    color: var(--text);
}

/* Password show/hide toggle (Phase 4 carryover) */
.password-wrap   { position: relative; display: block; }
.field .password-wrap input[type="password"],
.field .password-wrap input[type="text"]    { padding-right: 2.75rem; }
.password-toggle {
    position: absolute;
    right: 0.4rem;
    top: 50%;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    padding: 0;
    background: transparent;
    border: none;
    border-radius: 8px;
    color: var(--text-faint);
    cursor: pointer;
    transition: color .12s ease, background-color .12s ease;
}
.password-toggle:hover         { color: var(--text); background: rgba(255,255,255,.06); }
.password-toggle:focus-visible { outline: 2px solid var(--primary-hot); outline-offset: 2px; }
.password-toggle .eye          { width: 1.15rem; height: 1.15rem; }
.password-toggle .eye-hide     { display: none; }
.password-toggle[aria-pressed="true"] .eye-show { display: none; }
.password-toggle[aria-pressed="true"] .eye-hide { display: inline-block; }

.checkbox-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin: 0.25rem 0 1.25rem;
    font-size: 0.88rem;
    color: var(--text-dim);
}
.checkbox-row label {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
}

/* Custom checkbox — strips the browser default via appearance: none
   and paints a small rounded square that fills with a warm orange
   gradient + a white check on :checked. The check is an inline SVG
   data URI so no extra DOM is needed; the existing markup keeps
   working as-is.

   Scoped to the contexts where checkboxes actually appear today
   (just .checkbox-row for the "Remember me" toggle on login) so the
   styling doesn't bleed into surprise spots. Extend the selector
   list if more checkbox surfaces show up. */
.checkbox-row input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    width: 18px;
    height: 18px;
    border: 1.5px solid var(--border);
    border-radius: 5px;
    background: var(--bg-input);
    cursor: pointer;
    position: relative;
    margin: 0;
    flex-shrink: 0;
    transition: background-color .15s ease, border-color .15s ease;
}
.checkbox-row input[type="checkbox"]:hover {
    border-color: var(--border-hot);
}
.checkbox-row input[type="checkbox"]:checked {
    background: linear-gradient(135deg, var(--primary-deep), var(--primary));
    border-color: var(--primary);
}
.checkbox-row input[type="checkbox"]:checked::after {
    content: "";
    position: absolute;
    inset: 0;
    background-image: url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpolyline points='3 8.5 6.5 12 13 4.5' stroke='%23fff' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: center;
    background-size: 78%;
}
.checkbox-row input[type="checkbox"]:focus-visible {
    outline: none;
    box-shadow: 0 0 0 4px var(--primary-ring);
}


/* ---------- 9. Flash + error ---------- */
/* Slide-down + slight overshoot when the success banner first
   renders, so the "saved!" cue is unmissable. The cubic-bezier ends
   slightly above 1 to give a tiny settle bounce. */
@keyframes flashSlideIn {
    from { opacity: 0; transform: translateY(-12px); }
    to   { opacity: 1; transform: translateY(0); }
}
@keyframes flashSlideOut {
    from { opacity: 1; transform: translateY(0); }
    to   { opacity: 0; transform: translateY(-12px); }
}
.flash {
    max-width: var(--container);
    margin: 1rem auto 0;
    padding: 0.85rem 1rem;
    border-radius: var(--radius);
    font-size: 0.9rem;
    cursor: pointer;            /* click to dismiss */
    animation: flashSlideIn .35s cubic-bezier(.2, .9, .3, 1.05) both;
    transition: opacity .15s ease;
}
.flash:hover { opacity: 0.85; }
.flash.is-dismissing {
    animation: flashSlideOut .3s ease-out forwards;
    pointer-events: none;
}
.flash-success {
    background: var(--success-bg);
    color: var(--success);
    border: 1px solid rgba(52, 211, 153, 0.25);
}

.error-box {
    background: var(--danger-bg);
    border: 1px solid rgba(248, 113, 113, 0.25);
    color: var(--danger);
    border-radius: var(--radius);
    padding: 0.8rem 1rem;
    margin-bottom: 1.25rem;
    font-size: 0.88rem;
}
.error-box ul { list-style: none; display: flex; flex-direction: column; gap: 0.25rem; }
.error-box li::before { content: "• "; margin-right: 0.25rem; }

/* ----- Soft email-verification banner ------------------------------- */
/* Pinned just under the header for any logged-in user whose email
   hasn't been confirmed. Full-bleed background + container-constrained
   inner row so it reads as a system message, not a card. */
.verify-banner {
    background: var(--warning-bg);
    border-top:    1px solid rgba(251, 191, 36, 0.30);
    border-bottom: 1px solid rgba(251, 191, 36, 0.30);
    color: var(--text);
    font-size: 0.9rem;
}
.verify-banner__row {
    padding: 0.7rem 0;
}
.verify-banner__text { line-height: 1.5; }
.verify-banner__text strong { color: var(--warning); margin-right: 0.4rem; }

/* The form sits inline at the end of the sentence rather than as a
   separate flex item on the right edge — feels like part of the
   message instead of a floating CTA with a long visual gap. */
.verify-banner__form {
    display: inline;
    margin: 0;
}
.verify-banner__form .btn-link {
    color: var(--warning);
    padding: 0.05rem 0.4rem;
    margin-left: 0.35rem;
}
.verify-banner__form .btn-link:hover {
    color: #fde68a;
    background: rgba(251, 191, 36, 0.10);
}


/* ---------- 10. Stub / placeholder ---------- */
.stub-wrap {
    display: grid;
    place-items: center;
    flex: 1;
    padding: 3rem 1.25rem;
}
.stub-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    padding: 2.5rem;
    max-width: 480px;
    width: 100%;
}
.stub-badge {
    display: inline-block;
    padding: 0.35rem 0.8rem;
    border-radius: 999px;
    background: linear-gradient(135deg, var(--primary-deep), var(--primary));
    color: #fff;
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    margin-bottom: 1rem;
}
.stub-card h1 { font-size: 1.75rem; font-weight: 700; margin-bottom: 0.5rem; }
.stub-card p  { color: var(--text-dim); margin-bottom: 1.5rem; }


/* ===========================================================
   11. Landing page
   =========================================================== */

/* ----- Hero ----- */
.hero {
    position: relative;
    overflow: hidden;
    padding: 4.5rem 0 3.5rem;
    background:
        radial-gradient(circle at 12% 10%,  rgba(255, 122, 26, 0.18) 0%, transparent 45%),
        radial-gradient(circle at 90% 0%,   rgba(96, 165, 250, 0.10) 0%, transparent 50%),
        linear-gradient(180deg, #131722 0%, var(--bg) 100%);
    border-bottom: 1px solid var(--border-soft);
}

/* Mesh-gradient blobs behind the landing hero (excluded from the
   --compact tracker heroes so it only paints on /home). Two large
   blurred radial gradients drift slowly via translate, layered over
   the hero base gradient with mix-blend-mode: screen so they only
   ever lighten the underlying pixels — the photo on the right stays
   recognizable while the dark left half picks up an atmospheric
   orange/blue glow. Held at low enough opacity to never compete
   with text. */
.hero--photo:not(.hero--compact)::after {
    content: "";
    position: absolute;
    inset: -10%;
    pointer-events: none;
    z-index: 0;
    background:
        radial-gradient(circle at 18% 28%, rgba(255, 122, 26, 0.55) 0%, transparent 32%),
        radial-gradient(circle at 78% 78%, rgba(96, 165, 250, 0.40) 0%, transparent 38%);
    filter: blur(60px);
    mix-blend-mode: screen;
    opacity: 0.55;
}
@media (prefers-reduced-motion: no-preference) {
    @keyframes heroMeshDrift {
        0%   { transform: translate(0%, 0%); }
        50%  { transform: translate(2.5%, -1.5%); }
        100% { transform: translate(0%, 0%); }
    }
    .hero--photo:not(.hero--compact)::after {
        animation: heroMeshDrift 24s ease-in-out infinite;
        will-change: transform;
    }
}

/* Hero with a faded background photo. Reusable across pages — the
   actual image URL comes from the --hero-image custom property set
   inline on the section, so each page (landing, about, ...) can
   point at its own photo without forking the CSS. The image is
   muted to ~22% opacity behind a left-to-right mask fade so the
   headline text on the left stays readable while the right side
   shows the photo through. */
.hero--photo::before {
    content: "";
    position: absolute;
    inset: 0;
    background-image: var(--hero-image);
    background-size: cover;
    background-position: center right;
    background-repeat: no-repeat;
    opacity: 0.40;
    z-index: 0;
    pointer-events: none;
    -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 38%, #000 100%);
            mask-image: linear-gradient(to right, transparent 0%, #000 38%, #000 100%);
}
/* The container needs its own stacking context so the copy (and any
   asset like the BMI card) sits above the ::before image layer. */
.hero--photo > .container {
    position: relative;
    z-index: 1;
}

.hero-grid {
    display: grid;
    grid-template-columns: 1.05fr 1fr;
    gap: 3rem;
    align-items: center;
}

.eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0.8rem;
    border-radius: 999px;
    background: var(--primary-soft);
    color: var(--primary-hot);
    border: 1px solid var(--border-hot);
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    margin-bottom: 1.1rem;
}
.eyebrow::before {
    content: "";
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--primary);
    box-shadow: 0 0 10px var(--primary);
}

/* Tracker hero heading row — pairs the page's h1 with the matching
   tracker logo on the right, vertically centered. Carries the brand
   mark from home / about / dashboard onto the tool page itself.
   The wrapper takes over the h1's bottom margin so spacing to the
   lede stays identical to the pre-icon layout. Generic class names
   so the pattern can extend to /weight + /strength later. */
.hero-heading-row {
    display: flex;
    align-items: center;
    gap: 1.25rem;
    margin-bottom: 0.75rem;
}
.hero-heading-row h1 { margin-bottom: 0; }
.hero-icon {
    display: block;
    width: 96px;
    height: 96px;
    object-fit: contain;
    flex-shrink: 0;
}

.hero h1 {
    color: var(--text);
    margin-bottom: 1rem;
}
.hero h1 .accent {
    background: linear-gradient(135deg, var(--primary-hot), var(--primary));
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
}
.hero-lede {
    font-size: 1.05rem;
    color: var(--text-dim);
    margin-bottom: 1.75rem;
    max-width: 38ch;
}
.hero-cta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
}
.hero-cta .btn { width: auto; padding: 0.85rem 1.4rem; }


/* ----- BMI calculator card ----- */
.bmi-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-xl);
    padding: 1.75rem;
    box-shadow: var(--shadow);
    position: relative;
}
.bmi-card::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    padding: 1px;
    background: linear-gradient(135deg, rgba(255,122,26,.45), transparent 60%);
    -webkit-mask:
        linear-gradient(#000 0 0) content-box,
        linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
            mask-composite: exclude;
    pointer-events: none;
}

.bmi-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1.1rem;
}
.bmi-head h2 {
    font-size: 1.2rem;
    font-weight: 700;
}
.bmi-head .field-hint { margin-top: 0; }

.unit-toggle {
    display: inline-flex;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 3px;
}
.unit-toggle button {
    background: transparent;
    border: none;
    color: var(--text-dim);
    font: inherit;
    font-size: 0.82rem;
    font-weight: 600;
    padding: 0.35rem 0.85rem;
    border-radius: 999px;
    cursor: pointer;
    transition: background-color .15s ease, color .15s ease;
}
.unit-toggle button.is-active {
    background: linear-gradient(135deg, var(--primary-deep), var(--primary));
    color: #fff;
}
.unit-toggle button:focus-visible {
    outline: 2px solid var(--primary-hot);
    outline-offset: 2px;
}

.bmi-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.75rem;
}
.bmi-grid[hidden]   { display: none; }
.bmi-grid .field    { margin-bottom: 0; }
.bmi-grid--imperial { grid-template-columns: 1fr 1fr 1fr; }

.bmi-result {
    margin-top: 1.25rem;
    padding: 1rem 1.1rem;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    transition: border-color .2s ease;
}
.bmi-result__value {
    font-size: 2rem;
    font-weight: 800;
    line-height: 1;
    color: var(--text);
}
.bmi-result__label {
    font-size: 0.78rem;
    color: var(--text-faint);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin-top: 0.25rem;
}
.bmi-result__chip {
    display: inline-flex;
    align-items: center;
    padding: 0.4rem 0.85rem;
    border-radius: 999px;
    font-weight: 600;
    font-size: 0.85rem;
}
.bmi-result__chip[data-cat="under"]  { background: rgba(96,165,250,.15);  color: #93c5fd; }
.bmi-result__chip[data-cat="normal"] { background: var(--success-bg);     color: var(--success); }
.bmi-result__chip[data-cat="over"]   { background: var(--warning-bg);     color: var(--warning); }
.bmi-result__chip[data-cat="obese"]  { background: var(--danger-bg);      color: var(--danger); }
.bmi-result__chip[data-cat="none"]   { background: rgba(255,255,255,.05); color: var(--text-faint); }

/* Sign-up CTA shown below the BMI result for logged-out users.
   main.js toggles [hidden] based on whether there's a valid result.
   Subtle fade-in so the appearance feels intentional, not jumpy. */
@keyframes bmiCtaReveal {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
}
.bmi-cta {
    margin-top: 1.25rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--border-soft);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.6rem;
    text-align: center;
    animation: bmiCtaReveal .25s ease both;
}
.bmi-cta__copy {
    font-size: 0.9rem;
    color: var(--text-dim);
    margin: 0;
}
.bmi-cta .btn { width: auto; padding: 0.7rem 1.25rem; }


/* ----- Feature cards ----- */
.section {
    padding: 4rem 0;
}
.section-head {
    text-align: center;
    max-width: 640px;
    margin: 0 auto 2.5rem;
}
.section-head h2 { margin-bottom: 0.6rem; }
.section-head p  { color: var(--text-dim); }

.features {
    display: grid;
    /* Explicit breakpoints so the row stays even regardless of card
       count. Defaults to a 2x2 grid; widens to 4-across at laptop and
       collapses to a single column on phones. */
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.25rem;
}
@media (min-width: 1024px) {
    .features { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (max-width: 600px) {
    .features { grid-template-columns: 1fr; }
}
.feature-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 1.75rem;
    transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease;
}
.feature-card:hover {
    transform: translateY(-3px);
    border-color: var(--border-hot);
    box-shadow: var(--shadow-glow);
}
.feature-icon {
    display: inline-grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border-radius: 12px;
    background: var(--primary-soft);
    color: var(--primary-hot);
    margin-bottom: 1rem;
}
.feature-icon svg { width: 22px; height: 22px; }
/* PNG logo variant (replaces .feature-icon on the home feature
   cards). Larger and frame-less so the logo itself is the visual
   anchor instead of the icon-in-a-pill treatment. margin-inline
   auto centers it horizontally inside the card. */
.feature-image {
    display: block;
    width: 88px;
    height: 88px;
    object-fit: contain;
    margin: 0 auto 1rem;
}
/* Cards that use the PNG logo (home page) get centered text too —
   image and text aligned together reads cleaner than centered image
   over left-aligned text. Scoped via :has() so the dashboard's
   quick-action tiles (also .feature-card, but using .feature-icon)
   keep their left-aligned look. */
.feature-card:has(.feature-image) {
    text-align: center;
}
.feature-card h3 { margin-bottom: 0.4rem; color: var(--text); }
.feature-card p  { font-size: 0.95rem; color: var(--text-dim); }


/* ----- Quote ----- */
.quote {
    margin: 1rem auto 4rem;
    max-width: 720px;
    text-align: center;
    padding: 2rem 1.5rem;
    border-top: 1px solid var(--border-soft);
    border-bottom: 1px solid var(--border-soft);
}
.quote blockquote {
    font-size: 1.25rem;
    font-style: italic;
    color: var(--text);
    line-height: 1.5;
}
.quote blockquote::before { content: "“"; color: var(--primary); margin-right: .15rem; }
.quote blockquote::after  { content: "”"; color: var(--primary); margin-left:  .15rem; }
.quote cite {
    display: block;
    margin-top: 0.75rem;
    font-style: normal;
    font-size: 0.85rem;
    color: var(--text-faint);
    letter-spacing: 0.05em;
    text-transform: uppercase;
}


/* ===========================================================
   12. Responsive
   =========================================================== */
@media (max-width: 860px) {
    .hero            { padding: 3rem 0 2.5rem; }
    .hero-grid       { grid-template-columns: 1fr; gap: 2rem; }
    .section         { padding: 3rem 0; }

    /* Mobile nav — bumped up from 720px because the logged-in nav
       (Home/About/Contact/Dashboard + profile pill + Logout) was
       crowding the bar between 720 and 860. */
    .nav-toggle      { display: inline-flex; }

    .site-nav {
        position: absolute;
        top: 100%;
        left: 0; right: 0;
        flex-direction: column;
        align-items: stretch;
        gap: 0.25rem;
        padding: 0.75rem 1.25rem 1rem;
        background: var(--bg-elev);
        border-bottom: 1px solid var(--border-soft);
        box-shadow: var(--shadow);
        max-height: 0;
        overflow: hidden;
        opacity: 0;
        pointer-events: none;
        transition: max-height .25s ease, opacity .15s ease, padding .25s ease;
    }
    .site-nav.is-open {
        max-height: 70vh;
        opacity: 1;
        pointer-events: auto;
    }
    .nav-link        { padding: 0.7rem 0.85rem; font-size: 1rem; }
    /* No underline on the stacked mobile menu — links read as a list,
       not a row, so the underline visual would feel out of place. */
    .nav-link::after { display: none; }
    .nav-cta         { text-align: center; margin-top: 0.35rem; }
    .nav-hello       { padding: 0.5rem 0.85rem; }
}

@media (max-width: 720px) {
    .bmi-grid--imperial { grid-template-columns: 1fr 1fr; }
    .bmi-result      { flex-direction: column; align-items: flex-start; }
    /* Tracker hero icon — hide on narrow phones so the h1 has the full
       width. The icon is decorative (the eyebrow + heading still
       announce the page), so dropping it on small screens is safe. */
    .hero-heading-row .hero-icon { display: none; }
}

@media (max-width: 420px) {
    .auth-card       { padding: 1.75rem; }
}


/* ===========================================================
   13. Phase 6 — About + Contact pages
   =========================================================== */

/* ----- Compact hero (used on About + Contact intros) ----- */
.hero--compact {
    padding: 3rem 0 2.25rem;
}
.hero--compact h1     { margin-bottom: 0.75rem; }
.hero--compact .hero-lede { max-width: 60ch; margin-bottom: 0; }

/* ----- Alternate section band ----- */
.section--alt {
    background: var(--bg-elev);
    border-top:    1px solid var(--border-soft);
    border-bottom: 1px solid var(--border-soft);
}

/* ----- Section divider — subtle 3-dot mark between two plain
   sections (e.g. Summary → Trends on the dashboard). Painted via
   the second section's ::before so no markup changes are needed.
   Skipped when either side is .section--alt because that variant
   already shows its own top/bottom borders as the visual boundary.
   The dots are a tiny radial-gradient pattern tiled horizontally,
   tinted brand orange at low opacity so they whisper rather than
   shout. */
.section:not(.section--alt) + .section:not(.section--alt) {
    position: relative;
}
.section:not(.section--alt) + .section:not(.section--alt)::before {
    content: "";
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 64px;
    height: 4px;
    background-image: radial-gradient(circle, var(--primary-hot) 1.4px, transparent 1.8px);
    background-size: 20px 4px;
    background-repeat: repeat-x;
    background-position: center;
    opacity: 0.45;
}

/* ----- Long-form paragraph ----- */
.prose {
    color: var(--text-dim);
    max-width: 60ch;
    margin-top: 1rem;
}

/* ----- "How it works" two-column ----- */
.how-grid {
    display: grid;
    grid-template-columns: 1fr 1.1fr;
    gap: 3rem;
    align-items: start;
}
.how-grid h2 { margin-top: 0.75rem; }

/* ----- Numbered steps list ----- */
.steps {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 1rem;
}
.steps li {
    display: flex;
    gap: 1rem;
    align-items: flex-start;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 1.1rem 1.25rem;
    /* Match the .feature-card hover from §11 (lift + warm border + glow). */
    transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease;
}
.steps li:hover {
    transform: translateY(-3px);
    border-color: var(--border-hot);
    box-shadow: var(--shadow-glow);
}
.steps-num {
    flex-shrink: 0;
    width: 36px;
    height: 36px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--primary-deep), var(--primary));
    color: #fff;
    font-weight: 700;
    box-shadow: 0 6px 14px rgba(255, 122, 26, 0.25);
}
.steps li h3 { margin-bottom: 0.2rem; }
.steps li p  { font-size: 0.95rem; color: var(--text-dim); }

/* ----- "Who it's for" two-column ----- */
.who-grid {
    display: grid;
    grid-template-columns: 1.4fr 1fr;
    gap: 2.5rem;
    align-items: center;
}

/* ----- Small CTA card (about + contact reuse) ----- */
.cta-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 1.75rem;
    box-shadow: var(--shadow);
    transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease;
}
/* Hover lift to match .feature-card — the card itself isn't a link
   (the inner .btn is), so we use no pointer cursor; this is purely
   a visual flourish to make the section feel alive. */
.cta-card:hover {
    transform: translateY(-3px);
    border-color: var(--border-hot);
    box-shadow: var(--shadow-glow);
}
.cta-card h3 { margin-bottom: 0.5rem; }
.cta-card p  { color: var(--text-dim); margin-bottom: 1.25rem; }
.cta-card .btn { width: 100%; }

/* Photo variant — image fills the entire card as a background, with
   a dark gradient overlay on top so the heading, paragraph, and
   button stay readable. The image URL comes from the --cta-image
   custom property set inline on the element so each use can point
   at its own photo. Used on /about's "Who it's for" CTA card; the
   plain .cta-card style still applies on /contact. */
.cta-card--photo {
    position: relative;
    overflow: hidden;
    background-image: var(--cta-image);
    background-size: cover;
    background-position: center;
}
.cta-card--photo::before {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(
        180deg,
        rgba(11, 15, 23, 0.45) 0%,
        rgba(11, 15, 23, 0.78) 100%
    );
    z-index: 0;
}
/* All direct children (h3, p, .btn) sit above the overlay. */
.cta-card--photo > * {
    position: relative;
    z-index: 1;
}
/* Paragraph color in the photo variant — the default --text-dim is
   a touch low-contrast against the darkened photo, so bump it up
   to the regular text color for legibility. */
.cta-card--photo p { color: var(--text); }

/* ----- Contact page two-column ----- */
.contact-grid {
    display: grid;
    grid-template-columns: 1.3fr 1fr;
    gap: 2rem;
    align-items: start;
}

.contact-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 2rem;
    box-shadow: var(--shadow);
}

.contact-info {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}
.info-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 1.5rem;
}
.info-card h2,
.info-card h3 { margin-bottom: 0.5rem; }
/* Heading hierarchy on the contact page demands h2 here (h3 would
   skip a level after the page's h1). Style as compact card title so
   the visual matches the original h3 look. */
.info-card h2 { font-size: 1.15rem; font-weight: 600; letter-spacing: -0.01em; }
.info-card p  { color: var(--text-dim); font-size: 0.95rem; }

/* ----- FAQ (definition list) ----- */
.faq           { display: flex; flex-direction: column; gap: 0.75rem; margin-top: 0.4rem; }
.faq dt        { color: var(--text); font-weight: 600; font-size: 0.95rem; }
.faq dd        { color: var(--text-dim); font-size: 0.9rem; margin-left: 0; }

/* ----- Textarea (matches input styling from §8) ----- */
.field textarea {
    width: 100%;
    padding: 0.7rem 0.9rem;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text);
    font: inherit;
    line-height: 1.5;
    resize: vertical;
    min-height: 8rem;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.field textarea::placeholder { color: var(--text-faint); }
.field textarea:focus {
    outline: none;
    border-color: var(--primary-hot);
    box-shadow:
        0 0 0 4px var(--primary-ring),
        0 1px 2px rgba(0, 0, 0, 0.20) inset;
}

/* ----- Inline field error (sits directly under its input) ----- */
.field-error {
    color: var(--danger);
    font-size: 0.82rem;
    margin-top: 0.4rem;
    line-height: 1.35;
}
/* Tinted border + focus ring on invalid inputs */
.field input[aria-invalid="true"],
.field textarea[aria-invalid="true"] {
    border-color: rgba(248, 113, 113, 0.45);
}
.field input[aria-invalid="true"]:focus,
.field textarea[aria-invalid="true"]:focus {
    border-color: var(--danger);
    box-shadow: 0 0 0 4px rgba(248, 113, 113, 0.22);
}

/* ----- Inline flash variant (renders inside a card, not the page) ----- */
.flash--inline {
    max-width: none;
    margin: 0 0 1.25rem;
}

/* ----- Compact centered flash (sits above a form, not a full band) ---- */
.flash--centered {
    max-width: max-content;
    margin: 0 auto 1.25rem;
    text-align: center;
}

/* ----- Phase 6 responsive ----- */
@media (max-width: 860px) {
    .how-grid,
    .who-grid,
    .contact-grid { grid-template-columns: 1fr; gap: 1.75rem; }
}


/* ===================================================================
   Phase 7 — Tracker pages (calorie / weight / strength share these)
   =================================================================== */

/* ----- Card wrapper for any tracker section -------------------------- */
.tracker-card {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 1.75rem;
    box-shadow: var(--shadow);
    margin-bottom: 1.5rem;
}
.tracker-card:last-child { margin-bottom: 0; }

.tracker-card__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1.5rem;
    flex-wrap: wrap;
}
.tracker-card__head h2 {
    margin: 0 0 0.25rem;
    font-size: 1.25rem;
    line-height: 1.3;
}
.tracker-card__head h3 { margin: 0; }

/* ----- Form layout shared across trackers ---------------------------- */
.form-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem 1.25rem;
    margin-bottom: 1.25rem;
}
/* Override the inherited display:grid so [hidden] panes actually hide.
   Same gotcha the BMI calc handles for .bmi-grid above. */
.form-grid[hidden]    { display: none; }
.form-grid .field { margin-bottom: 0; }
.field--wide { grid-column: 1 / -1; }

@media (min-width: 720px) {
    .form-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .field--wide { grid-column: 1 / -1; }
}

/* ----- Distance + unit row (cardio tracker) -------------------------- */
/* Pairs a number input with a small unit <select> so the two move
   together. Uses CSS grid (not flex) so the existing global
   `.field input[type="number"] { width: 100% }` and
   `.field select { width: 100% }` rules cooperate — each child fills
   its grid cell, and the cells have explicit sizes. */
.distance-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 6rem;
    gap: 0.5rem;
    align-items: stretch;
}

/* ----- Non-input "label" used for radio groups ----------------------- */
.field-label {
    display: block;
    font-weight: 600;
    color: var(--text);
    margin-bottom: 0.5rem;
    font-size: 0.95rem;
}

/* ----- Radio pills (gender selector) --------------------------------- */
.radio-row {
    display: flex;
    gap: 0.5rem;
}
.radio-pill {
    position: relative;
    flex: 1;
    cursor: pointer;
    user-select: none;
}
.radio-pill input {
    position: absolute;
    opacity: 0;
    inset: 0;
    cursor: pointer;
}
.radio-pill > span {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0.65rem 0.75rem;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    font-weight: 600;
    color: var(--text-dim);
    transition: all .15s ease;
}
.radio-pill input:checked + span {
    background: var(--primary-soft);
    border-color: var(--border-hot);
    color: var(--primary-hot);
}
.radio-pill input:focus-visible + span {
    box-shadow: 0 0 0 3px var(--primary-ring);
}
.radio-row[aria-invalid="true"] .radio-pill > span {
    border-color: var(--danger);
}

/* ----- Calorie results preview --------------------------------------- */
.cal-preview {
    margin: 0.5rem 0 1.25rem;
    padding: 1.25rem;
    background: var(--bg-card-2);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}
.cal-preview__head {
    margin-bottom: 1rem;
}
.cal-preview__head h3 {
    margin: 0 0 0.25rem;
    font-size: 1.05rem;
}
.cal-preview__grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0.75rem;
}
.cal-preview__cell {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.15rem;
    padding: 1rem 0.5rem;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    text-align: center;
}
.cal-preview__cell--accent {
    background: var(--primary-soft);
    border-color: var(--border-hot);
}
.cal-preview__label {
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-dim);
    font-weight: 700;
}
.cal-preview__cell--accent .cal-preview__label { color: var(--primary-hot); }
.cal-preview__value {
    font-size: 1.6rem;
    font-weight: 800;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}
.cal-preview__unit {
    font-size: 0.78rem;
    color: var(--text-faint);
}

@media (max-width: 520px) {
    .cal-preview__grid { grid-template-columns: 1fr; }
    .cal-preview__cell { padding: 0.75rem 0.5rem; }
}

/* ----- Form action row (bottom of tracker forms) --------------------- */
.form-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-items: center;
}

/* ----- Tiny shake nudge on invalid Calculate ------------------------- */
@keyframes trackerShake {
    0%, 100% { transform: translateX(0); }
    25%      { transform: translateX(-4px); }
    75%      { transform: translateX(4px); }
}
.is-shake { animation: trackerShake .35s ease-in-out; }

/* ----- Back link (top of tracker pages → Dashboard) ----------------- */
/* Subtle "← Back to dashboard" link rendered above the hero eyebrow.
   Arrow nudges left on hover so the affordance reads as "go back". */
.back-link {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    color: var(--text-dim);
    font-size: 0.9rem;
    font-weight: 500;
    text-decoration: none;
    margin-bottom: 1rem;
    padding: 0.25rem 0.5rem;
    margin-left: -0.5rem;
    border-radius: var(--radius-sm);
    transition: color .15s ease, background-color .15s ease;
}
.back-link:hover { color: var(--text); background: rgba(255,255,255,.04); }
.back-link:hover .back-link__arrow { transform: translateX(-3px); }
.back-link:focus-visible {
    outline: 2px solid var(--primary-hot);
    outline-offset: 2px;
}
.back-link__arrow {
    display: inline-block;
    transition: transform .15s ease;
    line-height: 1;
}

/* ----- Empty state card --------------------------------------------- */
.empty-state {
    text-align: center;
    padding: 2.75rem 1.5rem 2.5rem;
}
.empty-state h2 {
    margin: 0 0 0.5rem;
    font-size: 1.15rem;
    color: var(--text);
}
.empty-state p {
    margin: 0;
    color: var(--text-dim);
    max-width: 32rem;
    margin-inline: auto;
}
/* Stroked SVG mark above the heading. Sits in a soft circular halo
   that picks up just enough warm orange to feel branded without
   shouting — the icon is the focal point but the halo gives it
   air. currentColor on the stroke is set by .empty-state__icon
   itself so the halo color can stay independent. */
.empty-state__icon {
    display: block;
    width: 56px;
    height: 56px;
    margin: 0 auto 1.1rem;
    padding: 0.8rem;
    color: var(--primary-hot);
    background: radial-gradient(circle at center,
        rgba(255, 122, 26, 0.14) 0%,
        rgba(255, 122, 26, 0.04) 60%,
        transparent 75%);
    border-radius: 999px;
    box-sizing: content-box;
}

/* ----- Chart canvas wrapper ----------------------------------------- */
.chart-wrap {
    position: relative;
    height: 320px;
}
@media (max-width: 520px) {
    .chart-wrap { height: 260px; }
}

/* Skeleton paint shown while Chart.js (CDN, deferred) is still loading.
   The page renders with .chart-wrap--loading on every wrap that will
   eventually host a chart; each chart's init function in main.js
   removes the class as its first step, so the canvas reveals cleanly
   once data is plotted. A 1.6s shimmer keeps the surface alive without
   shouting. Disabled under reduced-motion (the soft gradient still
   reads as "something is here" without animating). */
.chart-wrap--loading::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: var(--radius);
    background:
        linear-gradient(110deg,
            rgba(255, 255, 255, 0.00) 30%,
            rgba(255, 255, 255, 0.05) 50%,
            rgba(255, 255, 255, 0.00) 70%) ,
        linear-gradient(180deg,
            rgba(255, 122, 26, 0.05) 0%,
            rgba(255, 122, 26, 0.00) 60%) ,
        var(--bg-card-2);
    background-size: 200% 100%, 100% 100%, 100% 100%;
    background-repeat: no-repeat;
    animation: chartShimmer 1.6s ease-in-out infinite;
    z-index: 0;
}
.chart-wrap--loading canvas { position: relative; z-index: 1; }
@keyframes chartShimmer {
    0%   { background-position: -100% 0, 0 0, 0 0; }
    100% { background-position:  200% 0, 0 0, 0 0; }
}
@media (prefers-reduced-motion: reduce) {
    .chart-wrap--loading::before { animation: none; }
}

/* ----- History table ------------------------------------------------- */
.history-scroll {
    overflow-x: auto;
    margin: 0 -0.5rem;
    padding: 0 0.5rem;
}
/* On narrow screens the table scrolls horizontally — fade the right
   edge so the user has a visual cue that there's more content over
   there. The fade is intentionally subtle (last 28px) and only kicks
   in below the breakpoint where overflow actually happens. */
@media (max-width: 720px) {
    .history-scroll {
        -webkit-mask-image: linear-gradient(to right,
                            black 0%, black calc(100% - 28px), transparent 100%);
                mask-image: linear-gradient(to right,
                            black 0%, black calc(100% - 28px), transparent 100%);
    }
}
.history-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.95rem;
    min-width: 520px;
}
.history-table th,
.history-table td {
    padding: 0.75rem 0.85rem;
    text-align: left;
    border-bottom: 1px solid var(--border-soft);
    vertical-align: middle;
}
.history-table thead th {
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-faint);
    font-weight: 700;
    border-bottom-color: var(--border);
}
.history-table tbody tr:last-child td { border-bottom: 0; }
/* Subtle row striping for scannability. Two flavors:
     - Plain history table (weight): every other tbody row picks up
       a 1.5% white tint via :nth-child(even).
     - Day-grouped table (calorie/strength): striping is by DAY, not
       by individual row, so we let JS tag every-other day-row +
       its sub-rows with .is-stripe (see initHistoryStriping in
       main.js). The CSS just picks up the class.
   Both rules sit BEFORE the hover rule so the hover tint wins on
   equal specificity via source order. */
.history-table:not(.history-table--days) tbody tr:nth-child(even),
.history-table tbody tr.is-stripe {
    background-color: rgba(255, 255, 255, 0.018);
}
/* Smooth bg fade on hover (was a hard snap before). */
.history-table tbody tr {
    transition: background-color .15s ease;
}
.history-table tbody tr:hover { background: var(--bg-card-2); }
.history-table .num {
    text-align: right;
    font-variant-numeric: tabular-nums;
    color: var(--text);
}
.history-table .strong { font-weight: 700; }
.history-table .actions {
    text-align: right;
    width: 1%;
    white-space: nowrap;
}
.history-table .actions form { margin: 0; display: inline-block; }

/* ----- Inline danger button (table delete) -------------------------- */
.btn-link-danger {
    background: none;
    border: 0;
    color: var(--danger);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    padding: 0.25rem 0.5rem;
    border-radius: var(--radius-sm);
    transition: background .15s ease;
}
.btn-link-danger:hover  { background: var(--danger-bg); }
.btn-link-danger:focus-visible {
    outline: none;
    box-shadow: 0 0 0 4px rgba(248, 113, 113, 0.30);
}

/* ----- Danger button (account deletion, etc.) ----------------------- */
.btn-danger {
    background: var(--danger);
    color: #2a0f0f;
    border: 1px solid var(--danger);
    box-shadow: 0 2px 0 rgba(0,0,0,.15), 0 8px 20px rgba(248,113,113,.18);
    font-weight: 700;
}
.btn-danger:hover {
    background: #fb8484;
    transform: translateY(-2px);
    box-shadow: 0 4px 0 rgba(0,0,0,.15), 0 14px 28px rgba(248,113,113,.30);
}
.btn-danger:active {
    transform: translateY(0);
    box-shadow: 0 2px 0 rgba(0,0,0,.15), 0 6px 14px rgba(248,113,113,.18);
}
.btn-danger:focus-visible {
    outline: none;
    box-shadow: 0 0 0 4px rgba(248,113,113,.35);
}

/* ----- Danger-zone card (profile account deletion) ------------------ */
.tracker-card--danger {
    border-color: rgba(248, 113, 113, 0.30);
    background: linear-gradient(180deg, rgba(248,113,113,0.05), var(--bg-card) 70%);
}
.tracker-card--danger .tracker-card__head h2 { color: var(--danger); }

.danger-zone { margin-top: 0.6rem; }
.danger-zone > summary {
    display: inline-block;
    list-style: none;
    cursor: pointer;
}
.danger-zone > summary::-webkit-details-marker { display: none; }
.danger-zone[open] > summary { margin-bottom: 1rem; }
.danger-zone__form { display: grid; gap: 1rem; max-width: 28rem; }

/* ----- Inline link button (table edit) ------------------------------ */
.btn-link {
    background: none;
    border: 0;
    color: var(--text-dim);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    padding: 0.25rem 0.5rem;
    border-radius: var(--radius-sm);
    text-decoration: none;
    transition: background .15s ease, color .15s ease;
}
.btn-link:hover { color: var(--primary-hot); background: var(--primary-soft); }
.btn-link:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--primary-ring);
}

/* Sit Edit + Delete side-by-side in the actions cell with a small gap. */
.history-table .actions a + form,
.history-table .actions form + a { margin-left: 0.25rem; }

/* Date cells span multiple rows when a day has many entries — anchor them
   to the top so they line up with the first meal row instead of centering. */
.history-table .cell-date { vertical-align: top; }

/* Add a top border on each new date group to visually divide days. */
.history-table tbody tr.row-group-start td {
    border-top: 1px solid var(--border);
}
.history-table tbody tr.row-group-start:first-child td { border-top: 0; }


/* ----- Section toolbar (range picker scoped to a whole section) ----- */
/* Sits above both the chart and history cards as a "this scopes
   everything below" header. The bottom divider visually ties it to
   the cards beneath it so the picker reads as a section-level
   control rather than a per-card filter. */
.section-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.75rem 1rem;
    padding: 0 0 1rem;
    margin: 0 0 1.5rem;
    border-bottom: 1px solid var(--border-soft);
}
.section-toolbar__heading {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
}
.section-toolbar__title {
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--text);
}
.section-toolbar__hint {
    color: var(--text-dim);
    font-size: 0.82rem;
}

/* Range picker = label + the existing .unit-toggle pill group. We
   reuse .unit-toggle so we don't ship a parallel look. */
.range-picker-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
    margin: 0;
}
.range-picker-row__label {
    color: var(--text-dim);
    font-size: 0.85rem;
    font-weight: 600;
}

/* Active-pill treatment scoped to the range picker only — other
   unit toggles (lbs/kg, US/metric, chart units) keep their default
   state. Brighter gradient + subtle inset highlight + soft outer
   halo so the selected window reads at a glance without shouting. */
.range-picker-row .unit-toggle button.is-active {
    background: linear-gradient(135deg, var(--primary), var(--primary-hot));
    color: #fff;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.25),
        inset 0 -1px 1px rgba(0, 0, 0, 0.18),
        0 1px 4px rgba(255, 122, 26, 0.32);
}

/* Inline text link (e.g. "try All time" in the empty-range state). */
.link-inline {
    color: var(--primary);
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 0.18em;
}
.link-inline:hover { color: var(--primary-hot); }
.link-inline:focus-visible {
    outline: 2px solid var(--primary-hot);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}


/* ----- Tracker history — collapsible day rows ----------------------- */
/* Day-row = always-visible summary; per-entry rows underneath are
   hidden by default once JS injects the chevron toggle. The entry
   row class varies per tracker (.meal-row on /calorie, .lift-row on
   /strength) but the look is identical, so we group the selectors. */
.history-table--days tbody tr.day-row td {
    border-top: 1px solid var(--border);
}
.history-table--days tbody tr.day-row:first-child td {
    border-top: 0;
}
.history-table--days .meal-row,
.history-table--days .lift-row {
    background: rgba(255, 255, 255, 0.012);
}
.history-table--days .meal-row td,
.history-table--days .lift-row td {
    border-bottom-color: var(--border-soft);
}
.history-table--days .cell-meal-count {
    color: var(--text-dim);
    font-size: 0.88rem;
}
.history-table--days .cell-date--day {
    /* Date cell hosts the toggle button; padding is set on the button
       so the click target fills the cell without doubling up. */
    padding: 0;
}
.history-table--days .cell-date--day .day-toggle {
    padding: 0.75rem 0.85rem;
}

.day-toggle {
    background: none;
    border: 0;
    color: inherit;
    font: inherit;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    text-align: left;
    width: 100%;
    border-radius: var(--radius-sm);
    transition: color .15s ease;
}
.day-toggle:hover { color: var(--primary); }
.day-toggle:focus-visible {
    outline: 2px solid var(--primary-hot);
    outline-offset: 2px;
}
.day-toggle__chevron {
    width: 0.55rem;
    height: 0.55rem;
    border-right: 2px solid var(--text-dim);
    border-bottom: 2px solid var(--text-dim);
    transform: rotate(-45deg);
    transition: transform .2s ease;
    flex-shrink: 0;
    margin-top: -2px;
}
.day-toggle[aria-expanded="true"] .day-toggle__chevron {
    transform: rotate(45deg);
}
.day-toggle:hover .day-toggle__chevron {
    border-color: var(--primary);
}

/* ----- Calorie tracker — Today's meals list ------------------------- */
.meals-list {
    list-style: none;
    margin: 0 0 1.5rem;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
}
.meals-list__item {
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: center;
    gap: 1rem;
    padding: 0.75rem 1rem;
    border-bottom: 1px solid var(--border-soft);
    background: var(--bg-card-2);
}
.meals-list__item:last-of-type { border-bottom: 0; }
.meals-list__label {
    font-weight: 600;
    color: var(--text);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.meals-list__value {
    color: var(--text-dim);
    font-variant-numeric: tabular-nums;
}
.meals-list__actions {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}
.meals-list__actions form { margin: 0; }

/* Total row at the bottom of the list — visually distinct, not interactive. */
.meals-list__total {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.85rem 1rem;
    background: var(--primary-soft);
    color: var(--text);
    font-size: 0.95rem;
    border-top: 1px solid var(--border-hot);
}
.meals-list__total strong {
    color: var(--primary-hot);
    font-size: 1.05rem;
    font-variant-numeric: tabular-nums;
}

/* "Add a meal" divider above the form. */
.meals-add-divider {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    color: var(--text-faint);
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
    margin: 0 0 1rem;
}
.meals-add-divider::before,
.meals-add-divider::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--border);
}

/* Optional field marker (small, muted suffix on the label). */
.field-optional {
    color: var(--text-faint);
    font-weight: 400;
    font-size: 0.85em;
}

/* ----- Weight tracker — small inline label decorations -------------- */
.weight-unit-label,
.weight-unit-suffix {
    color: var(--text-faint);
    font-weight: 500;
    font-size: 0.85em;
}
.weight-unit-suffix { margin-left: 0.15em; }

.field-hint-inline {
    color: var(--text-faint);
    font-weight: 400;
    font-size: 0.85em;
}

/* Notes column: keep long notes from blowing up the row height. */
.notes-cell {
    color: var(--text-dim);
    max-width: 22rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ----- Inline cal-preview variant (used inside the targets card) ----- */
/* Strips the nested-card look so the 3-stat row sits flat on the
   tracker-card surface above the collapsible stats form. */
.cal-preview--inline {
    margin: 0 0 0.5rem;
    padding: 0;
    background: transparent;
    border: 0;
}

/* ----- Targets-form helpers ----------------------------------------- */
/* Horizontal rule with centered text, drawn above the stats form
   when the user already has saved targets so it's clear this section
   updates them. */
.targets-form-divider {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin: 1.5rem 0 1rem;
    color: var(--text-faint);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-weight: 700;
}
.targets-form-divider::before,
.targets-form-divider::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--border);
}

/* "Units" label + tab toggle, sitting above the form. */
.unit-toggle-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin: 0 0 1rem;
    flex-wrap: wrap;
}
.unit-toggle-row .field-label { margin: 0; }

/* ----- Inline status colors (history table + intake hint) ------------ */
.text-good { color: var(--success); font-weight: 600; }
.text-warn { color: var(--warning); font-weight: 600; }

/* ----- Visually-hidden utility (a11y) -------------------------------
   Removes content from the visual flow but keeps it readable to screen
   readers. Used for chart data tables and other text alternatives that
   shouldn't be visible but should still be announced. */
.visually-hidden {
    position: absolute !important;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ----- Confirmation dialog (sitewide) ------------------------------- */
/* Replaces inline `onsubmit="return confirm(...)"` calls. Opens on
   any form submission tagged with data-confirm="…" via the IIFE in
   main.js. <dialog> styling: card-shaped, dark backdrop, no marker. */
.confirm-dialog {
    background: var(--bg-card);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 1.6rem 1.75rem;
    max-width: 28rem;
    width: calc(100% - 2rem);
    box-shadow: var(--shadow), 0 24px 60px rgba(0, 0, 0, 0.55);
}
.confirm-dialog::backdrop {
    background: rgba(7, 11, 24, 0.62);
    backdrop-filter: blur(2px);
}
.confirm-dialog__title   { margin: 0 0 0.55rem; font-size: 1.15rem; }
.confirm-dialog__message { margin: 0 0 1.25rem; color: var(--text-dim); line-height: 1.55; }
.confirm-dialog__actions {
    display: flex;
    gap: 0.6rem;
    justify-content: flex-end;
    flex-wrap: wrap;
}

@keyframes confirmDialogIn {
    from { opacity: 0; transform: translateY(-6px) scale(0.98); }
    to   { opacity: 1; transform: translateY(0)    scale(1); }
}
.confirm-dialog[open] { animation: confirmDialogIn .18s ease-out; }
@media (prefers-reduced-motion: reduce) {
    .confirm-dialog[open] { animation: none; }
}

/* ----- Goal picker (Cutting / Maintaining / Bulking pills) ----------- */
/* Each <button> is a real submit — clicking POSTs to /calorie/goal,
   redirects, and the page comes back with the chosen pill highlighted. */
.goal-picker {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    margin: 1.25rem 0 0;
    flex-wrap: wrap;
}
.goal-picker__label { margin: 0; }
.goal-picker__pills {
    display: inline-flex;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 3px;
}
.goal-pill {
    background: transparent;
    border: 0;
    color: var(--text-dim);
    font: inherit;
    font-size: 0.82rem;
    font-weight: 600;
    /* Lock line-height: <button> defaults to ~1.2 but <label> inherits
       the body's 1.55, which makes the radio-pill variant render taller
       than the surrounding container expects. */
    line-height: 1.2;
    padding: 0.4rem 0.95rem;
    border-radius: 999px;
    cursor: pointer;
    transition: background-color .15s ease, color .15s ease;
}
.goal-pill:hover { color: var(--text); }
.goal-pill.is-active {
    background: linear-gradient(135deg, var(--primary-deep), var(--primary));
    color: #fff;
}
.goal-pill:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--primary-ring);
}

/* Radio-input variant: same look as the goal-pill submit buttons,
   but driven by a hidden <input type="radio"> inside each <label>.
   `:has(input:checked)` handles the active state without JS. */
.goal-pill--radio {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.goal-pill--radio input {
    position: absolute;
    opacity: 0;
    inset: 0;
    cursor: pointer;
}
.goal-pill--radio:has(input:checked) {
    background: linear-gradient(135deg, var(--primary-deep), var(--primary));
    color: #fff;
}
.goal-pill--radio:has(input:focus-visible) {
    box-shadow: 0 0 0 3px var(--primary-ring);
}
/* Mark the whole pill row as invalid when validation fails. */
.goal-picker__pills.is-invalid {
    box-shadow: 0 0 0 1px var(--danger);
}

/* Radio-variant pill row: drop the 3px container padding so the active
   pill's gradient fills the rounded track edge-to-edge. The button
   variant (calorie goal picker) keeps the 3px so each button stays
   visually distinct as a tap target. */
.goal-picker__pills:has(.goal-pill--radio) { padding: 0; }
.goal-picker__pills:has(.goal-pill--radio) .goal-pill--radio {
    padding: 0.55rem 1.1rem;   /* slightly more padding to fill the row */
}

/* Strength tracker: small "× reps" suffix in the history table. */
.reps-suffix {
    color: var(--text-faint);
    font-weight: 500;
    font-size: 0.9em;
    margin-left: 0.35em;
}

/* ===================================================================
   Phase 7 — Dashboard
   =================================================================== */

/* "Today at a glance" — three clickable summary cards in a row.
   auto-fit lets the row collapse 3→2→1 naturally as space tightens
   instead of jumping straight from 3 to 1 at a fixed breakpoint. */
.dash-summary {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 1.25rem;
}

/* ----- Legal-page prose (Privacy, Terms) ---------------------------- */
/* Comfortable reading width with structured h2 spacing. Kept its own
   class instead of touching global tag styles so the home/about pages
   don't pick up the wider rhythm. */
.legal-prose {
    max-width: 70ch;
    color: var(--text);
}
.legal-prose h2 {
    margin: 2.25rem 0 0.6rem;
    font-size: 1.25rem;
}
.legal-prose h2:first-child { margin-top: 0; }
.legal-prose p,
.legal-prose ul {
    color: var(--text-dim);
    line-height: 1.65;
    margin: 0 0 0.9rem;
}
.legal-prose ul {
    padding-left: 1.25rem;
    list-style: disc;
}
.legal-prose li { margin-bottom: 0.3rem; }
.legal-prose a   { color: var(--primary-hot); }
.legal-prose code {
    background: var(--bg-card-2);
    padding: 0.05rem 0.35rem;
    border-radius: var(--radius-sm);
    font-size: 0.9em;
}

/* ----- First-run welcome card --------------------------------------- */
/* Rendered above the snapshot grid only when the user has zero logs
   across all four trackers. Disappears the moment they log anything. */
.welcome-card {
    background: linear-gradient(135deg,
        rgba(255, 122, 50, 0.10),
        rgba(255, 148, 71, 0.04) 60%,
        var(--bg-card) 100%);
    border: 1px solid rgba(255, 148, 71, 0.30);
    border-radius: var(--radius-lg);
    padding: 1.75rem 2rem;
    display: grid;
    gap: 1.25rem;
}
.welcome-card__head h2 {
    margin: 0.35rem 0 0.5rem;
    font-size: clamp(1.25rem, 2.2vw, 1.6rem);
    line-height: 1.3;
}
.welcome-card__head p {
    margin: 0;
    color: var(--text-dim);
    max-width: 56ch;
}
.welcome-card__actions {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 0.75rem;
}
.welcome-action {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    padding: 0.9rem 1rem;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: inherit;
    text-decoration: none;
    transition: transform .15s ease, border-color .15s ease, background .15s ease;
}
.welcome-action:hover {
    transform: translateY(-2px);
    border-color: rgba(255, 148, 71, 0.45);
    background: var(--bg-card-2);
}
.welcome-action:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--primary-ring);
}
.welcome-action__title { font-weight: 700; }
.welcome-action__hint  { color: var(--text-dim); font-size: 0.82rem; }

.dash-card {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 1.5rem;
    color: inherit;
    text-decoration: none;
    transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease;
}
.dash-card:hover {
    transform: translateY(-3px);
    border-color: var(--border-hot);
    box-shadow: var(--shadow);
}
.dash-card:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--primary-ring);
}

.dash-card__eyebrow {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-dim);   /* bumped from text-faint for AA contrast at this size */
    font-weight: 700;
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
}
.dash-card__eyebrow-icon {
    width: 22px;
    height: 22px;
    color: var(--primary-hot);
    flex-shrink: 0;
    /* Heart's optical center sits a hair below the cap-height of the
       text next to it. Nudge up 1px so the icon and text read as
       sitting on the same line. */
    margin-top: -1px;
}
.dash-card__value {
    font-size: 2rem;
    font-weight: 800;
    color: var(--text);
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}
.dash-card__value--placeholder { color: var(--text-faint); }
/* Compact mark that replaces the bare em-dash on summary cards
   without data yet. Matches the empty-state icon vocabulary at a
   size that fits inside the value cell without crowding the hint. */
.dash-card__empty-icon {
    display: block;
    width: 32px;
    height: 32px;
    padding: 0.4rem;
    color: var(--primary-hot);
    background: radial-gradient(circle at center,
        rgba(255, 122, 26, 0.14) 0%,
        rgba(255, 122, 26, 0.04) 60%,
        transparent 75%);
    border-radius: 999px;
    box-sizing: content-box;
}
.dash-card__unit {
    font-size: 0.95rem;
    font-weight: 500;
    color: var(--text-dim);
    margin-left: 0.25rem;
}
.dash-card__hint {
    font-size: 0.85rem;
    color: var(--text-dim);
    line-height: 1.4;
}

/* Compact inline stat row — used on the Calories and Weight cards
   to surface a couple of supporting numbers ("3 meals today",
   "Lifetime ↓ 6 lbs over 75d") and stop the cards from looking
   empty when their primary content is short. Children separated by
   a subtle middot via flex gap; wraps on narrow widths. */
.dash-card__stats {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 1rem;
    font-size: 0.85rem;
    color: var(--text-dim);
    line-height: 1.4;
}
.dash-card__stats strong {
    color: var(--text);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* Strength card uses a 3-row list instead of a single value. */
.dash-card__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}
.dash-card__list li {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
}
.dash-card__list-label {
    color: var(--text-dim);
    font-size: 0.85rem;
}
.dash-card__list-value {
    color: var(--text);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* Trend charts row — four smaller charts under the snapshot grid.
   Explicit 2x2 layout so the cards stay even regardless of viewport
   width. Drops to single column on phones. */
.dash-charts {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.25rem;
    margin-top: 1.5rem;
}
@media (max-width: 600px) {
    .dash-charts { grid-template-columns: 1fr; }
}
.dash-charts .tracker-card {
    margin-bottom: 0;
    transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease;
}
/* Visual-only lift on hover. The card itself isn't a link (so Chart.js
   interactions stay alive) — the "Open tracker →" link in the header
   handles navigation. */
.dash-charts .tracker-card:hover {
    transform: translateY(-3px);
    border-color: var(--border-hot);
    box-shadow: var(--shadow);
}
@media (prefers-reduced-motion: reduce) {
    .dash-charts .tracker-card { transition: none; }
    .dash-charts .tracker-card:hover { transform: none; }
}
.dash-charts .tracker-card__head { margin-bottom: 1rem; }
.dash-charts .tracker-card__head h3 {
    margin: 0;
    font-size: 1rem;
}

/* Compact chart container — used on the dashboard so the trend
   row doesn't dominate the page like the full-size tracker chart. */
.chart-wrap--compact { height: 220px; }
@media (max-width: 520px) {
    .chart-wrap--compact { height: 200px; }
}

/* Empty-state shown inside a chart card when that tracker has no
   data yet. Matches the chart's height so the row stays aligned. */
.dash-chart-empty {
    height: 220px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.85rem;
    text-align: center;
    color: var(--text-dim);
    background: var(--bg-card-2);
    border: 1px dashed var(--border);
    border-radius: var(--radius);
    padding: 1.25rem;
}
.dash-chart-empty p { margin: 0; font-size: 0.95rem; }
/* Compact icon variant for the dashboard chart empties. Smaller box,
   same warm halo as the full empty-state icon so the visual language
   carries across both surfaces. */
.dash-chart-empty__icon {
    display: block;
    width: 38px;
    height: 38px;
    padding: 0.55rem;
    color: var(--primary-hot);
    background: radial-gradient(circle at center,
        rgba(255, 122, 26, 0.14) 0%,
        rgba(255, 122, 26, 0.04) 60%,
        transparent 75%);
    border-radius: 999px;
    box-sizing: content-box;
}
@media (max-width: 520px) {
    .dash-chart-empty { height: 200px; }
}

/* Cards-as-links — used by both the dashboard quick-action tiles
   and the home feature cards once they became clickable. Strips
   default link styling so the .feature-card surface looks the same
   whether it's an <article> or an <a>. */
.dash-actions { margin-top: 1.5rem; }
a.feature-card {
    color: inherit;
    text-decoration: none;
    display: block;
}
a.feature-card:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--primary-ring);
}

/* ----- Phase 7 responsive ------------------------------------------- */
@media (max-width: 720px) {
    .tracker-card { padding: 1.25rem; }
    .tracker-card__head { flex-direction: column; align-items: stretch; }
    .form-grid { grid-template-columns: 1fr; }
}


/* ===================================================================
   Dashboard polish — stat strip, progress ring, PR badge
   ===================================================================
   Sits between the greeting hero and the "Today at a glance" summary.
   Goal: break the three-stacks-of-three rhythm and surface momentum
   numbers (streak / weekly counts / weight delta) right where the eye
   lands first.
*/

.dash-stat-strip-wrap {
    padding: 1.75rem 0 0;
}
.dash-stat-strip {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 0.85rem;
}
.dash-stat {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    padding: 0.85rem 1rem;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease;
}
.dash-stat:hover {
    transform: translateY(-2px);
    border-color: var(--border-hot);
    box-shadow: var(--shadow);
}
@media (prefers-reduced-motion: reduce) {
    .dash-stat { transition: none; }
    .dash-stat:hover { transform: none; }
}
.dash-stat__icon {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 38px;
    height: 38px;
    border-radius: 10px;
    background: var(--primary-soft);
    color: var(--primary-hot);
}
.dash-stat__icon svg { width: 20px; height: 20px; }
.dash-stat__body {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    min-width: 0;
}
/* Label sits ABOVE the value (small uppercase eyebrow) and the value
   reads as the headline number. Markup order in the view puts label
   first so the visual stack matches without needing flex-direction
   reverse. Uses text-dim (not text-faint) so the very small 0.7rem
   label stays comfortably above the 4.5:1 AA threshold on bg-card. */
.dash-stat__label {
    font-size: 0.7rem;
    color: var(--text-dim);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 700;
    line-height: 1.1;
}
.dash-stat__value {
    font-size: 1.2rem;
    font-weight: 700;
    color: var(--text);
    font-variant-numeric: tabular-nums;
    line-height: 1.15;
}
.dash-stat__unit {
    font-size: 0.78rem;
    font-weight: 500;
    color: var(--text-dim);
    margin-left: 0.15rem;
}
.dash-stat__placeholder {
    color: var(--text-faint);
    font-weight: 500;
}

/* Streak pill gets a warmer accent the moment it's non-zero — the
   flame icon glows orange and the card border picks up a soft tint
   so it reads as the "achievement" pill in the row. */
.dash-stat--hot .dash-stat__icon {
    background: linear-gradient(135deg,
        rgba(255, 122, 26, 0.22),
        rgba(255, 148, 71, 0.10));
    color: var(--primary);
}
/* No border-color tint here — that conflicts with .dash-stat:hover
   (same --border-hot) and made the active streak pill look like it
   was being hovered at all times. The icon's warm gradient is enough
   to signal "streak is alive." */

/* ----- Calorie progress ring (dashboard summary card) --------------
   Replaces the bare "1840 / 2400" text with a small SVG donut + the
   numbers stacked beside it. Drawn rotated -90deg so the arc starts
   at 12 o'clock; the fill stroke is painted by the inline
   stroke-dashoffset attribute, which the PHP computes from today's %.
*/
.dash-ring-row {
    display: flex;
    align-items: center;
    gap: 0.85rem;
}
.dash-ring {
    position: relative;
    width: 68px;
    height: 68px;
    flex-shrink: 0;
}
.dash-ring__svg {
    width: 100%;
    height: 100%;
    transform: rotate(-90deg);
    overflow: visible;
}
.dash-ring__track {
    stroke: rgba(255, 255, 255, 0.08);
}
.dash-ring__fill {
    stroke: url(#dash-ring-grad);
    /* Fallback for browsers that don't resolve the gradient ref
       (it's defined inline in main.js if/when needed). Solid orange
       reads fine on its own. */
    stroke: var(--primary);
    transition: stroke-dashoffset .6s cubic-bezier(0.4, 0, 0.2, 1);
}
.dash-ring__pct {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    font-size: 0.8rem;
    font-weight: 700;
    color: var(--text);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.02em;
}
/* When the ring layout is in play, the side-stacked value reads
   smaller than the bare-number variant (1.5rem vs 2rem). The
   stacked unit chip stays the same as elsewhere on the card. */
.dash-card__value--ring {
    font-size: 1.5rem;
    line-height: 1.15;
}
@media (prefers-reduced-motion: reduce) {
    .dash-ring__fill { transition: none; }
}

/* ----- PR badge (strength summary card) ----------------------------
   Tiny orange pill that appears next to a lift on the strength
   summary card when that lift's latest entry is a new estimated
   1RM (best-ever). Sized to read as inline meta, not as a primary
   element — the lift line itself is still the main value. */
.dash-pr-badge {
    display: inline-block;
    margin-right: 0.35rem;
    padding: 0.05rem 0.4rem;
    font-size: 0.65rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    color: #fff;
    background: linear-gradient(135deg, var(--primary-deep), var(--primary));
    border-radius: 999px;
    vertical-align: 1px;
    line-height: 1.3;
    text-transform: uppercase;
    box-shadow: 0 1px 6px rgba(255, 122, 26, 0.35);
}

/* ----- Quick action strip (replaces the tall 3-up card grid) -------
   Slim horizontal row of three "log X" entries — icon + title +
   caption + chevron. Breaks the dashboard's repeating "three cards
   in a row" rhythm because the strip is much shorter (~72px tall
   vs ~250px for the old PNG-logo feature cards). */
.section--tight {
    padding: 2.5rem 0;
}
@media (max-width: 860px) {
    .section--tight { padding: 2rem 0; }
}
/* Variant of .section-head where the heading + lede sit left-aligned
   above the action row instead of centered with 2.5rem of breathing
   room. Reads as a smaller cap on top of an action list. */
.section-head--inline {
    text-align: left;
    margin: 0 0 1.25rem;
    max-width: none;
}
.section-head--inline h2 {
    font-size: 1.25rem;
    margin-bottom: 0.2rem;
}
.section-head--inline p {
    font-size: 0.9rem;
    margin: 0;
}

.action-strip {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 0.75rem;
}
.action-strip__item {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    padding: 0.85rem 1rem;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: inherit;
    text-decoration: none;
    transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease;
}
.action-strip__item:hover {
    transform: translateY(-2px);
    border-color: var(--border-hot);
    box-shadow: var(--shadow);
    color: inherit;
}
.action-strip__item:hover .action-strip__chev { color: var(--primary-hot); transform: translateX(3px); }
.action-strip__item:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--primary-ring);
}
.action-strip__icon {
    width: 44px;
    height: 44px;
    object-fit: contain;
    flex-shrink: 0;
}
.action-strip__body {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    flex: 1 1 auto;
    min-width: 0;
}
.action-strip__title {
    font-size: 0.95rem;
    font-weight: 700;
    color: var(--text);
    line-height: 1.2;
}
.action-strip__caption {
    font-size: 0.8rem;
    color: var(--text-dim);
}
.action-strip__chev {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 24px;
    height: 24px;
    color: var(--text-faint);
    transition: transform .18s ease, color .18s ease;
}
.action-strip__chev svg { width: 18px; height: 18px; }
@media (prefers-reduced-motion: reduce) {
    .action-strip__item,
    .action-strip__chev { transition: none; }
    .action-strip__item:hover { transform: none; }
    .action-strip__item:hover .action-strip__chev { transform: none; }
}

/* ----- Goal-progress bars (dashboard cards) ------------------------
   Used on:
     · Weight summary card  — full-width bar with "X lb to target" meta
       above and a percent chip on the right.
     · Strength summary card — compact variant inside each lift's row,
       paired with a small "120 / 150 kg · 80%" caption below.
   The track is a soft dark fill; the bar uses the orange brand
   gradient and animates from 0 → its final width on mount so the
   numbers feel earned. Gated by reduced-motion. */
.goal-bar {
    margin-top: 0.75rem;
}
.goal-bar__meta {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.5rem;
    margin-bottom: 0.3rem;
    font-size: 0.78rem;
}
.goal-bar__label {
    color: var(--text-dim);
    line-height: 1.2;
}
.goal-bar__pct {
    color: var(--text);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.goal-bar__pct--inline {
    color: var(--text-dim);
    font-weight: 600;
    margin-left: 0.1rem;
}
.goal-bar__track {
    position: relative;
    height: 6px;
    background: rgba(255, 255, 255, 0.07);
    border-radius: 999px;
    overflow: hidden;
}
.goal-bar__fill {
    height: 100%;
    background: linear-gradient(90deg, var(--primary-deep), var(--primary), var(--primary-hot));
    border-radius: 999px;
    box-shadow: 0 0 8px rgba(255, 122, 26, 0.45);
    animation: goalBarRise .9s cubic-bezier(0.4, 0, 0.2, 1) both;
    transform-origin: left center;
}
@keyframes goalBarRise {
    from { transform: scaleX(0); }
    to   { transform: scaleX(1); }
}
@media (prefers-reduced-motion: reduce) {
    .goal-bar__fill { animation: none; }
}

/* Compact variant (strength card per-lift row). Tighter spacing,
   thinner track, sub-caption below replaces the meta row above. */
.goal-bar--compact {
    margin-top: 0.25rem;
    margin-bottom: 0.25rem;
}
.goal-bar--compact .goal-bar__track {
    height: 4px;
}
.goal-bar__sub {
    display: block;
    margin-top: 0.2rem;
    font-size: 0.72rem;
    color: var(--text-dim);
    font-variant-numeric: tabular-nums;
}

/* When a strength row has a goal, the row body splits into the
   regular label/value line plus the compact bar stacked below it.
   The flex/baseline alignment used by the goal-less variant doesn't
   apply here — the `.dash-card__list-row` carries the original look. */
.dash-card__list li.has-goal {
    display: block;
}
.dash-card__list-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
}

/* ----- Macro bars (dashboard calorie card) -------------------------
   Three stacked rows shown under the calorie ring + hint when the
   user has logged at least one meal with macros today. Auto-target
   for each macro is computed in PHP from the active calorie goal
   using standard 40/30/30 (cut) / 30/40/30 (maintain) / 25/45/30
   (bulk) splits. Each macro gets a tinted bar color so a quick glance
   tells them apart. */
.macro-bars {
    margin-top: 0.75rem;
    display: grid;
    gap: 0.5rem;
}
.macro-bar__meta {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    font-size: 0.75rem;
    color: var(--text-dim);
    margin-bottom: 0.2rem;
}
.macro-bar__letter {
    display: inline-block;
    width: 16px;
    text-align: center;
    font-weight: 800;
    color: var(--text);
    letter-spacing: 0;
}
.macro-bar__nums {
    font-variant-numeric: tabular-nums;
}
.macro-bar__nums strong {
    color: var(--text);
    font-weight: 700;
}
.macro-bar__track {
    height: 4px;
    background: rgba(255, 255, 255, 0.06);
    border-radius: 999px;
    overflow: hidden;
}
.macro-bar__fill {
    height: 100%;
    border-radius: 999px;
    animation: goalBarRise .9s cubic-bezier(0.4, 0, 0.2, 1) both;
    transform-origin: left center;
}
/* Per-macro tint. Protein gets the brand orange; carbs go warm-yellow;
   fat gets a softer pink-orange. Reads as three related-but-distinct
   accent colors against the dark card. */
.macro-bar--protein .macro-bar__fill {
    background: linear-gradient(90deg, var(--primary-deep), var(--primary));
}
.macro-bar--carbs .macro-bar__fill {
    background: linear-gradient(90deg, #d97706, #fbbf24);
}
.macro-bar--fat .macro-bar__fill {
    background: linear-gradient(90deg, #b45309, #f97316);
}
@media (prefers-reduced-motion: reduce) {
    .macro-bar__fill { animation: none; }
}

/* ----- Macros row on the add-meal / edit-meal forms ----------------
   Sits below the main Date/Label/Calories row. The title is a small
   eyebrow; the three inputs are a 3-col grid that collapses to 1 col
   on phones. The field--macro variant tightens the input height a hair
   so the row reads as a sub-section, not a parallel primary row. */
.macros-row {
    margin-top: 1.25rem;
    padding-top: 1rem;
    border-top: 1px dashed var(--border-soft);
}
.macros-row__title {
    display: block;
    font-size: 0.85rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    color: var(--text-dim);
    text-transform: uppercase;
    margin-bottom: 0.6rem;
}
.macros-row__title .field-optional {
    text-transform: none;
    letter-spacing: 0;
    font-weight: 500;
    color: var(--text-faint);
}
.macros-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem;
}
@media (max-width: 640px) {
    .macros-grid { grid-template-columns: 1fr; }
}

/* Inline macro chips under each meal in "Today's meals" — three tiny
   pills showing P / C / F grams. Only rendered if the meal has at
   least one macro entered. Kept under the label so the calorie number
   on the right stays uncluttered. */
.meals-list__macros {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    margin-left: 0.5rem;
    font-size: 0.72rem;
    color: var(--text-dim);
    font-variant-numeric: tabular-nums;
}
.meals-list__macros span {
    display: inline-block;
    padding: 0.1rem 0.45rem;
    background: rgba(255, 255, 255, 0.04);
    border-radius: 999px;
    line-height: 1.35;
}


/* ===================================================================
   Dashboard — asymmetric grid (post-cardio redesign)
   ===================================================================
   3 cols at desktop, with this-week spanning 2 cols and a tall (or
   regular) strength card on the right paired with a smaller cardio
   card below it. Falls back gracefully on narrower viewports.
*/
.dash-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-auto-rows: minmax(0, auto);
    gap: 1.25rem;
}

/* Place items: row 1 is Calories | Weight | Strength,
   row 2 is "This week" (spans cols 1+2) | Cardio,
   row 3 is Goals & progress (spans all 3 cols). */
.dash-grid__calories { grid-column: 1; grid-row: 1; }
.dash-grid__weight   { grid-column: 2; grid-row: 1; }
.dash-grid__strength { grid-column: 3; grid-row: 1; }
.dash-grid__thisweek { grid-column: 1 / span 2; grid-row: 2; }
.dash-grid__cardio   { grid-column: 3; grid-row: 2; }
.dash-grid__goals    { grid-column: 1 / -1; grid-row: 3; }

/* Two cols at tablet: Calories | Weight, then Strength | Cardio,
   then "This week" full-width, then Goals full-width below. */
@media (max-width: 1099px) {
    .dash-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .dash-grid__calories { grid-column: 1; grid-row: 1; }
    .dash-grid__weight   { grid-column: 2; grid-row: 1; }
    .dash-grid__strength { grid-column: 1; grid-row: 2; }
    .dash-grid__cardio   { grid-column: 2; grid-row: 2; }
    .dash-grid__thisweek { grid-column: 1 / -1; grid-row: 3; }
    .dash-grid__goals    { grid-column: 1 / -1; grid-row: 4; }
}

/* Single column on phones — natural priority order. */
@media (max-width: 640px) {
    .dash-grid { grid-template-columns: 1fr; }
    .dash-grid__calories { grid-column: 1; grid-row: auto; }
    .dash-grid__weight   { grid-column: 1; grid-row: auto; }
    .dash-grid__strength { grid-column: 1; grid-row: auto; }
    .dash-grid__cardio   { grid-column: 1; grid-row: auto; }
    .dash-grid__thisweek { grid-column: 1; grid-row: auto; }
    .dash-grid__goals    { grid-column: 1; grid-row: auto; }
}


/* ----- Card head row (eyebrow + optional "view all" link) ----------- */
.dash-card__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}
.dash-card__view-all {
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text-dim);
    text-decoration: none;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    transition: color .15s ease;
}
.dash-card__view-all:hover { color: var(--primary-hot); }

/* "Last session" caption variant used in the cardio card. */
.dash-card__hint--last {
    margin-top: auto;
    padding-top: 0.75rem;
    border-top: 1px dashed var(--border-soft);
}
.dash-card__hint-label {
    display: block;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-dim);   /* small uppercase eyebrow — needs AA contrast */
    margin-bottom: 0.15rem;
    font-weight: 700;
}
.dash-card__inline-link {
    color: var(--primary-hot);
    text-decoration: underline;
    text-underline-offset: 2px;
}


/* ----- Strength card — inset toggle + lift list -------------------- */
.strength-toggle {
    display: inline-flex;
    align-self: flex-start;
    padding: 3px;
    background: var(--bg-input);
    border: 1px solid var(--border);
    border-radius: 999px;
    margin-bottom: 0.85rem;
}
.strength-toggle button {
    background: transparent;
    border: none;
    color: var(--text-dim);
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    padding: 0.3rem 0.85rem;
    border-radius: 999px;
    cursor: pointer;
    transition: background-color .15s ease, color .15s ease;
}
.strength-toggle button.is-active {
    background: linear-gradient(135deg, var(--primary-deep), var(--primary));
    color: #fff;
}
.strength-toggle button:focus-visible {
    outline: 2px solid var(--primary-hot);
    outline-offset: 2px;
}

.strength-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}
.strength-list__row { display: flex; flex-direction: column; gap: 0.35rem; }
.strength-list__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
}
.strength-list__lift {
    color: var(--text-dim);
    font-size: 0.9rem;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}
.strength-list__value {
    color: var(--text);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.strength-list__num { font-size: 1.05rem; }
.strength-list__unit {
    color: var(--text-dim);
    font-weight: 500;
    font-size: 0.85rem;
    margin-left: 0.15rem;
}


/* ----- Weight sparkline (dashboard weight card) --------------------
   Inline SVG line + end-dot that fills the empty space below the
   weight value. Stroke color is driven by the recent direction so
   the trend reads at a glance: green when weight is dropping, warn
   when rising, dim when flat. Uses currentColor on the polyline +
   dot so the color cascades from the parent. */
.weight-spark {
    margin: 0.65rem 0 0.4rem;
    height: 52px;
    color: var(--text-dim);
}
.weight-spark--down  { color: #34d399; }
.weight-spark--up    { color: #fb923c; }
.weight-spark--flat  { color: var(--text-faint); }
.weight-spark__svg {
    display: block;
    width: 100%;
    height: 100%;
    overflow: visible;
}


/* ----- Cardio card progress bar ------------------------------------
   Single continuous bar — the value line above ("3 / 4 sessions")
   carries the "of target" info, so the bar itself is a clean
   horizontal fill, sized prominently so it reads as the card's
   headline metric. */
.cardio-bar {
    height: 10px;
    background: var(--bg-card-2);
    border-radius: 999px;
    overflow: hidden;
    margin: 0.5rem 0 0.6rem;
}
.cardio-bar__fill {
    height: 100%;
    background: linear-gradient(90deg, var(--primary-deep), var(--primary));
    border-radius: 999px;
    transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
@media (prefers-reduced-motion: reduce) {
    .cardio-bar__fill { transition: none; }
}

/* ----- Cardio card stat rows ---------------------------------------
   Time / Distance / Progress stacked vertically below the progress
   bar. Label-left, value-right with a faint divider between rows
   so the column reads as a clean key/value list. */
.cardio-stats {
    margin: 0.5rem 0 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
}
.cardio-stats__row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: 0.45rem 0;
    border-bottom: 1px dashed var(--border-soft);
    font-size: 0.9rem;
}
.cardio-stats__row:last-child { border-bottom: none; }
.cardio-stats dt {
    color: var(--text-dim);
    font-weight: 500;
    margin: 0;
}
.cardio-stats dd {
    margin: 0;
    color: var(--text);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.cardio-stats__unit {
    color: var(--text-dim);
    font-weight: 500;
    font-size: 0.85rem;
    margin-left: 0.15rem;
}


/* ----- "This week" card — workouts bar + recent activity ----------- */
.weekly-bar {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    padding-bottom: 1rem;
    border-bottom: 1px dashed var(--border-soft);
    margin-bottom: 1rem;
}
.weekly-bar__meta {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.5rem;
}
.weekly-bar__label {
    color: var(--text-dim);
    font-size: 0.85rem;
}
.weekly-bar__value {
    color: var(--text);
    font-variant-numeric: tabular-nums;
    font-size: 0.95rem;
}
.weekly-bar__value strong { font-size: 1.2rem; font-weight: 800; margin-right: 0.15rem; }
.weekly-bar__value span { color: var(--text-dim); font-weight: 500; }
.weekly-bar__track {
    height: 8px;
    background: var(--bg-card-2);
    border-radius: 999px;
    overflow: hidden;
}
.weekly-bar__fill {
    height: 100%;
    background: linear-gradient(90deg, var(--primary-deep), var(--primary));
    border-radius: 999px;
    transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
.weekly-bar__hint {
    margin: 0.25rem 0 0;
    font-size: 0.78rem;
    color: var(--text-faint);
}
.weekly-bar__hint a {
    color: var(--primary-hot);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.recent-activity__title {
    margin: 0 0 0.5rem;
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-dim);   /* small uppercase heading — needs AA contrast */
    font-weight: 700;
}
.recent-activity__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
}
.recent-activity__item {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}
.recent-activity__dot {
    flex-shrink: 0;
    width: 8px;
    height: 8px;
    border-radius: 999px;
    background: var(--text-faint);
}
.recent-activity__dot--strength { background: #fb923c; }
.recent-activity__dot--cardio   { background: #60a5fa; }
.recent-activity__dot--weight   { background: #34d399; }
.recent-activity__dot--calorie  { background: #f472b6; }
/* Link wraps ONLY the summary text — not the timestamp on the right
   and not the colored dot on the left. Keeps the click target sized
   to the readable content. min-width: 0 + overflow ellipsis lets a
   long summary truncate cleanly without pushing the when off-row. */
.recent-activity__link {
    color: var(--text);
    text-decoration: none;
    font-size: 0.9rem;
    line-height: 1.35;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    transition: color .15s ease;
}
.recent-activity__link:hover { color: var(--primary-hot); }
.recent-activity__when {
    flex-shrink: 0;
    margin-left: auto;
    color: var(--text-faint);
    font-size: 0.78rem;
    font-variant-numeric: tabular-nums;
}
.recent-activity__empty {
    margin: 0;
    color: var(--text-faint);
    font-size: 0.85rem;
}


/* ----- Goals & progress strip (sitewide, dashboard) -----------------
   Standalone form: gets its own card-style wrapper (used outside the
   dashboard grid). Nested form (.goals-strip--nested): drops the
   wrapper so it sits flush inside the parent .dash-card without a
   double border. */
.goals-strip {
    list-style: none;
    margin: 0;
    padding: 0;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
}
.goals-strip--nested {
    background: transparent;
    border: none;
    border-radius: 0;
    /* Tighter side-padding inside the rows since the parent card
       already has its own padding. */
}
.goals-strip__row {
    display: grid;
    grid-template-columns: 28px minmax(170px, 1.3fr) minmax(0, 2fr) auto auto;
    align-items: center;
    gap: 1rem;
    padding: 0.85rem 1.25rem;
    border-bottom: 1px solid var(--border-soft);
    font-size: 0.9rem;
}
.goals-strip--nested .goals-strip__row {
    padding-left: 0;
    padding-right: 0;
}
.goals-strip--nested .goals-strip__row:first-child { padding-top: 0.5rem; }
.goals-strip__row:last-child { border-bottom: none; }
.goals-strip__icon {
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    border-radius: 8px;
    background: var(--primary-soft);
    color: var(--primary-hot);
    font-size: 0.9rem;
}
.goals-strip__label {
    color: var(--text);
    font-weight: 600;
}
.goals-strip__track {
    height: 8px;
    background: var(--bg-card-2);
    border-radius: 999px;
    overflow: hidden;
}
.goals-strip__fill {
    height: 100%;
    background: linear-gradient(90deg, var(--primary-deep), var(--primary));
    border-radius: 999px;
    transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
.goals-strip__pct {
    color: var(--text);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.goals-strip__suffix {
    color: var(--text-faint);
    font-size: 0.82rem;
    font-variant-numeric: tabular-nums;
    text-align: right;
    white-space: nowrap;
}
.section-head__link {
    color: var(--text-dim);
    font-size: 0.85rem;
    font-weight: 600;
    text-decoration: none;
    transition: color .15s ease;
}
.section-head__link:hover { color: var(--primary-hot); }

@media (max-width: 720px) {
    .goals-strip__row {
        grid-template-columns: 28px minmax(0, 1fr) auto;
        grid-template-areas:
            "icon label pct"
            "icon track track"
            "icon suffix suffix";
        row-gap: 0.4rem;
    }
    .goals-strip__icon  { grid-area: icon; align-self: start; }
    .goals-strip__label { grid-area: label; }
    .goals-strip__pct   { grid-area: pct; justify-self: end; }
    .goals-strip__track { grid-area: track; }
    .goals-strip__suffix{ grid-area: suffix; text-align: left; }
}


/* ===================================================================
   Phase 8 — Profile + avatar
   =================================================================== */

/* ----- Avatar component (used in nav + profile page) ---------------- */
.avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    overflow: hidden;
    background: var(--bg-card-2);
    border: 1px solid var(--border);
    flex-shrink: 0;
    object-fit: cover;
}
img.avatar { object-fit: cover; }

.avatar-sm { width: 28px; height: 28px; }
.avatar-lg { width: 96px; height: 96px; }

/* Initials fallback — warm gradient with the user's first letter on top.
   Letter sizing is a percentage of the circle so it scales with size. */
.avatar-initials {
    background: linear-gradient(135deg, var(--primary-deep), var(--primary));
    color: #fff;
    font-weight: 700;
    line-height: 1;
    border-color: var(--border-hot);
    text-transform: uppercase;
    letter-spacing: 0;
    user-select: none;
}
.avatar-sm.avatar-initials  { font-size: 0.85rem; }
.avatar-lg.avatar-initials  { font-size: 2.4rem; }

/* ----- Nav: avatar + greeting becomes a single profile link --------- */
.nav-profile {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.6rem;
    border-radius: 999px;
    color: var(--text-dim);
    font-size: 0.9rem;
    text-decoration: none;
    transition: background-color 0.15s ease, color 0.15s ease;
}
.nav-profile:hover {
    color: var(--text);
    background: rgba(255, 255, 255, 0.04);
}
.nav-profile.is-active {
    color: var(--primary);
    background: var(--primary-soft);
}
.nav-profile.is-active .avatar { border-color: var(--border-hot); }
.nav-profile:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--primary-ring);
}
/* Truncate long names in the nav so the bar doesn't wrap awkwardly. */
.nav-profile__name {
    max-width: 14ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ----- Profile page head -------------------------------------------- */
.profile-head {
    margin-bottom: 1.75rem;
}
.profile-head h1 {
    margin: 0 0 0.25rem;
    font-size: 1.85rem;
    line-height: 1.2;
}
.profile-head .lede {
    margin: 0;
    color: var(--text-dim);
    font-size: 1rem;
}

/* ----- At-a-glance summary block (read-only info) ------------------- */
.profile-summary {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 1.25rem 1.5rem;
    margin-bottom: 1.5rem;
}
.profile-summary__item {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
}
/* "Logs" line spans the full width — long, comma-separated counts. */
.profile-summary__item--wide { grid-column: 1 / -1; }

.profile-summary__label {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
    color: var(--text-faint);
}
.profile-summary__value {
    font-size: 1.05rem;
    color: var(--text);
    font-weight: 600;
}

/* ----- Goals form sub-heading --------------------------------------- */
/* Splits the goals form into two visual sections — weight/lift targets
   first, then weekly cadence below. Small uppercase eyebrow paired
   with a faint top divider so it reads as a section break, not a
   second card. */
.goals-subhead {
    margin: 1.5rem 0 0.25rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--border);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-weight: 700;
    color: var(--text-dim);
}
.goals-subhead__hint { margin: 0 0 1rem; }

/* ----- Profile photo card layout ------------------------------------ */
.profile-photo-row {
    display: flex;
    align-items: flex-start;
    gap: 1.5rem;
    flex-wrap: wrap;
}
.profile-photo-actions {
    flex: 1 1 280px;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}
.profile-upload-form .field { margin-bottom: 0.75rem; }
.profile-remove-form { margin: 0; }

/* ----- File input on dark theme ------------------------------------- */
/* Style only the browser-rendered "Choose file" button — leaves the
   filename label rendering up to the browser. */
.field input[type="file"] {
    width: 100%;
    padding: 0.55rem 0.75rem;
    background: var(--bg-input);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font: inherit;
    cursor: pointer;
}
.field input[type="file"]::file-selector-button {
    margin-right: 0.85rem;
    padding: 0.35rem 0.85rem;
    background: var(--bg-card-2);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}
.field input[type="file"]::file-selector-button:hover {
    background: var(--primary-soft);
    border-color: var(--border-hot);
    color: var(--text);
}
.field input[type="file"]:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-ring);
}

/* ----- Phase 8 responsive ------------------------------------------- */
@media (max-width: 600px) {
    .profile-photo-row { flex-direction: column; align-items: stretch; }
    .profile-summary   { grid-template-columns: 1fr; }
    .nav-profile__name { max-width: 18ch; }
}


/* ===================================================================
   Phase 9 — Error pages (404 etc.)
   =================================================================== */
.error-shell {
    flex: 1;
    display: grid;
    place-items: center;
    padding: 4rem 1.25rem;
}

.error-card {
    position: relative;
    overflow: hidden;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-lg);
    padding: 3rem 2.5rem;
    max-width: 560px;
    width: 100%;
    text-align: center;
}
/* Same warm gradient hairline as the BMI card. */
.error-card::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    padding: 1px;
    background: linear-gradient(135deg, rgba(255,122,26,.45), transparent 60%);
    -webkit-mask:
        linear-gradient(#000 0 0) content-box,
        linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
            mask-composite: exclude;
    pointer-events: none;
}

.error-code {
    font-size: clamp(5rem, 18vw, 8rem);
    font-weight: 800;
    line-height: 1;
    letter-spacing: -0.04em;
    margin: 0.75rem 0 1.25rem;
    background: linear-gradient(135deg, var(--primary-hot), var(--primary));
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    font-variant-numeric: tabular-nums;
}

.error-card h1 {
    font-size: clamp(1.5rem, 3vw, 1.85rem);
    margin-bottom: 0.75rem;
}

.error-lede {
    color: var(--text-dim);
    font-size: 1rem;
    max-width: 44ch;
    margin: 0 auto 1.75rem;
}

.error-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    justify-content: center;
}
.error-actions .btn { width: auto; padding: 0.85rem 1.4rem; }

@media (max-width: 520px) {
    .error-shell        { padding: 2.5rem 1rem; }
    .error-card         { padding: 2.25rem 1.5rem; }
    .error-actions      { flex-direction: column; align-items: stretch; }
    .error-actions .btn { width: 100%; }
}


/* ===================================================================
   Back-to-top button
   Floating bottom-right pill. Hidden until JS adds .is-visible after
   the user scrolls past the threshold (set in main.js). Designed to
   be unobtrusive — small, semi-translucent until hover.
   =================================================================== */
.back-to-top {
    position: fixed;
    right: 1.25rem;
    bottom: 1.25rem;
    width: 44px;
    height: 44px;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: var(--bg-card);
    color: var(--text);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
    z-index: 50;

    /* Hidden state: faded out + slightly lower so it slides up on
       reveal. visibility: hidden keeps it out of the tab order
       until shown. pointer-events: none stops it from blocking
       clicks on the layout below it. */
    opacity: 0;
    visibility: hidden;
    transform: translateY(8px);
    pointer-events: none;
    transition: opacity .2s ease, transform .2s ease, visibility .2s,
                background-color .15s ease, color .15s ease,
                border-color .15s ease;
}
.back-to-top svg {
    width: 20px;
    height: 20px;
}
.back-to-top.is-visible {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    pointer-events: auto;
}
.back-to-top:hover {
    background: linear-gradient(135deg, var(--primary-deep), var(--primary));
    color: #fff;
    border-color: transparent;
}
.back-to-top:focus-visible {
    outline: 2px solid var(--primary-hot);
    outline-offset: 3px;
}

/* Reduced-motion: snap visibility instead of fading. The global
   prefers-reduced-motion rule already shortens the transition; this
   removes the translate so it doesn't slide up either. */
@media (prefers-reduced-motion: reduce) {
    .back-to-top { transform: none; }
    .back-to-top.is-visible { transform: none; }
}

@media (max-width: 520px) {
    .back-to-top { right: 1rem; bottom: 1rem; width: 42px; height: 42px; }
}


/* ===================================================================
   Depth & motion polish

   Three layered effects that lift the dark theme without changing
   the design language:
     A. Sitewide film grain — a fixed, low-opacity SVG noise overlay
        that textures every page. Pure CSS via a data-URI <feTurbulence>
        so there's no extra HTTP request.
     B. Glassmorphism — translucent backgrounds + backdrop-blur on the
        two cards that sit on top of a photo (the home BMI card and
        the auth-page card). Gated behind @supports so older browsers
        keep the existing solid-card style.
     C. Scroll reveals — JS-controlled .reveal / .is-visible pair
        consumed by initRevealOnScroll() in main.js. CSS only defines
        the from/to states.
   =================================================================== */

/* ----- A. Sitewide film grain ------------------------------------- */

/* feTurbulence SVG inlined as a data URI. baseFrequency=0.85 gives a
   fine grain at the 220×220 tile size; mix-blend-mode: overlay means
   the noise only nudges underlying pixels lighter or darker rather
   than painting a solid texture on top. 4.5% opacity keeps it
   subliminal — you'd never notice it on a screenshot but the page
   feels less plasticky in motion. pointer-events:none lets clicks
   pass through, and z-index: 200 sits just above the skip-link (100)
   so the grain renders over everything (including back-to-top at 50). */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 200;
    opacity: 0.045;
    mix-blend-mode: overlay;
    background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 220 220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    background-size: 220px 220px;
}


/* ----- B. Glassmorphism on cards over photo backgrounds ----------- */

/* Gated behind @supports so a browser without backdrop-filter still
   sees the original solid card (defined earlier in the file). The
   translucent background lets the photo bleed through subtly, and the
   blur softens it into atmosphere rather than texture. */
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {

    /* Home page BMI card on the hero photo */
    .hero--photo .bmi-card {
        background: linear-gradient(135deg,
            rgba(27, 32, 48, 0.72) 0%,
            rgba(22, 26, 36, 0.62) 100%);
        backdrop-filter: blur(14px) saturate(140%);
        -webkit-backdrop-filter: blur(14px) saturate(140%);
        border-color: rgba(255, 255, 255, 0.12);
    }

    /* Auth-page card on the login/signup photo. Keeps the warm orange
       halo defined in section 8 — only the body fill + blur change. */
    .auth-shell--photo > .auth-card {
        background: linear-gradient(135deg,
            rgba(27, 32, 48, 0.74) 0%,
            rgba(22, 26, 36, 0.64) 100%);
        backdrop-filter: blur(16px) saturate(140%);
        -webkit-backdrop-filter: blur(16px) saturate(140%);
    }
}


/* ----- D. Custom scrollbar ---------------------------------------- */

/* Dark elevated track + warm-orange thumb so the scrollbar reads as
   part of the design rather than a stock system widget. The 3px solid
   border on the thumb (same color as the track) creates the "pill
   inset" look that's standard in modern dark UIs. Firefox uses the
   shorthand scrollbar-* properties; Chromium/Safari use the
   ::-webkit-scrollbar-* pseudo-elements. */
html {
    scrollbar-color: rgba(255, 122, 26, 0.40) var(--bg-elev);
    scrollbar-width: thin;
}
::-webkit-scrollbar         { width: 12px; height: 12px; }
::-webkit-scrollbar-track   { background: var(--bg-elev); }
::-webkit-scrollbar-thumb {
    background: rgba(255, 122, 26, 0.32);
    border-radius: 999px;
    border: 3px solid var(--bg-elev);
    transition: background-color .15s ease;
}
::-webkit-scrollbar-thumb:hover  { background: rgba(255, 122, 26, 0.55); }
::-webkit-scrollbar-corner       { background: var(--bg-elev); }


/* ----- C. Scroll-triggered reveal animations ---------------------- */

/* Wrapped in (prefers-reduced-motion: no-preference) so reduced-motion
   users never see content start at opacity:0 — the global rule that
   shortens animation-duration would otherwise leave them with a
   pre-animation frame still visible. main.js skips the IntersectionObserver
   setup for the same reason; this CSS rule is the belt-and-braces. */
@media (prefers-reduced-motion: no-preference) {
    .reveal {
        opacity: 0;
        transform: translateY(18px);
        transition:
            opacity .55s ease-out,
            transform .55s ease-out;
        transition-delay: var(--reveal-delay, 0ms);
        will-change: opacity, transform;
    }
    .reveal.is-visible {
        opacity: 1;
        transform: translateY(0);
    }
}
