/* SpinBike 2026 — adaptive dark/light, mobile-first.
   Token-driven: every component reads from :root variables.
   Dark palette is the default; light palette overrides via prefers-color-scheme. */

/* ========================================================================
   1. TOKENS — dark (default)
   ======================================================================== */

:root {
    /* Spacing */
    --s-1: 4px;
    --s-2: 8px;
    --s-3: 12px;
    --s-4: 16px;
    --s-5: 24px;
    --s-6: 32px;
    --s-7: 48px;

    /* Radius */
    --r-sm:   8px;
    --r:      12px;
    --r-lg:   16px;
    --r-pill: 24px;

    /* Font scale */
    --fs-xs:   12px;
    --fs-sm:   14px;
    --fs-base: 16px;
    --fs-md:   18px;
    --fs-lg:   22px;
    --fs-xl:   28px;
    --fs-2xl:  36px;

    /* Touch targets */
    --tap-min: 44px;
    --tap-md:  48px;
    --tap-lg:  56px;

    /* Motion */
    --dur-fast:   120ms;
    --dur:        180ms;
    --dur-slow:   280ms;
    --ease-out:   cubic-bezier(0.2, 0, 0, 1);
    --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);

    /* Page padding (responsive via media query below) */
    --page-pad: var(--s-4);

    /* Dark palette */
    --bg:            #0a0b0e;
    --surface:       #13151a;
    --surface-2:     #1b1e25;
    --surface-3:     #252932;
    --border:        #2a2e37;
    --border-strong: #3c4250;
    --text:          #ededf2;
    --text-muted:    #a8acb5;
    --text-dim:      #72767f;
    --brand:         #60a5fa;
    --brand-tint:    rgba(96, 165, 250, 0.14);
    --danger:        #f87171;
    --info:          color-mix(in srgb, var(--brand) 90%, black);
    --pass:          #fbbf24;
    --shadow:        0 4px 12px rgba(0, 0, 0, 0.4);
    --shadow-lg:     0 12px 36px rgba(0, 0, 0, 0.55);
    --shadow-sm:     0 1px 2px rgba(0, 0, 0, 0.35);
    --ring:          0 0 0 3px var(--brand-tint);

    /* Legacy semantic tokens — remapped to new canonical tokens.
       Kept because they are consumed by the colour-mix() expressions
       in component rules (hover states, alerts, pass banner, etc.). */
    --primary:        var(--brand);
    --primary-hover:  color-mix(in srgb, var(--brand) 85%, black);
    --primary-fg:     #fff;
    --danger-hover:   color-mix(in srgb, var(--danger) 85%, black);
    --danger-soft:    color-mix(in srgb, var(--danger) 20%, var(--surface));
    --danger-fg:      var(--danger);
    --warn-soft:      color-mix(in srgb, var(--danger) 18%, var(--surface));
    --warn-border:    color-mix(in srgb, var(--danger) 45%, var(--surface));
    --warn-fg:        var(--danger);
    --success-soft:   color-mix(in srgb, var(--brand) 18%, var(--surface));
    --success-border: color-mix(in srgb, var(--brand) 40%, var(--surface));
    --success-fg:     var(--brand);
    --info-soft:      color-mix(in srgb, var(--info) 22%, var(--surface));
    --info-border:    color-mix(in srgb, var(--info) 50%, var(--surface));
    --info-soft-fg:   var(--info);
    --info-hover:     color-mix(in srgb, var(--info) 85%, black);
    --info-fg:        #fff;
    --pass-hover:     color-mix(in srgb, var(--pass) 85%, black);
    --pass-fg:        #1a1306;

    /* Legacy radius aliases */
    --radius-sm: var(--r-sm);
    --radius:    var(--r);
    --radius-lg: var(--r-lg);

    /* Legacy surface aliases */
    --surface-alt:   var(--surface-2);
    --surface-inset: var(--surface-2);

    /* Legacy ease alias */
    --ease: var(--ease-out);
}

/* ========================================================================
   2. TOKENS — light override
   ======================================================================== */

@media (prefers-color-scheme: light) {
    :root {
        --bg:            #f6f7f9;
        --surface:       #ffffff;
        --surface-2:     #f1f3f7;
        --surface-3:     #e5e8ef;
        --border:        #dfe2ea;
        --border-strong: #c1c6d2;
        --text:          #14161b;
        --text-muted:    #545a67;
        --text-dim:      #8a8f9b;
        --brand:         #2563eb;
        --brand-tint:    rgba(37, 99, 235, 0.10);
        --danger:        #dc2626;
        --info:          color-mix(in srgb, var(--brand) 90%, black);
        --pass:          #d97706;
        --shadow:        0 2px 8px rgba(13, 20, 45, 0.07);
        --shadow-lg:     0 16px 48px rgba(13, 20, 45, 0.12);
        --shadow-sm:     0 1px 2px rgba(13, 20, 45, 0.05);
    }
}

/* ========================================================================
   3. GLOBAL RESETS
   ======================================================================== */

*, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html, body {
    height: 100%;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
    background: var(--bg);
    color: var(--text);
    line-height: 1.5;
    font-size: var(--fs-base);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    accent-color: var(--brand);
}

a { color: var(--brand); text-decoration: none; }
a:hover { text-decoration: underline; }

::selection { background: var(--brand-tint); color: var(--text); }

:focus-visible {
    outline: none;
    box-shadow: var(--ring);
    border-radius: var(--r-sm);
}

/* ========================================================================
   4. LAYOUT — app shell, page, page-title
   ======================================================================== */

.app-shell {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

.page {
    flex: 1;
    padding: var(--s-5) var(--page-pad);
    max-width: 960px;
    margin: 0 auto;
    width: 100%;
}

.page-title {
    font-size: var(--fs-xl);
    font-weight: 700;
    margin-bottom: var(--s-5);
    letter-spacing: -0.01em;
}

/* ========================================================================
   5. NAVBAR
   ======================================================================== */

.navbar {
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    padding: var(--s-3) var(--s-4);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-3);
    position: sticky;
    top: 0;
    z-index: 100;
    box-shadow: var(--shadow-sm);
}

.navbar-brand {
    font-size: var(--fs-lg);
    font-weight: 700;
    color: var(--brand);
    letter-spacing: -0.01em;
    text-decoration: none;
}

.navbar-brand:hover { text-decoration: none; opacity: 0.9; }

.navbar-links {
    display: flex;
    gap: var(--s-1);
    align-items: center;
    flex-wrap: wrap;
}

.navbar-links a,
.navbar-links button {
    color: var(--text-muted);
    text-decoration: none;
    padding: var(--s-2) var(--s-3);
    border-radius: var(--r);
    font-size: var(--fs-sm);
    font-weight: 500;
    background: none;
    border: 1px solid transparent;
    cursor: pointer;
    font-family: inherit;
    transition: background var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out);
}

.navbar-links a:hover,
.navbar-links button:hover {
    background: var(--surface-2);
    color: var(--text);
}

.navbar-links a.active {
    color: var(--brand);
    background: var(--brand-tint);
    border-color: color-mix(in srgb, var(--brand) 25%, transparent);
}

.navbar-user {
    font-size: var(--fs-xs);
    color: var(--text-dim);
    margin-right: var(--s-2);
    font-weight: 500;
}

.lang-toggle {
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    color: var(--text-muted);
    padding: var(--s-1) var(--s-2);
    border-radius: var(--r-sm);
    font-size: var(--fs-xs);
    font-family: inherit;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    cursor: pointer;
    transition: color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out);
}

.lang-toggle:hover {
    color: var(--text);
    border-color: var(--brand);
}

/* ========================================================================
   6. BUTTONS — canonical system
   ======================================================================== */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s-2);
    min-height: var(--tap-min);
    padding: 0 var(--s-4);
    font-size: var(--fs-sm);
    font-weight: 500;
    line-height: 1;
    border-radius: var(--r);
    border: 1px solid var(--border-strong);
    background: var(--surface-2);
    color: var(--text);
    cursor: pointer;
    font-family: inherit;
    text-decoration: none;
    white-space: nowrap;
    /* Defensive reset for #152's reported text misalignment — some mobile
       WebKit builds apply native button chrome (padding/line-height) on
       top of our own unless appearance is explicitly reset. Not
       reproducible in Chromium; unverified on a real iOS device. */
    appearance: none;
    -webkit-appearance: none;
    transition: background var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out),
                transform 0.05s var(--ease-out);
}

.btn:hover:not(:disabled) {
    background: var(--surface-3);
    border-color: var(--border-strong);
}

.btn:active:not(:disabled) { transform: translateY(1px); }

.btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Size variants */
.btn--hero    { min-height: var(--tap-lg); padding: 0 var(--s-5); font-size: var(--fs-md); border-radius: var(--r-lg); }
.btn--compact { min-height: 36px; padding: 0 var(--s-3); font-size: var(--fs-xs); border-radius: var(--r-sm); }
.btn--block   { width: 100%; }

/* Colour variants */
.btn--primary {
    background: var(--brand);
    border-color: var(--brand);
    color: #0a1f10;
    font-weight: 600;
}
.btn--primary:hover:not(:disabled) {
    background: #1fb357;
    border-color: #1fb357;
}

.btn--danger {
    background: transparent;
    border-color: var(--danger);
    color: var(--danger);
}
.btn--danger:hover:not(:disabled) {
    background: var(--danger);
    color: #fff;
}

.btn--ghost {
    background: transparent;
    border-color: var(--border);
    color: var(--text-muted);
}
.btn--ghost:hover:not(:disabled) {
    background: var(--surface-2);
    color: var(--text);
}

.btn--info {
    background: var(--info);
    border-color: var(--info);
    color: var(--info-fg);
    font-weight: 600;
}
.btn--info:hover:not(:disabled) {
    background: var(--info-hover);
    border-color: var(--info-hover);
}

/* Soft-tinted variants — same hue family as their solid siblings, lower
   saturation. Used for the right-side / less-frequent action in a paired row
   (Charge | Topup, Visit Fitness | Visit Spinning) so the more-used button
   on the left visually leads without making the right one disappear. */
.btn--primary-soft {
    background: var(--success-soft);
    border-color: var(--success-border);
    color: var(--success-fg);
    font-weight: 600;
}
.btn--primary-soft:hover:not(:disabled) {
    background: color-mix(in srgb, var(--brand) 28%, var(--surface));
    border-color: color-mix(in srgb, var(--brand) 55%, var(--surface));
}

.btn--info-soft {
    background: var(--info-soft);
    border-color: var(--info-border);
    color: var(--info-soft-fg);
    font-weight: 600;
}
.btn--info-soft:hover:not(:disabled) {
    background: color-mix(in srgb, var(--info) 26%, var(--surface));
    border-color: color-mix(in srgb, var(--info) 60%, var(--surface));
}


/* ========================================================================
   7. FORMS
   ======================================================================== */

.form-group {
    margin-bottom: var(--s-4);
}

.form-group label {
    display: block;
    margin-bottom: var(--s-1);
    font-size: var(--fs-xs);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.form-help {
    display: block;
    margin-top: var(--s-1);
    font-size: var(--fs-xs);
    color: var(--text-muted);
}

.form-control {
    width: 100%;
    min-height: var(--tap-min);
    padding: var(--s-3);
    background: var(--surface-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--r);
    color: var(--text);
    font-size: var(--fs-base);
    font-family: inherit;
    transition: border-color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out);
}

.form-control::placeholder { color: var(--text-dim); }

.form-control:focus {
    outline: none;
    border-color: var(--brand);
    box-shadow: var(--ring);
}

.form-control:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

select.form-control {
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23a8acb5' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right var(--s-3) center;
    padding-right: var(--s-6);
}

.inline-form {
    display: flex;
    gap: var(--s-2);
    align-items: flex-end;
    flex-wrap: wrap;
}

.inline-form .form-group {
    margin-bottom: 0;
    flex: 1;
    min-width: 140px;
}

/* ========================================================================
   8. CARDS
   ======================================================================== */

.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: var(--s-4);
    margin-bottom: var(--s-3);
    box-shadow: var(--shadow-sm);
}

.card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: var(--s-3);
    padding-bottom: var(--s-3);
    border-bottom: 1px solid var(--border);
}

.card-title {
    display: flex;
    align-items: baseline;
    gap: var(--s-2);
    flex-wrap: wrap;
}

.card-title__name {
    font-size: var(--fs-xl);
    font-weight: 700;
    letter-spacing: -0.01em;
    line-height: 1.15;
}

.card-title__barcode {
    font-size: var(--fs-base);
    font-weight: 500;
    color: var(--text-muted);
    font-family: ui-monospace, monospace;
}

.card-title__last-visit {
    font-size: 0.875rem;
    color: var(--text-muted);
    margin-top: 0.125rem;
}

/* Card balance — prominent credit display (used in Task 13) */
.card-balance {
    display: flex;
    align-items: baseline;
    gap: 4px;
    margin: var(--s-2) 0 var(--s-4);
}

.card-balance__num {
    font-size: var(--fs-2xl);
    font-weight: 700;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
}

.card-balance__unit {
    font-size: var(--fs-lg);
    color: var(--text-muted);
}

.card-balance--negative .card-balance__num { color: var(--danger); }

.action-row        { display: flex; gap: var(--s-3); }
.action-row > *    { flex: 1; }


/* ========================================================================
   10. DAY PICKER
   ======================================================================== */

.day-picker {
    display: flex;
    gap: var(--s-1);
    margin-bottom: var(--s-4);
    overflow-x: auto;
    padding-bottom: var(--s-1);
    scrollbar-width: thin;
}

.day-btn {
    flex: 1;
    min-width: 64px;
    min-height: 72px;
    padding: var(--s-2);
    border-radius: var(--r);
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--text-muted);
    text-align: center;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out);
    font-family: inherit;
    font-size: var(--fs-sm);
}

.day-btn:hover { background: var(--surface-2); color: var(--text); }

.day-btn.active {
    background: var(--brand-tint);
    color: var(--brand);
    border-color: color-mix(in srgb, var(--brand) 40%, transparent);
    font-weight: 600;
}

.day-btn .day-name {
    display: block;
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    opacity: 0.85;
}

.day-btn .day-num {
    display: block;
    font-size: var(--fs-lg);
    font-weight: 700;
    margin-top: 2px;
}

/* ========================================================================
   11. SEGMENTED CONTROL (.seg) — new primitive
   ======================================================================== */

.seg {
    display: flex;
    background: var(--surface-2);
    border-radius: var(--r);
    padding: 4px;
    gap: 2px;
}

.seg__item {
    flex: 1;
    min-height: 40px;
    background: transparent;
    color: var(--text-muted);
    border: 0;
    border-radius: calc(var(--r) - 4px);
    font-family: inherit;
    font-size: var(--fs-sm);
    font-weight: 500;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out);
}

.seg__item:hover { color: var(--text); }

.seg__item[aria-selected="true"] {
    background: var(--surface);
    color: var(--text);
    box-shadow: var(--shadow);
}

.seg-body { padding-top: var(--s-3); }


/* ========================================================================
   13. BADGES
   ======================================================================== */

.badge {
    display: inline-flex;
    align-items: center;
    min-height: 22px;
    padding: 2px var(--s-2);
    border-radius: var(--r-pill);
    font-size: var(--fs-xs);
    font-weight: 600;
    letter-spacing: 0.02em;
    background: var(--surface-3);
    color: var(--text-muted);
    border: 1px solid transparent;
    white-space: nowrap;
}

.badge--full      { background: rgba(248, 113, 113, 0.14); color: var(--danger); }
.badge--cancelled { background: var(--surface-3); color: var(--text-dim); }
.badge--voided    { background: rgba(248, 113, 113, 0.14); color: var(--danger); }


/* ========================================================================
   14. ALERTS
   ======================================================================== */

.alert {
    padding: var(--s-3) var(--s-4);
    border-radius: var(--r);
    margin-bottom: var(--s-4);
    font-size: var(--fs-sm);
    border: 1px solid transparent;
    display: flex;
    align-items: center;
    gap: var(--s-2);
}

.alert-error {
    background: var(--danger-soft);
    border-color: var(--warn-border);
    color: var(--danger);
}

.alert-success {
    background: var(--success-soft);
    border-color: var(--success-border);
    color: var(--brand);
}

.alert-info {
    background: var(--info-soft);
    border-color: var(--info-border);
    color: var(--info);
}

/* #234: staff duplicate-visit confirm — a real warning (not yet an error,
   not a routine info), so it gets its own combo of the existing tokens
   rather than reusing .alert-error's exact look. Stacks message + action
   buttons vertically, unlike the icon+text .alert base (align-items: center
   still centers the single wrapper div fine). */
.alert-warning {
    background: var(--warn-soft);
    border-color: var(--warn-border);
    color: var(--danger);
    align-items: stretch;
}

/* ========================================================================
   15. TABLES
   ======================================================================== */

table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--fs-sm);
}

table th {
    text-align: left;
    padding: var(--s-2) var(--s-3);
    background: var(--surface-2);
    color: var(--text-dim);
    border-bottom: 1px solid var(--border);
    font-weight: 600;
    font-size: var(--fs-xs);
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

table td {
    padding: var(--s-2) var(--s-3);
    border-bottom: 1px solid var(--border);
}

table tbody tr:hover {
    background: var(--surface-2);
}

table tbody tr:last-child td {
    border-bottom: none;
}


/* ========================================================================
   17. SHEET — bottom sheet on mobile, centered modal >= 768px (NEW)
   ======================================================================== */

.sheet-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
    z-index: 200;
    animation: sheet-backdrop-in var(--dur) var(--ease-out);
}

.sheet {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    background: var(--surface);
    border-top-left-radius: var(--r-lg);
    border-top-right-radius: var(--r-lg);
    padding: var(--s-3) var(--s-4) var(--s-5);
    z-index: 210;
    box-shadow: var(--shadow-lg);
    max-height: 90vh;
    overflow-y: auto;
    animation: sheet-in var(--dur-slow) var(--ease-spring);
}

.sheet__grab {
    width: 44px;
    height: 4px;
    border-radius: 2px;
    background: var(--border-strong);
    margin: 0 auto var(--s-3);
}

.sheet__title {
    font-size: var(--fs-md);
    font-weight: 600;
    margin-bottom: var(--s-4);
    letter-spacing: -0.005em;
}

.sheet__body > * + * { margin-top: var(--s-3); }

.sheet__actions {
    display: flex;
    gap: var(--s-2);
    padding-top: var(--s-4);
    margin-top: var(--s-4);
    border-top: 1px solid var(--border);
}

.sheet__actions .btn { flex: 1; }

/* User-name line at the top of the More sheet (AdaptiveNav).
   Visually distinct from buttons below; matches sheet body padding. */
.more-sheet__user {
    font-size: var(--fs-md);
    color: var(--text);
    font-weight: 600;
    padding: var(--s-2) 0;
    border-bottom: 1px solid var(--border);
    margin-bottom: var(--s-3);
}

@keyframes sheet-in {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
}

@keyframes modal-in {
    from { transform: translate(-50%, -45%); opacity: 0; }
    to   { transform: translate(-50%, -50%); opacity: 1; }
}

@keyframes sheet-backdrop-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* ========================================================================
   18. GROUP + LIST-ROW — unified list primitive (NEW)
   ======================================================================== */

.group {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r);
    overflow: hidden;
    margin-bottom: var(--s-4);
}

/* .list-row: canonical flex row primitive. */
.list-row {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    padding: var(--s-3) var(--s-4);
    min-height: 56px;
    border-top: 1px solid var(--border);
}

.list-row:first-child { border-top: none; }

.list-row--interactive {
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-out);
}

.list-row--interactive:hover { background: var(--surface-2); }

.list-row__main { flex: 1; min-width: 0; }
.list-row__title { font-weight: 500; color: var(--text); }
.list-row__sub { font-size: var(--fs-sm); color: var(--text-muted); margin-top: 2px; }
.list-row__amount { font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.list-row__amount--pos { color: var(--brand); }
.list-row__amount--neg { color: var(--danger); }
.list-row__end { display: flex; align-items: center; gap: var(--s-2); flex-shrink: 0; }

.list-row__accent {
    width: 3px;
    align-self: stretch;
    margin: -12px 0 -12px calc(-1 * var(--s-4));
    background: var(--border-strong);
}

.list-row__accent--available { background: var(--brand); }
.list-row__accent--booked    { background: var(--info); }
.list-row__accent--full      { background: var(--danger); }
.list-row__accent--cancelled { background: var(--border-strong); opacity: 0.5; }

.list-row--past      { opacity: 0.5; }
.list-row--cancelled { opacity: 0.55; }

/* ========================================================================
   19. PASS BANNER
   ======================================================================== */

.pass-banner {
    padding: var(--s-4);
    border-radius: var(--r);
    margin-bottom: var(--s-4);
    border: 1px solid transparent;
}

.pass-banner-ok {
    background: var(--success-soft);
    color: var(--brand);
    border-color: var(--success-border);
}

.pass-banner-expired {
    background: var(--warn-soft);
    color: var(--danger);
    border-color: var(--warn-border);
}

.pass-banner__line {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-2);
    font-size: var(--fs-md);
    font-weight: 600;
    letter-spacing: -0.005em;
}

.pass-banner__line-text {
    flex: 1;
    min-width: 0;
}

.pass-banner__edit-btn {
    background: transparent;
    border: 0;
    cursor: pointer;
    font-size: var(--fs-md);
    line-height: 1;
    padding: var(--s-1);
    color: inherit;
    opacity: 0.7;
}
.pass-banner__edit-btn:hover { opacity: 1; }

/* ========================================================================
   20. CARD SEARCH
   ======================================================================== */

.search-result {
    padding: var(--s-2) var(--s-3);
    border-bottom: 1px solid var(--border);
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-out);
}

.search-result:hover {
    background: var(--surface-2);
}

.search-result-active {
    background: var(--surface-2);
    border-left: 2px solid var(--brand);
    padding-left: calc(var(--s-3) - 2px);
}

/* ========================================================================
   21. TRANSACTION ROW STATES
   ======================================================================== */

/* Negative card credit — staff spots overdrawn cards */
.credit-negative {
    color: var(--danger);
    font-weight: 700;
}

/* Free visits logged under an active pass */
.txn-row-visit {
    color: var(--info);
}

.txn-row-visit td {
    font-style: italic;
}

/* Soft-deleted (voided) transaction rows — token-based, no light-theme hacks */
.txn-row--voided       { color: var(--text-dim); opacity: 0.75; }

.txn-voided-tag {
    font-size: var(--fs-xs);
    color: var(--danger);
    margin-left: 6px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* ========================================================================
   22. PARTICIPANTS LIST
   ======================================================================== */

.participants-list {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--r);
    padding: var(--s-2) var(--s-3);
    font-size: var(--fs-sm);
    color: var(--text-muted);
}

/* Walkin (staff schedule) */
.walkin-results {
    list-style: none;
    padding: 0;
    margin: var(--s-2) 0 0 0;
    display: flex;
    flex-direction: column;
    gap: var(--s-1);
}

.walkin-row .btn {
    width: 100%;
    text-align: left;
}

/* ========================================================================
   24. EMPTY / LOADING
   ======================================================================== */

.empty-state {
    text-align: center;
    padding: var(--s-6) var(--s-4);
    color: var(--text-dim);
    font-size: var(--fs-sm);
}

.spinner {
    display: inline-block;
    width: 18px;
    height: 18px;
    border: 2px solid var(--border);
    border-top-color: var(--brand);
    border-radius: 50%;
    animation: spin 0.7s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* ========================================================================
   25. UTILITIES
   ======================================================================== */

.text-center  { text-align: center; }
.text-muted   { color: var(--text-muted); }

.mt-1 { margin-top: var(--s-2); }
.mt-2 { margin-top: var(--s-4); }
.mt-3 { margin-top: var(--s-5); }
.mb-1 { margin-bottom: var(--s-2); }
.mb-2 { margin-bottom: var(--s-4); }

.flex            { display: flex; }
.gap-1           { gap: var(--s-2); }
.items-center    { align-items: center; }
.justify-between { justify-content: space-between; }
.hidden          { display: none; }

/* ========================================================================
   26. RESPONSIVE
   ======================================================================== */

@media (max-width: 540px) {
    .page {
        padding: var(--s-4) var(--s-3);
    }

    .navbar {
        flex-wrap: wrap;
        gap: var(--s-2);
    }

    .navbar-links {
        width: 100%;
        justify-content: center;
    }

    .inline-form { flex-direction: column; align-items: stretch; }
    .inline-form .form-group { min-width: 0; }

    .page-title { font-size: var(--fs-lg); }
}

@media (min-width: 768px) {
    /* Widen page padding on desktop */
    :root { --page-pad: var(--s-5); }

    /* Sheet becomes a centered modal on desktop */
    .sheet {
        position: fixed;
        left: 50%;
        top: 50%;
        right: auto;
        bottom: auto;
        transform: translate(-50%, -50%);
        width: min(480px, 90vw);
        border-radius: var(--r-lg);
        animation: modal-in var(--dur-slow) var(--ease-spring);
    }

    .sheet__grab { display: none; }
}

/* ========================================================================
   27. REDUCED MOTION
   ======================================================================== */

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

/* ========================================================================
   28. PAGE-FORM — centered narrow form layout (login, link-card)
   ======================================================================== */

.page-form { max-width: 420px; margin: 0 auto; }
.page-form .form-group { margin-bottom: var(--s-4); }

/* ========================================================================
   29. DASHBOARD HELPERS — small named classes hoisted from inline styles
   ======================================================================== */

/* Vertical rhythm helpers — finer-grained than .mt-1/-2/-3 (which use s-2/4/5). */
.stack-12 { margin-top: var(--s-3); }       /* 12px */
.stack-16 { margin-top: var(--s-4); }       /* 16px */

/* Chip / button row that wraps. */
.chip-row { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.chip-row--spaced { margin-bottom: var(--s-2); }
/* #32d: log-visit class buttons read bigger and bolder than the default
   --compact chip. Applied via .chip-row--readable on the parent so any
   future quick-action chip row can opt in. */
.chip-row--readable .btn {
    font-size: var(--fs-md);
    font-weight: 700;
    padding: var(--s-2) var(--s-4);
}

/* Card header layout: title block grows, close button stays compact. */
.card-header__main { flex: 1; min-width: 0; }

/* Pass banner sits inside .group; cancel its own border/radius/margin
   so the .group wrapper provides the visual container. */
.pass-banner--in-group {
    margin-bottom: 0;
    border-radius: 0;
    border: none;
}

/* Search results list (renders before the action panel). */
.search-input--lg { font-size: var(--fs-md); padding: var(--s-3); }
.search-results-list {
    margin-top: var(--s-2);
    border-top: 1px solid var(--border);
}
.search-result-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: var(--s-3);
    border-bottom: 1px solid var(--border);
    cursor: pointer;
    gap: var(--s-2);
}
.search-result--negative {
    border-left: 3px solid var(--danger);
    background: var(--danger-soft);
}
.search-result-name   { font-weight: 600; }
.search-result-meta   { font-size: var(--fs-sm); color: var(--text-muted); }
.search-result-last-visit { font-size: var(--fs-sm); color: var(--text-muted); }
.search-result-credit { font-weight: 600; white-space: nowrap; }

/* #235: "last visit was TODAY" — search dropdown row + card panel header
   share this loud highlight so staff can't miss it (the exact signal that
   helps avoid a duplicate visit, #234). Same danger+bold treatment as
   .credit-negative for visual consistency across "needs attention" states. */
.visited-today {
    color: var(--danger);
    font-weight: 700;
}
.search-hint          { font-size: var(--fs-sm); color: var(--text-muted); }
.badge--inline        { margin-left: var(--s-2); }
.badge--lock          { margin-left: 0.35rem; font-size: 0.9rem; opacity: 0.85; }

/* ─────────── Negative-balance idle list ─────────── */
.negative-balance-list__heading {
    font-size: 0.95rem;
    margin: 0 0 0.5rem 0;
    color: var(--text-muted);
}
.negative-balance-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-3);
    padding: var(--s-2) var(--s-1);
    cursor: pointer;
    border-bottom: 1px solid var(--border);
}
.negative-balance-row:last-child { border-bottom: none; }
.negative-balance-row:hover { background: var(--surface-2); }
.negative-balance-row__label {
    min-width: 0;
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 600;
}
.negative-balance-row__meta-inline {
    font-weight: 400;
    font-size: var(--fs-sm);
    color: var(--text-muted);
}
.negative-balance-row__credit {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

/* ═════════════════════════════════════════════════════════════
   Staff/CEO redesign (v0.10.0)
   ───────────────────────────────────────────────────────────── */

/* ─────────── Adaptive navigation ─────────── */
.app-shell { padding-bottom: calc(56px + env(safe-area-inset-bottom)); }

/* When AdaptiveNav is rendered (= staff/admin logged in), the user-identity
   controls (username, Logout, EN/SK) move into the AdaptiveNav 'More' sheet
   to avoid two redundant chromes carrying the same controls on desktop. The
   top navbar collapses to just the SpinBike brand wordmark on a single thin
   row. Customers and logged-out users keep the full top navbar (login,
   register, my/bookings etc.) — :has() doesn't match without .adaptive-nav. */
body:has(.adaptive-nav) .navbar-links { display: none; }

.adaptive-nav {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    justify-content: space-around;
    align-items: stretch;
    background: var(--surface);
    border-top: 1px solid var(--border);
    padding-bottom: env(safe-area-inset-bottom);
    z-index: 100;
}

.adaptive-nav__item {
    /* Mixed selector: applies to both <a> nav items and the <button> for
       'More'. The button-specific resets override OS-default chrome
       (background, border, font, cursor) so the button matches the <a>
       siblings visually. */
    background: none;
    border: none;
    cursor: pointer;
    font-family: inherit;
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: 56px;
    padding: 6px 4px;
    color: var(--text-muted);
    text-decoration: none;
    font-size: var(--fs-xs);
    font-weight: 500;
}

.adaptive-nav__item[aria-current="page"] {
    color: var(--brand);
    background: var(--brand-tint);
}

.adaptive-nav__icon {
    display: inline-flex;
    width: 22px;
    height: 22px;
    color: currentColor;
}
.adaptive-nav__icon svg { width: 100%; height: 100%; }
.adaptive-nav__label { letter-spacing: 0.02em; }

@media (min-width: 768px) {
    .app-shell { padding-bottom: 0; padding-left: 72px; }
    .adaptive-nav {
        top: 0;
        right: auto;
        bottom: 0;
        width: 72px;
        flex-direction: column;
        justify-content: flex-start;
        padding: var(--s-3) 0;
        border-top: none;
        border-right: 1px solid var(--border);
    }
    .adaptive-nav__item {
        flex: 0 0 auto;
        min-height: 64px;
        padding: 10px 4px;
    }
    .adaptive-nav__item[aria-current="page"] {
        border-radius: var(--r-sm);
        margin: 2px var(--s-2);
    }
}

/* ─────────── Date input (custom, replaces native <input type="date">) ─── */
.date-input {
    display: flex;
    align-items: stretch;
    gap: var(--s-2);
    flex-wrap: wrap;
}
.date-input__field {
    flex: 1 1 12ch;
    min-width: 12ch;
    font-variant-numeric: tabular-nums;
    text-align: center;
}
.date-input .btn--compact {
    flex: 0 0 auto;
}

/* ─────────── Reports page layout ─────────── */
.reports-page {
    padding: var(--s-4);
    max-width: 960px;
    margin: 0 auto;
}

/* ─────────── Reports date strip ─────────── */
.reports-date-strip {
    display: flex;
    flex-direction: column;
    gap: var(--s-2);
    margin-bottom: var(--s-4);
}

/* ─────────── KPI cards ─────────── */
.kpi-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--s-3);
    margin-bottom: var(--s-4);
}

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

.kpi-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r);
    padding: var(--s-4);
    min-height: 96px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: var(--s-2);
}

.kpi-card__label {
    font-size: var(--fs-xs);
    color: var(--text-dim);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.kpi-card__value {
    font-size: var(--fs-2xl);
    font-weight: 700;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

/* ─────────── Feed row colored dots ─────────── */
.feed-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
}

.feed-dot--charge { background: var(--danger); }
.feed-dot--topup  { background: var(--brand); }
.feed-dot--pass   { background: var(--info); }
.feed-dot--visit  { background: var(--info-soft); }
.feed-dot--voided { background: var(--text-dim); }

.list-row__note {
    margin-top: 2px;
    font-size: 0.85em;
    color: var(--text-muted, #888);
    font-style: italic;
}

.list-row__note-edit {
    display: flex;
    gap: 4px;
    margin-top: 4px;
    align-items: center;
}

.list-row__note-edit .form-control--inline {
    flex: 1;
    min-width: 0;
}

/* Per-row action icons (note edit, date edit, void) on the transactions
 * list. Stays horizontal so the row height does not balloon to fit a
 * vertical stack — see #85. Uses btn--icon (square 28px) instead of
 * btn--compact (text-sized) so all three glyphs render uniformly. */
.list-row__end--icons {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: var(--s-1);
    flex-shrink: 0;
}

.btn--icon {
    width: 28px;
    height: 28px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    font-size: var(--fs-sm);
}

/* ═════════════════════════════════════════════════════════════
   Build version label — sticky bottom-right, every route.
   Sits above the mobile bottom-nav (56px tall) so it doesn't
   overlap the nav strip; on desktop the nav becomes a left
   sidebar so the bottom-right corner is free.
   ───────────────────────────────────────────────────────────── */
.app-version {
    position: fixed;
    right: var(--s-3, 8px);
    bottom: calc(56px + env(safe-area-inset-bottom) + 4px);
    font-size: var(--fs-xs, 11px);
    color: var(--text-dim, #888);
    background: var(--surface-faint, rgba(0, 0, 0, 0.35));
    padding: 2px 6px;
    border-radius: 4px;
    pointer-events: none;
    z-index: 50;
    user-select: none;
}

@media (min-width: 768px) {
    .app-version { bottom: var(--s-3, 8px); }
}

/* ─── Per-card Overview tab (v0.13.18) ───────────────────────────── */
.stats-kpi {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 1rem;
}
.stats-kpi th,
.stats-kpi td {
    padding: 0.4rem 0.6rem;
    text-align: right;
    font-variant-numeric: tabular-nums;
}
.stats-kpi th:first-child,
.stats-kpi td:first-child {
    text-align: left;
    font-weight: 500;
}
.stats-kpi th {
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted, #888);
}

.stats-chart-title {
    font-size: 0.95rem;
    margin: 1rem 0 0.4rem;
    color: var(--text-muted, #555);
}
.stats-chart {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}
.stats-row {
    display: grid;
    grid-template-columns: 4.5rem 1fr 5rem;
    align-items: center;
    gap: 0.5rem;
    font-variant-numeric: tabular-nums;
    font-size: 0.9rem;
}
.stats-row__label {
    color: var(--text-muted, #666);
}
.stats-row__bar-wrap {
    background: var(--surface-2, #f0f0f0);
    border-radius: 3px;
    height: 0.85rem;
    overflow: hidden;
}
.stats-row__bar {
    height: 100%;
    background: var(--brand);
    border-radius: 3px;
    transition: width 200ms ease;
    min-width: 0;
}
.stats-row__value {
    text-align: right;
}

/* ---- Door open self-service (#92) ---------------------------------- */
.door-btn {
    width: 100%;
    min-height: 64px;
    border-radius: 12px;
    border: none;
    background: var(--color-primary, #2563eb);
    color: #fff;
    font-size: 1.1rem;
    font-weight: 600;
    position: relative;
    overflow: hidden;
    touch-action: none; /* required for pointer events on mobile */
    user-select: none;
    cursor: pointer;
    margin: 1rem 0;
}
.door-btn[disabled] { cursor: not-allowed; opacity: 0.7; }
.door-btn__label { position: relative; z-index: 1; }
.door-btn__icon { position: relative; z-index: 1; margin-right: 0.5rem; }
.door-btn__progress {
    position: absolute;
    inset: 0;
    background: rgba(255,255,255,0.25);
    pointer-events: none;
    z-index: 0;
}
.door-btn--idle, .door-btn--holding, .door-btn--firing { background: #2563eb; }
.door-btn--success { background: #16a34a; }
.door-btn--errorunavailable { background: #dc2626; }
.door-btn--errorratelimited { background: #6b7280; }
.door-btn--notallowed { background: #9ca3af; }

.banner { padding: 0.75rem; border-radius: 8px; margin: 0.75rem 0; text-align: center; font-weight: 500; }
.banner--success { background: #dcfce7; color: #166534; }
.banner--error   { background: #fee2e2; color: #991b1b; }
.banner--warn    { background: #f1f5f9; color: #475569; }

.install-prompt {
    margin: 1rem 0;
}
.install-prompt--ios,
.install-prompt--webview {
    background: var(--surface, #fff);
    border-radius: 10px;
    padding: 1rem;
    box-shadow: 0 1px 2px rgba(0,0,0,0.06);
}
.install-prompt__title {
    font-weight: 600;
    margin: 0 0 0.5rem;
}
.install-prompt__steps {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.install-prompt__steps li {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
.install-prompt__icon {
    flex-shrink: 0;
}
.install-prompt__icon svg {
    width: 1.3rem;
    height: 1.3rem;
    display: block;
}
.install-prompt__step-num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.4rem;
    height: 1.4rem;
    border-radius: 50%;
    background: var(--brand);
    color: var(--surface);
    font-size: 0.75rem;
    font-weight: 700;
    flex-shrink: 0;
}
.install-prompt__scroll-hint,
.install-prompt__footer-hint {
    font-size: 0.8rem;
    color: var(--text-muted);
    margin: 0.6rem 0 0;
}
.install-prompt--webview .install-prompt__title {
    margin-bottom: 0.75rem;
}
.install-prompt--webview .alert {
    margin: 0.75rem 0 0;
}

.card-credit, .card-pass {
    background: var(--surface, #fff);
    border-radius: 10px;
    padding: 1rem;
    margin-bottom: 0.75rem;
    box-shadow: 0 1px 2px rgba(0,0,0,0.06);
}
.card-credit__label, .card-pass__label { font-size: 0.85rem; color: #6b7280; }
.card-credit__value { font-size: 1.6rem; font-weight: 700; }
.card-pass__value { font-size: 1.1rem; }

.recent-visits__heading {
    font-size: 1rem;
    font-weight: 600;
    color: var(--text, #111827);
    margin: 1.25rem 0 0.5rem;
}
/* Rows use the shared, theme-aware `.list-row` primitive (#144); the old
   `.recent-visits__*` row rules were hardcoded light-mode hex and are retired. */
.recent-visits { list-style: none; padding: 0; }
