/* =========================================================================
   Turbolift - stylesheet
   Sections: 1 variables (1b theme overrides) · 2 base · 3 header & timers
             4 notifications · 5 layout · 6 exercise cards · 7 session
             actions · 8 logs · 9 panels · 10 install/update · 11 fullscreen
             12 animations · 13 responsive
   ========================================================================= */


/* ============================= 1 · Variables ============================= */
:root {
    /* Surfaces - three flat, distinct elevation tiers (no gradients/washes):
       page canvas, in-flow card, and a slightly lighter raised tier for
       nested/secondary surfaces (e.g. toggle tracks). */
    --color-background: rgb(11, 11, 12);
    --color-background-header: rgb(11, 11, 12);
    --color-background-panel: rgb(11, 11, 12);
    --color-surface: rgb(17, 17, 19);
    --color-surface-raised: rgb(23, 23, 26);
    --color-background-log-entry: rgb(243, 244, 246);
    /* Fixed dark ink for text on top of --color-background-log-entry, which
       stays light in every theme - unlike --color-text-dark (below), this is
       never overridden per theme. */
    --color-ink: rgb(17, 17, 19);

    /* Borders - neutral hairlines, used for structure. Reserve the accent
       border for genuinely accent-colored controls (see below). */
    --color-border: rgba(255, 255, 255, 0.08);
    --color-border-strong: rgba(255, 255, 255, 0.16);

    /* Text */
    --color-text: rgb(237, 237, 239);
    --color-text-dark: rgb(11, 11, 12);
    --color-text-muted: rgb(138, 138, 144);
    /* Emphasis text (stat values, key numbers) - shares the single accent
       hue rather than a separate "info" color. */
    --color-highlight: rgb(198, 241, 53);

    /* Primary accent - one signal color (lime), reserved for the primary
       CTA, completed/active states, and emphasized numbers. Mono + signal:
       almost everything else in the app is grayscale. */
    --color-accent: rgb(198, 241, 53);
    --color-accent-border: rgba(198, 241, 53, 0.4);
    --color-accent-fill: rgba(198, 241, 53, 0.14);

    /* State colors - completed/pending/success all alias the one accent;
       partial is a neutral bright fill rather than a second hue, so the
       three set states read as outline / neutral-fill / accent-fill instead
       of a rainbow. Danger stays real red - the one unavoidable second hue,
       reserved strictly for destructive actions. */
    --color-yes: rgb(198, 241, 53);
    --color-no: rgb(198, 241, 53);
    --color-pending: rgb(198, 241, 53);
    --color-completed: rgb(198, 241, 53);
    --color-partial: rgb(228, 228, 231);
    --color-danger: rgb(248, 113, 113);

    /* Panel action colours - shared across all panel confirm/cancel/secondary
       buttons: accent to save, red to cancel/destroy. */
    --color-success: rgb(198, 241, 53);
    --color-success-border: rgba(198, 241, 53, 0.4);

    /* Neutral press feedback for secondary/ghost buttons - kept separate
       from --color-accent-fill, which stays reserved for accent-colored
       controls only. */
    --color-press: rgba(255, 255, 255, 0.06);
    /* Track background for the settings toggle/segmented pill controls. */
    --color-track: rgba(255, 255, 255, 0.12);

    /* Radii */
    --radius-small: 8px;
    --radius-medium: 10px;
    --radius-pill: 9999px;

    /* Spacing */
    --spacing-s: 0.5rem;
    --spacing-sm: 0.75rem;
    --spacing-m: 1rem;
    --spacing-l: 1.5rem;

    /* Sizing */
    --button-min-height: 2.6rem;
    --set-button-width: 78px;
    --container-width: min(100%, 480px);


    /* Type scale */
    --font-size-button-small: 1.2rem;
    --font-size-button-medium: 1.4rem;
    --font-size-timer: 3.5rem;
    --font-size-rest-timer: 3.5rem;

    /* Header geometry
       --safe-area-top has a floor of 14px, not just the raw env() value.
       In a regular (non-installed) mobile browser tab, env(safe-area-inset-top)
       is typically 0 even on notched phones - the browser's own chrome
       already avoids the notch, so nothing is reported to the page. That
       leaves fixed-position header content sitting flush against the very
       top of the layout viewport, which can render partly under/behind the
       browser's own address bar. The floor keeps a minimum clearance there
       in every context; installed/standalone mode still gets the real
       (larger) notch value via max(). */
    --safe-area-top: max(env(safe-area-inset-top, 0px), 14px);
    --safe-area-bottom: env(safe-area-inset-bottom, 0px);
    --header-base-height: 70px;
    --header-height: calc(var(--header-base-height) + var(--safe-area-top));

    /* Layers
       Panel sits above main content but below the header, rest timer, and
       notifications so timers stay reachable while a panel is open. */
    --z-panel: 90;
    --z-header: 100;
    --z-rest-timer: 250;
    --z-notification: 500;



    /* Effects - elevated-card shadows replace the old gradient-wash cards.
       Two tiers: a resting card shadow, and a stronger one for elements
       actively lifted above their neighbors (e.g. a dragged reorder row). */
    --shadow-card: 0 8px 20px rgba(0, 0, 0, 0.4), 0 2px 6px rgba(0, 0, 0, 0.3);
    --shadow-raised: 0 12px 28px rgba(0, 0, 0, 0.5);
    --pulse-animation-normal: 3s;
    --pulse-animation-fast: 1s;
}

/* ------------------------- 1b · Theme overrides --------------------------
   :root above is the "Mono" theme (default, no [data-theme] attribute
   needed). Each block below overrides only the color/shadow tokens for a
   selectable alternative - structure, spacing, and radii stay identical
   across every theme, only the token values change. Applied via
   document.documentElement.dataset.theme, set on load by
   scripts/theme-init.js (to avoid a flash of the wrong theme) and live by
   the theme picker in Settings (see openSettingsPopup() in ui.js). */

/* Indigo - the app's original palette: indigo for the primary action, a
   distinct green for success/completed, amber for partial, cyan for
   emphasized numbers. Richer/busier than Mono on purpose. */
[data-theme="indigo"] {
    --color-background: rgb(10, 11, 14);
    --color-background-header: rgb(10, 11, 14);
    --color-background-panel: rgb(10, 11, 14);
    --color-surface: rgb(18, 19, 24);
    --color-surface-raised: rgb(25, 26, 32);

    --color-border: rgba(255, 255, 255, 0.08);
    --color-border-strong: rgba(255, 255, 255, 0.16);

    --color-text: rgb(243, 244, 246);
    --color-text-dark: rgb(10, 11, 14);
    --color-text-muted: rgb(156, 163, 175);
    --color-highlight: rgb(34, 211, 238);

    --color-accent: rgb(99, 102, 241);
    --color-accent-border: rgba(99, 102, 241, 0.45);
    --color-accent-fill: rgba(99, 102, 241, 0.14);

    --color-yes: rgb(52, 211, 153);
    --color-no: rgb(99, 102, 241);
    --color-pending: rgb(99, 102, 241);
    --color-completed: rgb(52, 211, 153);
    --color-partial: rgb(251, 191, 36);
    --color-danger: rgb(248, 113, 113);

    --color-success: rgb(52, 211, 153);
    --color-success-border: rgba(52, 211, 153, 0.45);

    --color-press: rgba(255, 255, 255, 0.06);
    --color-track: rgba(255, 255, 255, 0.12);
}

/* Graphite - a warmer near-black with a coral primary and mint success. */
[data-theme="graphite"] {
    --color-background: rgb(16, 14, 12);
    --color-background-header: rgb(16, 14, 12);
    --color-background-panel: rgb(16, 14, 12);
    --color-surface: rgb(23, 20, 18);
    --color-surface-raised: rgb(30, 26, 23);

    --color-border: rgba(255, 255, 255, 0.08);
    --color-border-strong: rgba(255, 255, 255, 0.16);

    --color-text: rgb(245, 241, 236);
    --color-text-dark: rgb(16, 14, 12);
    --color-text-muted: rgb(166, 156, 144);
    --color-highlight: rgb(255, 177, 153);

    --color-accent: rgb(255, 107, 74);
    --color-accent-border: rgba(255, 107, 74, 0.4);
    --color-accent-fill: rgba(255, 107, 74, 0.14);

    --color-yes: rgb(74, 222, 128);
    --color-no: rgb(255, 107, 74);
    --color-pending: rgb(255, 107, 74);
    --color-completed: rgb(74, 222, 128);
    --color-partial: rgb(253, 186, 116);
    --color-danger: rgb(248, 113, 113);

    --color-success: rgb(74, 222, 128);
    --color-success-border: rgba(74, 222, 128, 0.4);

    --color-press: rgba(255, 255, 255, 0.06);
    --color-track: rgba(255, 255, 255, 0.12);
}

/* Light - the one light-background theme. Fills (accent/success/partial/
   danger) are deep/saturated enough for white text on top, the reverse of
   the dark themes above, so --color-text-dark flips to white here. */
[data-theme="light"] {
    --color-background: rgb(247, 247, 248);
    --color-background-header: rgb(247, 247, 248);
    --color-background-panel: rgb(247, 247, 248);
    --color-surface: rgb(255, 255, 255);
    --color-surface-raised: rgb(241, 241, 243);

    --color-border: rgba(0, 0, 0, 0.10);
    --color-border-strong: rgba(0, 0, 0, 0.18);

    --color-text: rgb(24, 24, 27);
    --color-text-dark: rgb(255, 255, 255);
    --color-text-muted: rgb(113, 113, 122);
    --color-highlight: rgb(67, 56, 202);

    --color-accent: rgb(79, 70, 229);
    --color-accent-border: rgba(79, 70, 229, 0.4);
    --color-accent-fill: rgba(79, 70, 229, 0.10);

    --color-yes: rgb(21, 128, 61);
    --color-no: rgb(79, 70, 229);
    --color-pending: rgb(79, 70, 229);
    --color-completed: rgb(21, 128, 61);
    --color-partial: rgb(180, 83, 9);
    --color-danger: rgb(220, 38, 38);

    --color-success: rgb(21, 128, 61);
    --color-success-border: rgba(21, 128, 61, 0.4);

    --color-press: rgba(0, 0, 0, 0.05);
    --color-track: rgba(0, 0, 0, 0.10);

    --shadow-card: 0 4px 14px rgba(0, 0, 0, 0.10), 0 1px 3px rgba(0, 0, 0, 0.08);
    --shadow-raised: 0 10px 24px rgba(0, 0, 0, 0.16);
}

/* =============================== 2 · Base =============================== */
*,
*::before,
*::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

/* Scrollbar: hidden on the root viewport so the exercise list scrollbar
   never overlaps the set buttons. The page remains scrollable via touch
   or trackpad; only the visible thumb/track is removed. */
html {
    font-size: 16px;
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
    scrollbar-width: none;
    -ms-overflow-style: none;
}

html::-webkit-scrollbar {
    display: none;
}

body {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    background-color: var(--color-background);
    color: var(--color-text);
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* Locks the page behind an open content panel. */
body.has-overlay {
    overflow: hidden;
}


/* `hidden` must win over the display values set on buttons and flex boxes. */
[hidden] {
    display: none !important;
}

button,
input,
.exercise-name {
    font-family: inherit;
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
    user-select: none;
    -webkit-user-select: none;
}

button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: var(--button-min-height);
    border: none;
    border-radius: var(--radius-pill);
    cursor: pointer;
    color: inherit;
    background: transparent;
}

button:focus-visible,
input:focus-visible,
.exercise-name:focus-visible {
    outline: 2px solid var(--color-highlight);
    outline-offset: 2px;
}

input[type="number"] {
    -moz-appearance: textfield;
    appearance: textfield;
}

input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

/* Tapping a number field clears it to a blinking cursor, moving the
   previous value into the placeholder so it stays visible - greyed out -
   behind the caret (see clearToPlaceholder()/valueOrPlaceholder() in ui.js).
   These fields are focused programmatically the instant they appear, so the
   browser's own focus ring would just flash uninvited; the caret itself is
   feedback enough. */
input.reps-input,
.weight-input-container input[type="number"],
.weight-input-container input[type="text"] {
    outline: none !important;
}

input.reps-input::placeholder {
    color: inherit;
    opacity: 0.45;
}

.weight-input-container input[type="number"]::placeholder,
.weight-input-container input[type="text"]::placeholder {
    color: var(--color-text-muted);
    opacity: 0.7;
}



/* ========================= 3 · Header and timers ======================== */

#workout-header {
    position: fixed;
    inset: 0 0 auto 0;
    height: var(--header-height);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: var(--z-header);
    background-color: var(--color-background-header);
}

.header-content {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    max-width: var(--container-width);
    height: 100%;
    padding: var(--spacing-s) 0;
    padding-top: calc(var(--spacing-s) + var(--safe-area-top));
}

/* Idle-screen "start" control - a single filled primary CTA (the app's one
   accent-filled button per screen). Stays alone in the fixed header - a
   single short line - so the header's height never depends on how much
   text is in play; the "switch to the other workout" override lives below
   it in #workout-summary instead (in the scrollable page, not the fixed
   header) precisely to avoid that dependency. See updateWorkoutSelect() in
   main.js, which sets both buttons' labels and data-type from
   suggestedWorkoutType(). */
#workout-main-btn {
    width: 80%;
    max-width: 420px;
    font-size: var(--font-size-button-medium);
    font-weight: 700;
    color: var(--color-text-dark);
    background-color: var(--color-accent);
    border: 1px solid var(--color-accent);
    border-radius: var(--radius-medium);
}

#workout-main-btn:active {
    opacity: 0.75;
}

/* Workout timer -------------------------------------------------------- */
#workout-timer {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
}

#workout-timer-display {
    font-size: var(--font-size-timer);
    font-variant-numeric: tabular-nums;
    -webkit-font-feature-settings: "tnum";
    font-feature-settings: "tnum";
    cursor: pointer;
}

/* Shown instead of the running digits while the workout is paused. */
#workout-timer-controls {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--spacing-m);
    width: 90%;
    max-width: 420px;
}

#resume-workout-btn,
#end-workout-btn,
#save-workout-btn,
#discard-workout-btn {
    flex: 1;
    font-size: var(--font-size-button-medium);
    color: var(--color-text);
    background-color: var(--color-background);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-medium);
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

#resume-workout-btn:active,
#end-workout-btn:active,
#save-workout-btn:active,
#discard-workout-btn:active {
    background-color: var(--color-press);
}

/* Save / Discard: tap-to-arm, tap-again-to-confirm, same pattern as the log
   delete button - accent to keep the workout, red to throw it away. */
#save-workout-btn {
    color: var(--color-success);
    border-color: var(--color-success-border);
}

#save-workout-btn:active {
    background-color: rgba(198, 241, 53, 0.14);
}

#save-workout-btn.confirm {
    color: var(--color-text-dark);
    background-color: var(--color-success);
    border-color: var(--color-success);
    font-weight: bold;
}

#discard-workout-btn {
    color: var(--color-danger);
    border-color: rgba(248, 113, 113, 0.4);
}

#discard-workout-btn:active {
    background-color: rgba(248, 113, 113, 0.12);
}

#discard-workout-btn.confirm {
    color: var(--color-text-dark);
    background-color: var(--color-danger);
    border-color: var(--color-danger);
    font-weight: bold;
}

/* The rest timer takes over the header area while it counts down. */
#workout-header.rest-timer-active #workout-timer,
#workout-header.rest-timer-active #workout-main-btn {
    visibility: hidden;
    pointer-events: none;
}

/* Rest timer ---------------------------------------------------------- */
#rest-timer {
    position: fixed;
    inset: 0 0 auto 0;
    height: var(--header-height);
    display: flex;
    align-items: center;
    justify-content: center;
    padding-top: var(--safe-area-top);
    z-index: var(--z-rest-timer);
    background-color: var(--color-background);
    color: var(--color-text);
    font-size: var(--font-size-rest-timer);
    font-variant-numeric: tabular-nums;
    user-select: none;
    -webkit-user-select: none;
}

.rest-timer-number {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 5rem;
    cursor: text;
}

#rest-timer .reps-input {
    width: 6rem;
    height: 1.2em;
    font-size: var(--font-size-rest-timer);
    text-align: center;
    border: none;
    background: transparent;
    color: var(--color-text);
    caret-color: var(--color-text);
}

/* ========================== 4 · Notifications =========================== */
.notification {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: var(--header-height);
    padding-top: var(--safe-area-top);
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    z-index: var(--z-notification);
    background-color: var(--color-background);
    color: var(--color-text);
    font-size: clamp(1.1rem, 5vw, 2rem);
    font-weight: bold;
    transition: opacity 0.3s ease;
    /* Informational only: never intercept taps meant for the timer beneath. */
    pointer-events: none;
}

.notification--hide {
    opacity: 0;
}

/* ============================== 5 · Layout ============================== */
main {
    width: 100%;
    max-width: var(--container-width);
    min-height: calc(100vh - var(--header-height));
    margin-inline: auto;
    padding-top: var(--header-height);
    padding-bottom: calc(4rem + var(--safe-area-bottom));
    display: flex;
    flex-direction: column;
    gap: var(--spacing-l);
}

/* Start page gets a small welcome gap between the header and the first
   content block so the transition from the fixed header to the summary
   doesn't feel abrupt, but exercises (when a workout is active) keep
   the tight top so cards start right below the header. */
body:not([data-workout]) main {
    padding-top: calc(var(--header-height) + var(--spacing-l));
}

/* Section headers used on the start page for History and Tools.
   Echoes the exercise-card header hairlines so the language reads consistent. */
.section-header {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 90%;
    max-width: var(--container-width);
    margin: 0 auto;
    padding: var(--spacing-s) 0;
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.section-header::before,
.section-header::after {
    content: '';
    position: absolute;
    left: 0;
    width: 100%;
    height: 1px;
    background-color: var(--color-border);
}

.section-header::before { top: 0; }
.section-header::after { bottom: 0; }

/* Extra breathing room between major functional groups on the start page,
   while keeping tighter gaps within each group (logs, tool buttons). */
#workout-summary {
    margin-bottom: var(--spacing-l);
}

#workout-logs {
    margin-bottom: var(--spacing-l);
}

#data-tools {
    margin-top: var(--spacing-s);
}

#exercises {
    width: 100%;
}

.exercise-list {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: var(--spacing-m);
    width: 100%;
}

/* ========================== 6 · Exercise cards ========================== */
/* Exercise cards sit flush on the page background rather than as their own
   elevated box - with one of these per exercise (several per screen), a
   filled surface here reads as a wall of gray. Elevation is reserved for
   content that's genuinely secondary/floating (log entries, panel cards);
   the exercise list itself is the primary content, so it stays flat and is
   separated only by the hairlines around its title row and the gap between
   cards. */
.exercise-item {
    display: grid;
    gap: var(--spacing-s);
    width: 100%;
    padding: var(--spacing-s);
}

/* Name + remove button share one row. The name keeps the fullscreen toggle;
   the ✕ is a separate button so the two actions never collide. */
.exercise-header {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    margin-bottom: var(--spacing-m);
    padding: var(--spacing-sm) var(--spacing-s);
}

/* Hairlines above and below the exercise title row. */
.exercise-header::before,
.exercise-header::after {
    content: '';
    position: absolute;
    left: 0;
    width: 100%;
    height: 1px;
    background-color: var(--color-border);
}

.exercise-header::before { top: 0; }
.exercise-header::after { bottom: 0; }

.exercise-name {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 0;
    padding: 0 var(--spacing-s);
    font-size: 1.2rem;
    font-weight: bold;
    color: var(--color-text);
    cursor: pointer;
}

/* Invisible counterweight to .remove-exercise-btn - see exerciseHeaderHtml()
   in ui.js for why this is needed to truly center .exercise-name. */
.exercise-header-spacer {
    flex-shrink: 0;
    width: 2.4rem;
    height: 2.4rem;
}

.remove-exercise-btn {
    flex-shrink: 0;
    width: 2.4rem;
    height: 2.4rem;
    min-height: 0;
    margin-left: auto;
    color: var(--color-text-muted);
    background-color: transparent;
    border: 1px solid var(--color-border-strong);
    border-radius: 50%;
    transition: color 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
}

.remove-exercise-btn svg {
    width: 20px;
    height: 20px;
}

.remove-exercise-btn:active {
    color: var(--color-danger);
    background-color: rgba(248, 113, 113, 0.12);
    border-color: rgba(248, 113, 113, 0.4);
}

/* Hide the remove control while a content panel is open so it doesn't
   compete with the panel's own dismiss X (cards stay mounted underneath). */
body.has-overlay .remove-exercise-btn {
    visibility: hidden;
    pointer-events: none;
}

/* Replaces the name + ✖ once tapped - see showRemoveExerciseConfirm() in
   ui.js. Same flex row as the normal header, so nothing jumps: the prompt
   takes the name's flexible space, Cancel/Remove take the ✖'s slot. */
.exercise-remove-prompt {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 0;
    padding: 0 var(--spacing-s);
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--color-text);
}

.exercise-remove-actions {
    display: flex;
    flex-shrink: 0;
    gap: var(--spacing-s);
}

.exercise-header-btn {
    min-height: 2.4rem;
    padding: 4px 14px;
    font-size: 0.85rem;
    font-weight: 700;
    border-radius: var(--radius-medium);
    border: 1px solid transparent;
    transition: color 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
}

/* Cancel: neutral outline, matches .panel-actions .cancel-btn. */
.exercise-cancel-btn {
    color: var(--color-text-muted);
    border-color: var(--color-border-strong);
}

.exercise-cancel-btn:active {
    color: var(--color-text);
    background-color: var(--color-press);
}

/* Remove: filled danger, matches .delete-log-btn.confirm / #discard-workout-btn.confirm. */
.exercise-confirm-remove-btn {
    color: var(--color-text-dark);
    background-color: var(--color-danger);
    border-color: var(--color-danger);
}

.exercise-confirm-remove-btn:active {
    background-color: rgba(248, 113, 113, 0.75);
}

body.has-overlay .exercise-remove-actions {
    visibility: hidden;
    pointer-events: none;
}



/* Sets/warmups always come first, with the sidebar's two-column button grid
   below them - this used to be a side-by-side layout on wider screens (the
   sidebar to the left of the sets), but that squeezed the set rows into a
   narrow column and, on actual phone widths, broke down further into an
   illegible wrapped mess. Stacked-with-the-sidebar-below reads well at
   every width, so it is the only layout now rather than a narrow-screen
   fallback. */
.exercise-controls {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--spacing-s);
    align-items: start;
    width: 100%;
    /* Shared inset for both the sidebar buttons and the set/warmup rows
       below - set on this common ancestor (rather than separately on
       .exercise-sidebar and .set-column) so the two can never drift out of
       alignment with each other again. */
    padding: 0 var(--spacing-sm);
}

.exercise-sidebar {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--spacing-s);
    align-content: start;
    /* The sidebar comes first in the markup (it used to sit to the left of
       the sets), so without this it would stack above the sets instead of
       below them. `order` flips the visual sequence without touching the
       markup itself. */
    order: 2;
}

/* Exercise-card controls use small rounded rects (not the global pill
   default) so they sit flush with panels/logs instead of reading as chips.
   Neutral by default - the single accent stays reserved for the primary
   CTA and completed/active states, not every tappable control. */
.exercise-sidebar button {
    width: 100%;
    font-size: 0.95rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    background-color: transparent;
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-small);
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

.exercise-sidebar button:active {
    color: var(--color-text);
    background-color: var(--color-press);
    border-color: var(--color-border-strong);
}

/* A manually-entered 1RM (vs. the app's calculated estimate) - flagged with
   a dashed border rather than a color, consistent with the "custom" flag
   used for free-form exercises in the Add Exercise picker below. */
.one-rep-max-button.custom {
    border-style: dashed;
    border-color: var(--color-border-strong);
    color: var(--color-text);
}

/* Increment normally has no rules of its own here - it shares the same
   neutral styling as the other sidebar controls via .exercise-sidebar button /
   .exercise-sidebar button:active above, so the column reads as one
   cohesive group. When a deload is suggested (see
   getConsecutiveFailureStreak() in coaching.js), only its label text pulses
   - a filled background was tried first, but it made the button read as a
   different control from its sidebar neighbors instead of just flagging
   the existing one. */
.increment-btn.needs-deload .increment-btn-label {
    animation: opacityPulse 1.3s ease-in-out infinite;
}

/* Increment + Add Set share this row and both need to span the sidebar's
   full two-column width - as two separate buttons normally, or as the one
   merged WARMUP/WORKING control below once Add Set is tapped (see
   showAddSetChoice() in ui.js). A plain flex row handles both shapes
   identically: two buttons side by side, or a single full-width child. */
.increment-addset-group {
    grid-column: 1 / -1;
    display: flex;
    gap: var(--spacing-s);
}

.increment-addset-group > button {
    flex: 1 1 0;
}

/* One bordered control split by a thin line, not two separately-spaced
   buttons - reads as "pick one of two", not "two more buttons appeared".
   WARMUP/WORKING reuse the app's only two filled tones (the neutral-bright
   "partial" fill and the single accent, same ones a partially-done vs.
   completed set already use) rather than inventing new colors. */
.addset-choice {
    display: flex;
    flex: 1 1 auto;
    width: 100%;
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-small);
    overflow: hidden;
}

.addset-choice .addset-choice-btn {
    flex: 1 1 0;
    min-height: var(--button-min-height);
    font-size: 0.95rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    border: none;
    border-radius: 0;
    transition: opacity 0.15s ease;
}

.addset-choice .addset-choice-btn:active {
    opacity: 0.7;
}

.addset-choice .addset-choice-warmup {
    color: var(--color-text-dark);
    background-color: var(--color-partial);
}

.addset-choice .addset-choice-working {
    color: var(--color-text-dark);
    background-color: var(--color-accent);
    /* The thin dividing line between the two halves. */
    border-left: 1px solid rgba(0, 0, 0, 0.2);
}

.exercise-main {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-s);
    min-width: 0;
    order: 1;
}

/* Warmup section - visually distinct (dashed, muted buttons) from the
   working sets below it, so the two never read as the same kind of set.
   No divider rule of its own anymore - each row's "Warmup"/"Set" label
   already makes the boundary obvious without an extra dashed line. */
.warmup-section {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-s);
}

.warmup-tracking {
    opacity: 0.85;
}

.warmup-btn {
    border-style: dashed;
    color: var(--color-text-muted);
}

.warmup-btn.completed {
    color: var(--color-text-dark);
    background-color: var(--color-text-muted);
    border-color: var(--color-text-muted);
    border-style: solid;
}

/* One set/warmup per row, always - letting them fall into an auto-fit grid
   of multiple columns per row made rows of different widths wrap against
   each other unpredictably, especially once .exercise-main had the full
   card width to itself (see .exercise-controls above). A single column
   keeps every row the same width and reads top-to-bottom in set order. */
.sets-tracking {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--spacing-s);
}


/* "Warmup" / "Set 1", "Set 2" etc, to the left of that row's buttons -
   identifies each row by kind/number now that an exercise's sets/warmups are
   always a single stacked column rather than a multi-column grid where
   left-to-right position also hinted at order. Warmups don't need their own
   number - there's rarely more than a couple, and they're already visually
   separated from the working sets below. */
.set-column {
    display: grid;
    /* A fixed (not auto-sized) label column, so "WARMUP" vs "SET 1"/"SET 12"
       can never make one row's label wider than another's - each
       .set-column is its own independent grid, so an auto-sized track could
       otherwise vary row to row depending on that row's own label text,
       nudging the weight/reps button's width along with it. */
    grid-template-columns: 4.75rem 1fr auto;
    gap: var(--spacing-s);
    align-items: center;
}

.set-column-label {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    /* On top of .set-column's regular gap - just between the label and the
       weight/reps button, not between that button and the checkmark. */
    margin-right: var(--spacing-s);
    /* Never wraps to a second line - a two-line label would grow this row
       taller than its neighbors instead of just sitting in its fixed-width
       column. */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.edit-weight-btn {
    width: 100%;
    /* Caps how wide this can stretch within its 1fr grid track - the track
       itself is unchanged (so the checkmark button two columns over never
       moves), but the button no longer stretches edge to edge on wider
       cards, which reads as a bulkier bar than the compact pill it should be. */
    max-width: 170px;
    min-width: 80px;
    height: var(--button-min-height);
    font-size: 1rem;
    font-weight: 600;
    color: var(--color-text);
    background-color: transparent;
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-small);
    transition: background-color 0.2s ease, border-color 0.2s ease;
}

.edit-weight-btn:active {
    background-color: var(--color-press);
    border-color: var(--color-border-strong);
}

/* Untouched/target state - neutral outline. Only completed, pending, and
   partial fills reach for color, so a still-empty set never reads as
   already "activated". */
.set-btn {
    position: relative;
    flex-shrink: 0;
    width: var(--set-button-width);
    height: var(--button-min-height);
    font-size: 1.8rem;
    color: var(--color-text-muted);
    background-color: transparent;
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-small);
    transition: background-color 0.2s ease, border-color 0.2s ease;
}

.set-btn:active {
    background-color: var(--color-press);
}


/* Filled states cover the border, so the checkmark needs a dark tone for
   contrast against the bright fill instead of the accent color above. */
/* Blinks for CONFIG.SET_BLINK_MS, the window in which the set can be corrected. */
.set-btn.pending {
    color: var(--color-text-dark);
    background-color: var(--color-pending);
    border-color: var(--color-pending);
    animation: opacityPulse 1s infinite;
}

.set-btn.completed {
    color: var(--color-text-dark);
    background-color: var(--color-completed);
    border-color: var(--color-completed);
}

/* Completed, but with fewer reps than prescribed - same tick, different colour. */
.set-btn.partial {
    color: var(--color-text-dark);
    background-color: var(--color-partial);
    border-color: var(--color-partial);
}

/* Inline reps entry, rendered inside the set button. */
.set-btn .reps-input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    text-align: center;
    font-size: var(--font-size-button-medium);
    border: none;
    border-radius: var(--radius-small);
    background-color: var(--color-background-log-entry);
    color: var(--color-ink);
}


/* ========================= 7 · Session actions ========================== */
#session-actions {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-l);
    width: 100%;
}

.button-group {
    display: flex;
    flex-wrap: wrap;
    gap: var(--spacing-s);
    width: 80%;
    max-width: 420px;
    margin: 0 auto;
}

.button-group button {
    flex: 1;
    gap: var(--spacing-s);
    padding: 12px;
    font-size: 1rem;
    color: var(--color-text);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-medium);
    transition: background-color 0.2s ease, border-color 0.2s ease;
}

.button-group button:active {
    background-color: var(--color-press);
    border-color: var(--color-border-strong);
}

.button-group svg {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
}



/* ============================== 8 · Logs ================================ */
#workout-summary {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--spacing-s);
    width: 90%;
    max-width: var(--container-width);
    margin-inline: auto;
    text-align: center;
}

/* Overrides which workout is due next - the exception, not a second
   equal-weight choice, so it's a quiet text link rather than a button that
   competes with the header's primary CTA above it. Lives here (in the
   scrollable page) rather than in the fixed header, so the header's height
   never has to grow to fit it - see the comment on #workout-main-btn. */
#workout-switch-btn {
    min-height: 0;
    padding: 4px;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--color-text-muted);
    text-decoration: underline;
    text-underline-offset: 3px;
}

#workout-switch-btn:active {
    color: var(--color-text);
}

.workout-logs {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-l);
    width: 80%;
    margin-inline: auto;
}

/* Empty-state placeholder: a quiet, centered prompt to get started.
   Same elevated surface as the log-entry cards so it reads as part of
   the same visual family. */
.workout-logs p {
    padding: var(--spacing-m);
    text-align: center;
    font-size: 0.95rem;
    color: var(--color-text-muted);
    border-radius: var(--radius-medium);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-card);
}

/* "Show N more / Show less" toggle below the log entries. A small muted
   pill that stays visually subordinate to the log cards above it. */
.logs-toggle-btn {
    align-self: center;
    min-height: 2rem;
    padding: 6px 18px;
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    background: transparent;
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-pill);
    cursor: pointer;
    transition: color 0.15s ease, border-color 0.15s ease;
}

.logs-toggle-btn:active {
    color: var(--color-text);
    border-color: var(--color-border-strong);
}

/* Each entry reads like a compact receipt: a badge/date/duration header,
   one line per exercise, then a volume total + delete action footer. */
.log-entry {
    display: flex;
    flex-direction: column;
    width: 100%;
    padding: var(--spacing-m);
    border-radius: var(--radius-medium);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-card);
    color: var(--color-text);
}

.log-header {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--spacing-s);
    padding-bottom: var(--spacing-s);
    margin-bottom: var(--spacing-s);
    border-bottom: 1px solid var(--color-border);
}

.log-workout-badge {
    padding: 2px 12px;
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-text-dark);
    background-color: var(--color-accent);
    border-radius: var(--radius-pill);
}

.log-date {
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

.log-duration {
    margin-left: auto;
    font-size: 0.85rem;
    font-variant-numeric: tabular-nums;
    color: var(--color-text-muted);
}

.log-exercises {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-s);
}

.log-exercise {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-s);
    padding-bottom: var(--spacing-s);
    border-bottom: 1px solid var(--color-border);
    font-size: 0.92rem;
}

.log-exercise:last-child {
    padding-bottom: 0;
    border-bottom: none;
}

.log-exercise-row {
    display: flex;
    align-items: baseline;
    gap: var(--spacing-s);
}

.log-exercise-name {
    color: var(--color-text);
    font-weight: 600;
}

.log-exercise-sets {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--spacing-s) var(--spacing-sm);
    align-items: center;
}

.log-set-label {
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    text-align: right;
    white-space: nowrap;
}

.log-set-pill {
    display: inline-block;
    padding: 2px 10px;
    font-size: 0.82rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--color-text);
    background-color: var(--color-surface-raised);
    border-radius: var(--radius-pill);
    white-space: nowrap;
}

/* Dashed and muted, echoing the live exercise card's warmup styling, so a
   warmup row never reads as just another working set. */
.log-warmup-pill {
    color: var(--color-text-muted);
    background-color: transparent;
    border: 1px dashed var(--color-border-strong);
}

.log-added-reps {
    color: var(--color-accent);
    font-weight: 700;
}

.log-exercise-badge {
    padding: 1px 7px;
    font-size: 0.7rem;
    font-weight: 700;
    color: var(--color-text-dark);
    background-color: var(--color-partial);
    border-radius: var(--radius-pill);
}

.log-exercise-1rm {
    font-size: 0.82rem;
    color: var(--color-highlight);
}

.log-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--spacing-s);
    margin-top: var(--spacing-m);
    padding-top: var(--spacing-m);
    border-top: 1px solid var(--color-border);
}

.log-volume {
    font-size: 0.92rem;
    color: var(--color-text-muted);
}

.log-volume strong {
    color: var(--color-highlight);
    font-weight: 700;
}

.delete-log-btn {
    min-height: 2rem;
    padding: 6px 16px;
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--color-text-muted);
    background-color: transparent;
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-medium);
    transition: color 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
}

.delete-log-btn:active {
    color: var(--color-danger);
    background-color: rgba(248, 113, 113, 0.08);
    border-color: rgba(248, 113, 113, 0.4);
}

.delete-log-btn.confirm {
    color: var(--color-text-dark);
    background-color: var(--color-danger);
    border-color: var(--color-danger);
    font-weight: bold;
}

/* ============================= 9 · Panels =============================== */
/*
   Full-bleed content panels sit under the fixed header (or edge-to-edge in
   fullscreen-mode). Same dark language as the rest of the app - no white
   floating cards. The panel *is* the surface; there is no separate dimmed
   backdrop to tap.
*/
.panel {
    position: fixed;
    top: var(--header-height);
    left: 0;
    right: 0;
    bottom: 0;
    z-index: var(--z-panel);
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
    background-color: var(--color-background-panel);
    color: var(--color-text);
    animation: panelIn 0.22s ease-out;
    /* Contain scroll to the body region so the header/footer stay put. */
    overflow: hidden;
}

/* Edge-to-edge when the app header is already hidden. */
body.fullscreen-mode .panel {
    top: 0;
    padding-top: var(--safe-area-top);
}

/* Inner column keeps the same narrow reading width as main content. */
.panel-header,
.panel-body,
.panel-footer {
    width: 100%;
    max-width: var(--container-width);
}

.panel-header {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    gap: var(--spacing-s);
    min-height: 3.25rem;
    padding: var(--spacing-m);
    border-bottom: 1px solid var(--color-border);
}

.panel-title {
    margin: 0;
    font-size: 1.15rem;
    font-weight: 700;
    text-align: center;
    color: var(--color-text);
    line-height: 1.3;
    overflow-wrap: anywhere;
}

.panel-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: var(--spacing-l) var(--spacing-m);
}

.panel-footer {
    flex-shrink: 0;
    padding: var(--spacing-m);
    padding-bottom: calc(var(--spacing-m) + var(--safe-area-bottom));
    border-top: 1px solid var(--color-border);
    background-color: var(--color-background-panel);
}

/* Typography ---------------------------------------------------------- */
.panel-copy {
    margin-bottom: var(--spacing-s);
    font-size: 1.05rem;
    line-height: 1.55;
    color: var(--color-text);
}

.panel-copy--muted {
    color: var(--color-text-muted);
}

.panel-copy strong {
    color: var(--color-highlight);
    font-weight: 700;
}

.panel-section-title {
    margin: var(--spacing-l) 0 var(--spacing-s);
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.panel-section-title:first-child {
    margin-top: 0;
}

.panel-stat {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--spacing-m);
    margin-bottom: 0.55rem;
    font-size: 1rem;
    color: var(--color-text-muted);
}

.panel-stat strong {
    color: var(--color-highlight);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    text-align: right;
}

.panel-bullet-list {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin: var(--spacing-s) 0 var(--spacing-m);
    padding: var(--spacing-m);
    border-radius: var(--radius-medium);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-card);
    color: var(--color-text);
    font-size: 0.98rem;
    line-height: 1.45;
}

.panel-bullet-list li::before {
    content: '· ';
    color: var(--color-accent);
    font-weight: 700;
}

.panel-warning {
    margin-top: var(--spacing-m);
    padding: var(--spacing-m);
    border-radius: var(--radius-medium);
    border: 1px solid rgba(248, 113, 113, 0.4);
    background-color: rgba(248, 113, 113, 0.1);
    color: var(--color-text);
    font-size: 1rem;
    line-height: 1.45;
}

.panel-warning strong {
    color: var(--color-text);
}

/* How-to / recovery body copy */
.how-to-content {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-s);
    line-height: 1.6;
}

.how-to-content p {
    font-size: 1.05rem;
    color: var(--color-text);
}

/* Progress weight trend (scripts/chart.js) ----------------------------
   Same SVG geometry as the exported report's per-exercise chart; each
   context brings its own colors since they're two separate stylesheets. */
.trend-chart-wrap {
    margin-bottom: var(--spacing-m);
    padding: var(--spacing-sm) var(--spacing-m);
    border-radius: var(--radius-medium);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-card);
}

.trend-chart {
    display: block;
    width: 100%;
    height: 64px;
}

.trend-chart-line {
    stroke: var(--color-highlight);
    stroke-width: 2;
    stroke-linejoin: round;
    stroke-linecap: round;
    vector-effect: non-scaling-stroke;
}

.trend-chart-dot {
    fill: var(--color-highlight);
}

.trend-chart-range {
    display: flex;
    justify-content: space-between;
    margin-top: var(--spacing-s);
    font-size: 0.78rem;
    font-variant-numeric: tabular-nums;
    color: var(--color-text-muted);
}

/* Progress recent sessions -------------------------------------------- */
.panel-session-list {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: var(--spacing-s);
    margin: 0;
    padding: 0;
}

.panel-session-list li {
    display: grid;
    grid-template-columns: auto 1fr auto;
    gap: var(--spacing-s);
    align-items: baseline;
    padding: var(--spacing-s) var(--spacing-sm);
    border-radius: var(--radius-small);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-card);
    font-size: 0.9rem;
}

.panel-session-date {
    color: var(--color-text-muted);
    white-space: nowrap;
}

.panel-session-detail {
    color: var(--color-text);
    min-width: 0;
    overflow-wrap: anywhere;
}

.panel-session-volume {
    color: var(--color-highlight);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    white-space: nowrap;
}

/* Form fields --------------------------------------------------------- */
.weight-input-container {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--spacing-m);
    width: 100%;
    margin-bottom: var(--spacing-m);
    padding: var(--spacing-sm) var(--spacing-m);
    border-radius: var(--radius-medium);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-card);
}

.weight-input-container label {
    font-size: 1rem;
    color: var(--color-text);
}

.weight-input-container input[type="number"],
.weight-input-container input[type="text"] {
    width: 110px;
    height: var(--button-min-height);
    font-size: 1.2rem;
    text-align: center;
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-small);
    background: var(--color-background);
    color: var(--color-text);
    caret-color: var(--color-text);
}

.weight-input-container input[type="text"] {
    width: min(100%, 12rem);
    text-align: left;
    padding: 0 var(--spacing-s);
    font-size: 1.05rem;
}

.weight-input-container input[type="number"]::placeholder,
.weight-input-container input[type="text"]::placeholder {
    color: var(--color-text-muted);
    opacity: 0.7;
}

/* Plate calculator card below the reps input - see openSetEditPopup() and
   plateCalcBodyHtml() in ui.js. A self-contained elevated card (not a bare
   line of text), one tier lighter than .weight-input-container above it so
   the two stacked cards stay visually distinct. */
.plate-calc {
    width: 100%;
    margin-bottom: var(--spacing-m);
    padding: var(--spacing-sm) var(--spacing-m);
    border-radius: var(--radius-medium);
    background-color: var(--color-surface-raised);
    box-shadow: var(--shadow-card);
}

.plate-calc-title {
    margin: 0 0 var(--spacing-s);
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.plate-calc-unit {
    text-transform: none;
    letter-spacing: normal;
    font-weight: 400;
    color: var(--color-text-muted);
}

.plate-calc-note {
    margin: 0;
    font-size: 0.95rem;
    color: var(--color-highlight);
}

.plate-calc-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--spacing-m);
    padding: 0.3rem 0;
}

.plate-calc-row + .plate-calc-row {
    border-top: 1px solid var(--color-border);
}

.plate-calc-label {
    font-size: 0.88rem;
    color: var(--color-text-muted);
}

.plate-calc-value {
    font-size: 0.95rem;
    font-weight: 700;
    color: var(--color-highlight);
    font-variant-numeric: tabular-nums;
    text-align: right;
}

/* Actions --------------------------------------------------------------
   Every panel footer resolves to one of two shapes:
     - a single dismiss button (Close / Done), centered and pill-sized
     - a Cancel + Save/Confirm pair, side by side and equal width
   Cancel/Close read as neutral outlined buttons; Save/Confirm are filled
   and colored by intent (accent = positive, red = destructive) so the
   weight of the action is legible at a glance, not just its label. */
.panel-actions {
    display: flex;
    align-items: stretch;
    justify-content: center;
    gap: var(--spacing-m);
    width: 100%;
}

.panel-actions button {
    flex: 1 1 0;
    min-height: var(--button-min-height);
    padding: var(--spacing-s) var(--spacing-m);
    font-size: 1rem;
    font-weight: 700;
    letter-spacing: 0.01em;
    color: var(--color-text);
    background-color: transparent;
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-medium);
    transition: background-color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;
}

.panel-actions button:active {
    opacity: 0.7;
}

/* A lone Close/Done button shouldn't stretch edge to edge - give it a
   comfortable fixed width and center it instead. */
.panel-actions > button:only-child {
    flex: 0 1 auto;
    min-width: 160px;
}

/* Cancel / Close: neutral outline, never competes with the primary action. */
.panel-actions .cancel-btn,
.panel-actions .close-btn {
    color: var(--color-text-muted);
    border-color: var(--color-border-strong);
}

.panel-actions .cancel-btn:active,
.panel-actions .close-btn:active {
    color: var(--color-text);
    background-color: var(--color-press);
}

/* Save / Confirm: filled, positive by default. */
.panel-actions .confirm-btn {
    color: var(--color-text-dark);
    background-color: var(--color-success);
    border-color: var(--color-success);
}

.panel-actions .confirm-btn:active {
    background-color: rgba(198, 241, 53, 0.75);
}

/* Destructive variant (e.g. Restore) - same shape, red instead of green. */
.panel-actions .confirm-btn.danger {
    color: var(--color-text-dark);
    background-color: var(--color-danger);
    border-color: var(--color-danger);
}

.panel-actions .confirm-btn.danger:active {
    background-color: rgba(248, 113, 113, 0.75);
}

/* Secondary, non-destructive action shown above the main button row
   (e.g. "Apply to all sets", "Use calculated value") - a quiet full-width
   outlined button so it reads as a deliberate action without competing
   with the Cancel/Save pair below it or borrowing the one accent color. */
.panel-secondary-action {
    display: block;
    width: 100%;
    margin-bottom: var(--spacing-m);
    padding: var(--spacing-sm) var(--spacing-m);
    font-size: 0.85rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    text-align: center;
    color: var(--color-text);
    background: transparent;
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-medium);
    transition: background-color 0.15s ease, border-color 0.15s ease;
}

.panel-secondary-action:active {
    background-color: var(--color-press);
    border-color: var(--color-text-muted);
}


/* Add exercise picker -------------------------------------------------- */
.add-exercise-panel .exercise-buttons {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-s);
}

.add-exercise-panel .exercise-buttons button {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--spacing-m);
    width: 100%;
    min-height: 3rem;
    padding: var(--spacing-sm) var(--spacing-m);
    font-size: 1rem;
    color: var(--color-text);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-card);
    border-radius: var(--radius-medium);
    transition: background-color 0.15s ease;
}

.add-exercise-panel .exercise-buttons button:active {
    background-color: var(--color-surface-raised);
}

.exercise-pick-name {
    font-weight: 600;
    text-align: left;
}

.exercise-pick-meta {
    flex-shrink: 0;
    font-variant-numeric: tabular-nums;
    color: var(--color-text-muted);
    font-weight: 600;
}

/* Distinct bottom action for defining a free-form exercise - flagged with a
   dashed border rather than a color, matching the "custom 1RM" treatment. */
.add-exercise-panel .exercise-pick-custom {
    margin-top: var(--spacing-s);
    border-style: dashed;
    border-color: var(--color-border-strong);
    color: var(--color-text);
}

.add-exercise-panel .exercise-pick-custom .exercise-pick-name {
    color: var(--color-text);
}

.add-exercise-panel .exercise-pick-custom .exercise-pick-meta {
    color: var(--color-text-muted);
    font-weight: 500;
}

/* Panel body copy for the manual - scoped so it only applies inside .manual-panel
   and does not bleed into other panels. */
.manual-content {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-s);
    line-height: 1.6;
    color: var(--color-text);
}

.manual-content h2 {
    margin: var(--spacing-l) 0 0;
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--color-highlight);
    border-bottom: 1px solid var(--color-border);
    padding-bottom: 0.35rem;
}

.manual-content h2:first-child {
    margin-top: 0;
}

.manual-content h3 {
    margin: var(--spacing-m) 0 0;
    font-size: 1rem;
    font-weight: 700;
    color: var(--color-text-muted);
}

.manual-content p {
    margin: 0;
    font-size: 0.98rem;
    color: var(--color-text);
}

.manual-content ul,
.manual-content ol {
    margin: 0 0 0 var(--spacing-l);
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.manual-content li {
    font-size: 0.98rem;
    color: var(--color-text);
    padding-left: 0.2rem;
}

.manual-content strong {
    color: var(--color-highlight);
    font-weight: 700;
}

.manual-content code {
    font-family: monospace;
    font-size: 0.92em;
    color: var(--color-text);
    background: var(--color-surface-raised);
    padding: 1px 5px;
    border-radius: 4px;
}

/* ======================== 9c · Settings panel ============================ */
.settings-list {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-s);
}

.settings-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--spacing-m);
    padding: var(--spacing-sm) var(--spacing-m);
    border-radius: var(--radius-medium);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-card);
}

/* Stacked variant for rows whose control is too wide to sit beside the
   label on one line (the theme swatch grid below). */
.settings-row--stacked {
    flex-direction: column;
    align-items: stretch;
    gap: var(--spacing-sm);
}

/* Theme picker - a 2x2 grid of swatches rather than the segmented-pill
   pattern used for kg/lb, since a visual choice like this needs an actual
   color preview rather than just a text label. */
.theme-swatch-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--spacing-s);
    width: 100%;
}

.theme-swatch {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--spacing-s);
    padding: var(--spacing-sm);
    border-radius: var(--radius-medium);
    border: 1px solid var(--color-border);
    background-color: transparent;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

/* Selected state uses the currently-active theme's own accent, so the
   indicator feels integrated with whichever theme is live right now. */
.theme-swatch[aria-checked="true"] {
    border-color: var(--color-accent);
    background-color: var(--color-accent-fill);
}

.theme-swatch-preview {
    position: relative;
    display: block;
    width: 100%;
    height: 40px;
    border-radius: var(--radius-small);
    border: 1px solid rgba(255, 255, 255, 0.08);
}

.theme-swatch-dot {
    position: absolute;
    bottom: 6px;
    right: 6px;
    width: 14px;
    height: 14px;
    border-radius: 50%;
}

.theme-swatch-label {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--color-text);
}

/* Per-theme literal preview colors - deliberately NOT driven by the active
   [data-theme] tokens, since every swatch must always show what ITS OWN
   theme looks like, regardless of which theme is currently applied. */
.theme-swatch--mono .theme-swatch-preview { background-color: rgb(17, 17, 19); }
.theme-swatch--mono .theme-swatch-dot { background-color: rgb(198, 241, 53); }

.theme-swatch--indigo .theme-swatch-preview { background-color: rgb(18, 19, 24); }
.theme-swatch--indigo .theme-swatch-dot { background-color: rgb(99, 102, 241); }

.theme-swatch--graphite .theme-swatch-preview { background-color: rgb(23, 20, 18); }
.theme-swatch--graphite .theme-swatch-dot { background-color: rgb(255, 107, 74); }

.theme-swatch--light .theme-swatch-preview {
    background-color: rgb(255, 255, 255);
    border-color: rgba(0, 0, 0, 0.12);
}
.theme-swatch--light .theme-swatch-dot { background-color: rgb(79, 70, 229); }

.settings-row-label {
    font-size: 1rem;
    color: var(--color-text);
}

/* Custom role="switch" control - no native checkbox chrome appears anywhere
   else in the app, so this matches the pill-button language instead. */
.settings-toggle {
    flex-shrink: 0;
    justify-content: flex-start;
    width: 3rem;
    height: 1.7rem;
    min-height: 0;
    padding: 3px;
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    background-color: var(--color-track);
    transition: background-color 0.2s ease;
}

.settings-toggle[aria-checked="true"] {
    background-color: var(--color-accent);
}

.settings-toggle-knob {
    width: 1.25rem;
    height: 1.25rem;
    border-radius: 50%;
    background-color: var(--color-text);
    transition: transform 0.2s ease;
}

.settings-toggle[aria-checked="true"] .settings-toggle-knob {
    transform: translateX(1.3rem);
}

/* Custom role="radiogroup" control for exclusive choices (e.g. kg vs lb) -
   a two-segment pill sitting on the same track styling as settings-toggle. */
.settings-segmented {
    flex-shrink: 0;
    display: flex;
    gap: 2px;
    padding: 3px;
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    background-color: var(--color-track);
}

.settings-segmented-btn {
    min-height: 0;
    padding: 0.25rem 0.85rem;
    border: none;
    border-radius: var(--radius-pill);
    background-color: transparent;
    color: var(--color-text-muted, var(--color-text));
    font-size: 0.875rem;
    font-weight: 600;
    line-height: 1.5;
    cursor: pointer;
    transition: background-color 0.2s ease, color 0.2s ease;
}

.settings-segmented-btn[aria-checked="true"] {
    background-color: var(--color-accent);
    color: var(--color-text-dark);
}

/* ======================== 9d · Reorder panel ============================= */
.reorder-list {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-s);
    list-style: none;
    margin: 0;
    padding: 0;
}

.reorder-item {
    display: flex;
    align-items: center;
    gap: var(--spacing-m);
    padding: var(--spacing-sm) var(--spacing-m);
    border-radius: var(--radius-medium);
    background-color: var(--color-surface);
    box-shadow: var(--shadow-card);
    /* The dragged item is repositioned with `transform` (see enableDragReorder()
       in ui.js) - `position: relative` makes that a no-op for layout while
       letting `z-index` lift it above its neighbors while dragging. */
    position: relative;
}

.reorder-item:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
}

.reorder-item.dragging {
    z-index: 5;
    background-color: var(--color-surface-raised);
    box-shadow: var(--shadow-raised);
}

/* A large, dedicated grab target - dragging only starts from here (see
   enableDragReorder()), so `touch-action: none` is scoped to just this
   handle rather than the whole row, leaving panel scrolling untouched
   everywhere else. */
.reorder-handle {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 2.4rem;
    height: 2.4rem;
    margin: -0.5rem;
    color: var(--color-text-muted);
    cursor: grab;
    touch-action: none;
}

.reorder-item.dragging .reorder-handle {
    cursor: grabbing;
    color: var(--color-text);
}

.reorder-handle svg {
    width: 22px;
    height: 22px;
}

.reorder-name {
    font-size: 1rem;
    color: var(--color-text);
}

/* ==================== 9e · Data tools (backup/export) =================== */
#data-tools {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--spacing-l);
    width: 100%;
}

#data-tools .button-group {
    width: 80%;
    max-width: 420px;
    gap: var(--spacing-m);
}

#data-tools .button-group button {
    flex: 1 1 0;
    min-width: 100px;
}

/* Restore is the one destructive/high-stakes action in this group - flag it
   the same way Discard is flagged elsewhere, so it doesn't read as identical
   in weight to the plain export actions beside it. */
#import-backup-btn {
    color: var(--color-danger);
    border-color: rgba(248, 113, 113, 0.4);
}

#import-backup-btn:active {
    background-color: rgba(248, 113, 113, 0.14);
}

/* ======================= 10 · Install and update ======================= */
#workout-controls {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
}

#installButton {
    width: 90%;
    max-width: 300px;
    min-height: 48px;
    font-size: 1.2rem;
    font-weight: bold;
    color: var(--color-text-dark);
    background-color: var(--color-accent);
    border-radius: var(--radius-medium);
}

.install-instructions {
    max-width: 300px;
    padding: var(--spacing-m);
    text-align: center;
    font-size: 1rem;
    line-height: 1.5;
    color: var(--color-text);
}

#updateButton {
    position: absolute;
    top: calc(var(--safe-area-top) + 4px);
    right: var(--spacing-m);
    min-height: 2rem;
    padding: var(--spacing-s) var(--spacing-m);
    font-size: 0.9rem;
    font-weight: bold;
    color: var(--color-text-dark);
    background-color: var(--color-yes);
    border-radius: var(--radius-small);
}

/* ========================== 11 · Fullscreen ============================ */
body.fullscreen-mode #workout-header,
body.fullscreen-mode #rest-timer {
    display: none;
}

body.fullscreen-mode main {
    padding-top: var(--safe-area-top);
}

/* ========================== 12 · Animations ============================ */
@keyframes opacityPulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.5; }
}

/* Content panel: soft rise + fade from just below its resting position. */
@keyframes panelIn {
    from { opacity: 0; transform: translateY(12px); }
    to { opacity: 1; transform: translateY(0); }
}


@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}

/* ========================== 13 · Responsive ============================ */
/* The exercise card's stacked-sidebar-below-sets layout (see .exercise-controls
   above) is universal now, not a narrow-screen fallback, so there is nothing
   exercise-card-specific left to override here. */

@media (min-width: 481px) {
    main {
        max-width: var(--container-width);
    }
}
