@import "tailwindcss";

@layer utilities {
  .hide-native-scrollbar {
    -ms-overflow-style: none !important;  /* IE and Edge */
    scrollbar-width: none !important;  /* Firefox */
  }
  .hide-native-scrollbar::-webkit-scrollbar {
    display: none !important; /* Chrome, Safari and Opera */
  }
  .custom-scrollbar::-webkit-scrollbar {
    width: 6px;
    height: 6px;
  }
  .custom-scrollbar::-webkit-scrollbar-track {
    background: transparent;
  }
  .custom-scrollbar::-webkit-scrollbar-thumb {
    background-color: rgba(156, 163, 175, 0.3); /* zinc-400 with opacity */
    border-radius: 10px;
  }
  .custom-scrollbar::-webkit-scrollbar-thumb:hover {
    background-color: rgba(156, 163, 175, 0.5);
  }
}


/* Force scrollbar visibility on touch devices */
@media (pointer: coarse), (hover: none) {
  ::-webkit-scrollbar {
    width: 8px !important;
    height: 8px !important;
    -webkit-appearance: none;
    display: block !important;
    background: transparent;
  }
  
  ::-webkit-scrollbar-track {
    background: rgba(0, 0, 0, 0.05) !important;
    border-radius: 10px;
  }

  ::-webkit-scrollbar-thumb {
    background-color: rgba(156, 163, 175, 0.5) !important;
    border-radius: 10px;
    border: 2px solid transparent;
    background-clip: content-box;
  }
}

/* Apply scrollbar styling to any element that scrolls */
.overflow-y-auto::-webkit-scrollbar,
.overflow-auto::-webkit-scrollbar,
.modal-content::-webkit-scrollbar {
  width: 8px;
  height: 8px;
  -webkit-appearance: none;
}

.overflow-y-auto::-webkit-scrollbar-track,
.overflow-auto::-webkit-scrollbar-track,
.modal-content::-webkit-scrollbar-track {
  background: transparent;
}

.overflow-y-auto::-webkit-scrollbar-thumb,
.overflow-auto::-webkit-scrollbar-thumb,
.modal-content::-webkit-scrollbar-thumb {
  background-color: rgba(156, 163, 175, 0.4);
  border-radius: 10px;
}

.overflow-y-auto::-webkit-scrollbar-thumb:hover,
.overflow-auto::-webkit-scrollbar-thumb:hover,
.modal-content::-webkit-scrollbar-thumb:hover {
  background-color: rgba(156, 163, 175, 0.6);
}
/* Firefox */
* {
  scrollbar-width: thin;
  scrollbar-color: rgba(156, 163, 175, 0.3) transparent;
}

/* ── No long-press selection / callout on controls (app-wide, all OS) ──────
   Long-pressing any button or interactive control must never select its text,
   fire the iOS touch-callout, or flash the tap-highlight. (These properties
   inherit, so icons inside the control are covered too.) Text content — chat
   messages, inputs — keeps normal selection. The browser's image/link
   long-press menu is suppressed by a capture-phase contextmenu guard in
   index.tsx. */
button,
[role="button"],
.session-mode-tag,
.stamina-collect-btn,
.logo-button,
.profile-picture-button {
  -webkit-user-select: none;
  -moz-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}


/* Face2Face Mode Styles */
.f2f-active-ring {
    box-shadow: 0 0 0 2px #4ade80, 0 0 10px #4ade80;
    border-radius: 50%;
    animation: pulse-ring 2s infinite;
}

@keyframes pulse-ring {
    0% {
        box-shadow: 0 0 0 2px rgba(74, 222, 128, 0.5), 0 0 10px rgba(74, 222, 128, 0.3);
    }
    50% {
        box-shadow: 0 0 0 4px rgba(74, 222, 128, 0.8), 0 0 15px rgba(74, 222, 128, 0.6);
    }
    100% {
        box-shadow: 0 0 0 2px rgba(74, 222, 128, 0.5), 0 0 10px rgba(74, 222, 128, 0.3);
    }
}

.api-indicator {
    position: absolute;
    width: calc(100% + 12px);
    height: calc(100% + 12px);
    border-radius: 50%;
    pointer-events: none;
    z-index: 20;
    box-sizing: border-box;
    transition: all 0.3s ease-in-out;
    border: 2px solid transparent;
}

.api-indicator.tool-call {
    animation: api-spin 1s linear infinite;
}

.api-indicator.tool-color-0 { border-top-color: #06b6d4; border-right-color: #06b6d4; } /* Cyan */
.api-indicator.tool-color-1 { border-top-color: #3b82f6; border-right-color: #3b82f6; } /* Blue */
.api-indicator.tool-color-2 { border-top-color: #ec4899; border-right-color: #ec4899; } /* Pink */
.api-indicator.tool-color-3 { border-top-color: #f43f5e; border-right-color: #f43f5e; } /* Red-Pink */

.api-indicator.retry {
    border-color: #ef4444;
    animation: api-breathe 2s ease-in-out infinite;
}

@keyframes api-spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

@keyframes api-breathe {
    0%, 100% {
        box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.4), 0 0 8px rgba(239, 68, 68, 0.4);
        opacity: 0.6;
    }
    50% {
        box-shadow: 0 0 0 4px rgba(239, 68, 68, 0.8), 0 0 16px rgba(239, 68, 68, 0.8);
        opacity: 1;
    }
}

.face2face-overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.7);
    backdrop-filter: blur(5px);
    z-index: 50;
    display: flex;
    justify-content: center;
    align-items: center;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.5s ease-in-out;
}

.face2face-overlay.visible {
    opacity: 1;
    pointer-events: none;
}

.avatar-container-large {
    position: relative;
    width: 100vw;
    height: 100vh;
    max-width: 600px;
    max-height: 800px;
    display: flex;
    justify-content: center;
    align-items: center;
    touch-action: pan-y; /* Allow vertical scrolling even when pointer-events is auto */
}

@keyframes slideInFromTop {
    from {
        opacity: 0;
        transform: translateY(-1rem);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@keyframes gatePulse {
    0%, 100% {
        transform: scale(1);
        box-shadow: 0 0 0 0 rgba(6, 182, 212, 0.3);
    }
    50% {
        transform: scale(1.06);
        box-shadow: 0 0 16px 4px rgba(6, 182, 212, 0.15);
    }
}

@keyframes offlineHeartbeat {
    0% {
        box-shadow: 0 0 4px rgba(245, 158, 11, 0.2);
        border-color: rgba(245, 158, 11, 0.3);
    }
    50% {
        box-shadow: 0 0 16px 6px rgba(245, 158, 11, 0.45);
        border-color: rgba(245, 158, 11, 0.6);
        background: rgba(245, 158, 11, 0.08) !important;
    }
    100% {
        box-shadow: 0 0 4px rgba(245, 158, 11, 0.2);
        border-color: rgba(245, 158, 11, 0.3);
    }
}

.chat-input.is-offline {
    background: rgba(245, 158, 11, 0.05) !important;
    border-color: rgba(245, 158, 11, 0.3) !important;
    color: rgba(245, 158, 11, 0.85) !important;
    cursor: not-allowed;
    text-align: center;
    font-weight: 600;
    animation: offlineHeartbeat 2.5s ease-in-out infinite;
}

@keyframes offlineTextPulse {
    0% {
        opacity: 0.85;
        transform: scale(1);
    }
    50% {
        opacity: 1;
        transform: scale(1.02);
        text-shadow: 0 0 8px rgba(245, 158, 11, 0.4);
    }
    100% {
        opacity: 0.85;
        transform: scale(1);
    }
}

.offline-notice-heartbeat {
    animation: offlineTextPulse 2s ease-in-out infinite;
    display: inline-block;
}

@keyframes offlineBannerPulse {
    0% {
        box-shadow: 0 0 4px rgba(245, 158, 11, 0.1);
        border-color: rgba(245, 158, 11, 0.2);
        background: rgba(245, 158, 11, 0.08);
        transform: scale(1);
    }
    50% {
        box-shadow: 0 0 16px 4px rgba(245, 158, 11, 0.35);
        border-color: rgba(245, 158, 11, 0.5);
        background: rgba(245, 158, 11, 0.15);
        transform: scale(1.015);
    }
    100% {
        box-shadow: 0 0 4px rgba(245, 158, 11, 0.1);
        border-color: rgba(245, 158, 11, 0.2);
        background: rgba(245, 158, 11, 0.08);
        transform: scale(1);
    }
}

.offline-banner-heartbeat {
    animation: offlineBannerPulse 2.5s ease-in-out infinite;
    border: 1px solid rgba(245, 158, 11, 0.3) !important;
}

/* ─── Header Status Banner (sync / offline) ──────────────────────── */
/* Thin full-width bar that overlaps the top of the chat history, styled after
   the sandbox status bar. Pointer events pass through so it never blocks the
   chat underneath. Rendered as the first child of the (position: relative) chat
   region so `top: 0` pins it just below the header. */
.status-banner {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    z-index: 1500;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    height: 32px;
    padding: 0 12px;
    font-size: 0.78rem;
    font-weight: 700;
    white-space: nowrap;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    border-bottom: 1px solid transparent;
    pointer-events: none;
}

.status-banner--offline {
    color: #fbbf24;
    background: rgba(245, 158, 11, 0.12);
    border-bottom-color: rgba(245, 158, 11, 0.35);
}

.status-banner--sync {
    color: var(--text-secondary, #a1a1aa);
    background: rgba(15, 23, 42, 0.6);
    border-bottom-color: rgba(148, 163, 184, 0.25);
}

/* Busy status (Thinking / Planning lesson / Taking notes) — relocated out of
   the header title column so the mode/persona pills keep a stable position on
   narrow phones. Solid background (was rgba .45): the banner now carries the
   load-bearing no-refund notice, and chat text bleeding through made it
   unreadable. */
.status-banner--status {
    color: #c7d2fe;
    background: #312e81;
    border-bottom-color: rgba(129, 140, 248, 0.5);
}

/* No-refund notice appended to the busy status (Backend API Resilience,
   Session 2): stamina isn't refunded if the user leaves/reloads mid-turn.
   Load-bearing copy — the no-refund-on-disconnect policy is only fair
   because the user was told. Dimmer + smaller than the status text so it
   reads as a footnote; ellipsizes instead of wrapping on narrow phones. */
.status-banner__notice {
    font-weight: 500;
    font-size: 0.68rem;
    opacity: 0.75;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Limited storage mode notice — dismissible, so unlike the other banner
   flavors it must accept pointer events. */
.status-banner--limited {
    color: #93c5fd;
    background: rgba(59, 130, 246, 0.12);
    border-bottom-color: rgba(59, 130, 246, 0.35);
    pointer-events: auto;
}

.status-banner__dismiss {
    background: none;
    border: none;
    color: inherit;
    cursor: pointer;
    font-size: 0.85rem;
    line-height: 1;
    padding: 4px 6px;
    opacity: 0.7;
}

.status-banner__dismiss:hover {
    opacity: 1;
}

/* Dashboard-authored announcement / promo banner (App Promotion Session 3).
   Dismissible, so it must accept pointer events; per-variant accent color. */
.status-banner--announcement {
    pointer-events: auto;
    gap: 0.5rem;
    height: auto;
    padding: 6px 12px;
    white-space: normal;
    flex-wrap: wrap;
}
.status-banner--announcement-promo {
    color: #6ee7b7;
    background: rgba(16, 185, 129, 0.12);
    border-bottom-color: rgba(16, 185, 129, 0.35);
}
.status-banner--announcement-stamina {
    color: #fcd34d;
    background: rgba(245, 158, 11, 0.12);
    border-bottom-color: rgba(245, 158, 11, 0.35);
}
.status-banner--announcement-info {
    color: #93c5fd;
    background: #1e3a8a;
    border-bottom-color: rgba(59, 130, 246, 0.35);
}
.status-banner__announcement-cta {
    text-decoration: underline;
    font-weight: 600;
    white-space: nowrap;
    pointer-events: auto;
}
.status-banner__announcement-cta:hover {
    opacity: 0.85;
}

/* Safe Test Mode — persistent while armed; loud on purpose. */
.status-banner--testmode {
    color: #f0abfc;
    background: rgba(192, 38, 211, 0.14);
    border-bottom-color: rgba(192, 38, 211, 0.4);
}

/* Second banner row when the top slot is taken (offline/sync/limited). */
.status-banner--stacked {
    top: 32px;
}

/* ─── Stamina UI ─────────────────────────────────────────────────── */

.stamina-counter-wrapper {
    display: flex;
    align-items: center;
    gap: 4px;
    /* Never clip the stamina counter. The collect (+) button is overlaid as a
       corner badge (see .stamina-collect-btn) rather than sitting inline, so the
       wrapper stays counter-width whether or not the daily collect is available
       — no horizontal cost, no layout shift, leaving room for the user avatar. */
    position: relative;
    /* Center the counter in the space between the title/pills and the action
       cluster: this auto margin plus the auto margin on .header-controls-right
       split the controls bar's free space evenly on each side of the counter,
       while the cluster (and the user avatar) stay pinned to the right edge. */
    margin-left: auto;
    /* The counter takes "whatever space is available": it yields before the
       right-side action cluster does, so the user avatar is NEVER pushed off the
       edge. On normal widths there is slack and it shows in full; only on a very
       narrow screen (e.g. the Z-Fold cover) does it give up a little. The inner
       .stamina-counter owns the overflow clip so the corner (+) badge, which
       sits outside this box, is never cut. */
    flex-shrink: 1;
    min-width: 0;
}

/* When the counter sits inside the vertical header slot (paid-tier plan pill
   stacked above it), neutralize the legacy margin-left:auto — it was there to
   center the counter horizontally back when it was a direct child of the
   controls bar. On the slot's cross-axis an auto margin CANCELS the default
   `stretch`, so the counter would hold its full intrinsic width and refuse to
   clip under a squeeze (re-opening the Bug 2 avatar overflow). Zeroing it lets
   the counter stretch to the slot and clip via its own overflow:hidden. */
.header-stamina-slot .stamina-counter-wrapper {
    margin-left: 0;
}

.stamina-counter {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace;
    font-weight: 700;
    font-size: 0.8rem;
    padding: 3px 6px;
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.08);
    transition: color 0.3s ease, background 0.3s ease;
    white-space: nowrap;
    justify-content: center;
    line-height: 1.2;
    /* Yields with its wrapper on a very narrow header; clips here (not on the
       wrapper) so the corner (+) badge stays visible. */
    flex-shrink: 1;
    min-width: 0;
    overflow: hidden;
}

.stamina-row {
    /* Two-column grid, not flex: a FIXED icon column shared by both rows so
       the Ki/Genki flames sit centered above each other while the two numeric
       values left-align at the same x (owner request 2026-07-10). The column
       matches the icons' 1.5em default box (ui/StaminaSymbols.tsx). */
    display: grid;
    grid-template-columns: 1.5em auto;
    column-gap: 3px;
    align-items: center;
    flex-shrink: 0;
}

.stamina-row > svg {
    justify-self: center;
    align-self: center;
}

.stamina-value {
    letter-spacing: 0.02em;
    justify-self: start;
}

/* Amber pulse for low balance (< 15, > 0) */
@keyframes stamina-amber-pulse {
    0%, 100% {
        background: rgba(251, 191, 36, 0.08);
        box-shadow: 0 0 0 0 rgba(251, 191, 36, 0);
    }
    50% {
        background: rgba(251, 191, 36, 0.18);
        box-shadow: 0 0 8px 2px rgba(251, 191, 36, 0.25);
    }
}

.stamina-counter.stamina-pulse {
    animation: stamina-amber-pulse 2s ease-in-out infinite;
}

/* Collect button (+) next to the counter */
.stamina-collect-btn {
    /* Overlaid as a corner badge on the stamina counter (top-right, sticking
       up — not rightward, so it never collides with the bookmark). Out of flow,
       so it costs no header width and causes no layout shift when the daily
       collect appears/disappears. */
    position: absolute;
    top: -7px;
    right: 2px;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    border: 2px solid #0c0c0f;
    background: #10b981;
    color: #ffffff;
    cursor: pointer;
    transition: transform 0.15s ease, background 0.15s ease;
    padding: 0;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

.stamina-collect-btn:hover:not(:disabled):not(.is-disabled) {
    background: #34d399;
    transform: scale(1.12);
}

.stamina-collect-btn:active:not(:disabled):not(.is-disabled) {
    transform: scale(0.9);
}

.stamina-collect-btn.is-collecting {
    opacity: 0.5;
    pointer-events: none;
    animation: stamina-collect-spin 0.8s linear infinite;
}

.stamina-collect-btn.is-disabled {
    opacity: 0.3;
    cursor: not-allowed;
    pointer-events: auto;
    border-color: rgba(161, 161, 170, 0.2);
    background: rgba(161, 161, 170, 0.05);
    color: #71717a;
}

@keyframes stamina-collect-spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

/* Bounce on successful collect */
@keyframes stamina-collect-bounce {
    0% { transform: scale(1); }
    30% { transform: scale(1.3); }
    50% { transform: scale(0.95); }
    70% { transform: scale(1.1); }
    100% { transform: scale(1); }
}

/* ─── Exhaustion Gate Overlay Card ─────────────────────────────── */

.stamina-gate-card {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    background: linear-gradient(135deg, rgba(24, 24, 27, 0.98), rgba(39, 39, 42, 0.95));
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 1.25rem;
    padding: 2rem 1.5rem;
    max-width: 380px;
    width: 100%;
    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.7),
                0 0 30px rgba(239, 68, 68, 0.1);
    animation: gate-slide-in 0.3s ease-out;
}

@keyframes gate-slide-in {
    from {
        opacity: 0;
        transform: translateY(20px) scale(0.97);
    }
    to {
        opacity: 1;
        transform: translateY(0) scale(1);
    }
}

.stamina-gate-icon {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    background: rgba(239, 68, 68, 0.1);
    border: 1px solid rgba(239, 68, 68, 0.2);
    display: flex;
    align-items: center;
    justify-content: center;
    animation: gate-icon-pulse 3s ease-in-out infinite;
}

@keyframes gate-icon-pulse {
    0%, 100% {
        box-shadow: 0 0 0 0 rgba(239, 68, 68, 0);
    }
    50% {
        box-shadow: 0 0 12px 4px rgba(239, 68, 68, 0.15);
    }
}

.stamina-gate-collect-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    max-width: 260px;
    padding: 12px 20px;
    border-radius: 12px;
    border: none;
    background: linear-gradient(135deg, #34d399, #06b6d4);
    color: #09090b;
    font-weight: 700;
    font-size: 0.9rem;
    cursor: pointer;
    transition: all 0.2s ease;
    box-shadow: 0 4px 15px rgba(52, 211, 153, 0.3);
}

.stamina-gate-collect-btn:hover:not(:disabled) {
    transform: translateY(-1px);
    box-shadow: 0 6px 20px rgba(52, 211, 153, 0.4);
}

.stamina-gate-collect-btn:active:not(:disabled) {
    transform: translateY(0);
}

.stamina-gate-collect-btn:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.stamina-gate-stub-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 10px 16px;
    border-radius: 10px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    background: rgba(255, 255, 255, 0.03);
    color: #a1a1aa;
    font-weight: 500;
    font-size: 0.8rem;
    cursor: pointer;
    transition: all 0.2s ease;
    width: 100%;
}

.stamina-gate-stub-btn:hover {
    background: rgba(255, 255, 255, 0.06);
    color: #d4d4d8;
}

.stamina-gate-stub-btn.pro {
    border-color: rgba(251, 191, 36, 0.2);
    color: #fbbf24;
}

.stamina-gate-stub-btn.pro:hover {
    background: rgba(251, 191, 36, 0.08);
}

/* ─── Store variant + purchase section (Paddle Session 4) ───────── */

/* Cyan shop framing replaces the red alarm framing when the gate is
   opened proactively from the user menu's "Stamina Store" entry. */
.stamina-gate-icon.store {
    background: rgba(34, 211, 238, 0.1);
    border-color: rgba(34, 211, 238, 0.2);
    animation: gate-icon-pulse-store 3s ease-in-out infinite;
}

@keyframes gate-icon-pulse-store {
    0%, 100% {
        box-shadow: 0 0 0 0 rgba(34, 211, 238, 0);
    }
    50% {
        box-shadow: 0 0 12px 4px rgba(34, 211, 238, 0.15);
    }
}

/* ONE heading treatment for the store's section titles and its plan-card
   titles (owner request): same family, size and weight, so "Genki Packs" and
   "Genki Plan" read as the same level of heading rather than a caption above
   a title. Case is unified by dropping the label's uppercase/letter-spacing
   rather than shouting the plan names — those are proper nouns, and
   "SUPER GENKI PLAN" reads as an advertisement. Kept as a pair here so the
   two cannot be resized independently and drift apart again. */
.stamina-section-label,
.genki-plan-name {
    font-size: 0.95rem;
    font-weight: 700;
    letter-spacing: 0;
    text-transform: none;
}

.stamina-section-label {
    /* Lifted from the old caption grey: at the shared heading size the former
       value read as washed-out body text beside the plan titles' amber/cyan,
       which defeats the point of matching their size. Neutral rather than
       coloured — the tier colours stay the plan cards' own identity. */
    color: #a1a1aa;
    text-align: center;
}

/* The heading is a button when it opens the explainer — strip the control
   chrome so it still reads as a heading, and centre the icon against the
   larger text. */
.stamina-resources-heading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: 100%;
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    color: #67e8f9;
    transition: color 0.2s ease;
}

.stamina-resources-heading:hover {
    color: #a5f3fc;
}

.stamina-pack-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px;
    width: 100%;
    /* Headroom for the badges that overhang the card tops. */
    padding-top: 8px;
}

/* Fine print under the pack grid — prices are shown before tax (Paddle, as
   Merchant of Record, computes the exact regional tax at checkout). */
.stamina-tax-note {
    margin: 6px 0 0;
    color: #71717a;
    font-size: 0.62rem;
    line-height: 1.35;
    text-align: center;
}

.stamina-pack-card {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    padding: 14px 4px 10px;
    border-radius: 12px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    background: rgba(255, 255, 255, 0.03);
    cursor: pointer;
    transition: all 0.2s ease;
}

.stamina-pack-card:hover:not(:disabled) {
    border-color: rgba(34, 211, 238, 0.4);
    background: rgba(34, 211, 238, 0.06);
    transform: translateY(-1px);
}

.stamina-pack-card:active:not(:disabled) {
    transform: translateY(0);
}

.stamina-pack-card:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.stamina-pack-badge {
    position: absolute;
    top: -8px;
    left: 50%;
    transform: translateX(-50%);
    padding: 2px 7px;
    border-radius: 999px;
    background: #0e7490;
    color: #e0f2fe;
    font-size: 0.55rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    white-space: nowrap;
    pointer-events: none;
}

.stamina-pack-badge.best {
    background: linear-gradient(135deg, #f59e0b, #fbbf24);
    color: #1c1917;
}

.stamina-pack-amount {
    display: flex;
    align-items: center;
    gap: 3px;
    color: #f4f4f5;
    font-weight: 700;
    font-size: 0.9rem;
}

/* Genki pool symbols (Genki paid / Ki free) are custom inline SVG components —
   see ui/StaminaSymbols.tsx (GenkiIcon / KiIcon). They size via a `size`
   prop (default 1.5em — owner request 2026-07-10) and render identically on
   every device (no emoji-font variance), so no per-glyph CSS is needed here. */

.stamina-pack-price {
    color: #a1a1aa;
    font-size: 0.72rem;
    font-weight: 500;
}

.stamina-pro-card {
    display: flex;
    flex-direction: column;
    gap: 8px;
    width: 100%;
    padding: 12px;
    border-radius: 14px;
    border: 1px solid rgba(251, 191, 36, 0.25);
    background: linear-gradient(160deg, rgba(251, 191, 36, 0.07), rgba(251, 191, 36, 0.02));
}

.stamina-pro-title {
    display: flex;
    align-items: center;
    gap: 6px;
    color: #fbbf24;
    font-weight: 700;
    font-size: 0.85rem;
}

.stamina-pro-benefits {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.stamina-pro-benefits li {
    display: flex;
    align-items: center;
    gap: 6px;
    color: #d4d4d8;
    font-size: 0.75rem;
}

.stamina-pro-check {
    color: #34d399;
    flex-shrink: 0;
}

.stamina-pro-cta {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    padding: 10px 16px;
    border-radius: 10px;
    border: none;
    background: linear-gradient(135deg, #f59e0b, #fbbf24);
    color: #1c1917;
    font-weight: 700;
    font-size: 0.85rem;
    cursor: pointer;
    transition: all 0.2s ease;
    box-shadow: 0 4px 15px rgba(251, 191, 36, 0.25);
}

.stamina-pro-cta:hover:not(:disabled) {
    transform: translateY(-1px);
    box-shadow: 0 6px 20px rgba(251, 191, 36, 0.35);
}

.stamina-pro-cta:active:not(:disabled) {
    transform: translateY(0);
}

.stamina-pro-cta:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.stamina-pro-footnote {
    margin: 0;
    color: #71717a;
    font-size: 0.65rem;
    text-align: center;
}

/* Tappable 💎 (paid) portion of the header counter — opens the Stamina Store. */
.stamina-row-store {
    border-radius: 6px;
    padding: 0 2px;
    transition: opacity 0.15s ease, background-color 0.15s ease;
}

.stamina-row-store:hover {
    opacity: 0.85;
    background-color: rgba(34, 211, 238, 0.12);
}

.stamina-row-store:focus-visible {
    outline: 2px solid rgba(34, 211, 238, 0.6);
    outline-offset: 1px;
}

/* Pro-member (already-subscribed) panel: informational rules + manage link. */
.stamina-pro-note {
    margin: 0;
    color: #a1a1aa;
    font-size: 0.72rem;
    line-height: 1.45;
}

.stamina-pro-manage {
    align-self: center;
    margin-top: 2px;
    padding: 2px 4px;
    background: none;
    border: none;
    color: #a1a1aa;
    font-size: 0.72rem;
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor: pointer;
    transition: color 0.2s ease;
}

.stamina-pro-manage:hover:not(:disabled) {
    color: #e4e4e7;
}

.stamina-pro-manage:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

/* ── Side-by-side plan comparison (Genki Plan / Super Genki Plan) — Session 3.
   Cards wrap to a single column on the narrowest phones so nothing overflows
   the store card. Prices are localized-catalog-only (rule 20) — the em-dash
   skeleton shows while the PricePreview loads or if it fails. */
.genki-plans {
    width: 100%;
    max-width: 320px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    margin-top: 4px;
}

.genki-plans-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 8px;
    width: 100%;
}

.genki-plan-card {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 12px 10px;
    border-radius: 14px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    background: rgba(255, 255, 255, 0.03);
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.genki-plan-card.super {
    border-color: rgba(34, 211, 238, 0.25);
    background: linear-gradient(160deg, rgba(34, 211, 238, 0.06), rgba(34, 211, 238, 0.01));
}

.genki-plan-card.highlight {
    border-color: rgba(34, 211, 238, 0.6);
    box-shadow: 0 0 18px rgba(34, 211, 238, 0.18);
}

.genki-plan-card.current {
    border-color: rgba(52, 211, 153, 0.4);
}

.genki-plan-name {
    display: flex;
    align-items: center;
    gap: 5px;
    color: #fbbf24;
    /* size/weight come from the shared heading rule above */
}

.genki-plan-name.super {
    color: #22d3ee;
}

.genki-plan-price {
    color: #f4f4f5;
    font-weight: 700;
    font-size: 0.95rem;
}

.genki-plan-per {
    color: #a1a1aa;
    font-weight: 500;
    font-size: 0.62rem;
}

.genki-plan-price-loading {
    color: #52525b;
}

.genki-plan-benefits {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
    flex: 1 1 auto;
}

.genki-plan-benefits li {
    display: flex;
    align-items: flex-start;
    gap: 5px;
    color: #d4d4d8;
    font-size: 0.68rem;
    line-height: 1.3;
}

.genki-plan-check {
    color: #34d399;
    flex-shrink: 0;
    margin-top: 2px;
}

/* ── Included allowances (inside the resources card) ────────────────────
   A quiet meter, not a sales pitch: it answers "how much of what I pay for is
   left" while shopping. Reads left-to-right — what it is, how much is gone,
   how much remains — and the remaining count is the number, because that is
   the question being asked. Amber when spent, which is exactly when it
   explains a Genki charge the customer is about to see.

   The rule above it separates two kinds of resource — spendable balance,
   included allowance — and belongs to THIS block rather than the card, so an
   account with no allowance gets no dangling divider. */
.stamina-allowances {
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid rgba(255, 255, 255, 0.07);
}

.stamina-allowance-row {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 8px;
}

.stamina-allowance-name {
    font-size: 0.72rem;
    color: #d4d4d8;
    white-space: nowrap;
}

.stamina-allowance-meter {
    height: 5px;
    border-radius: 9999px;
    background: rgba(255, 255, 255, 0.08);
    overflow: hidden;
}

.stamina-allowance-fill {
    display: block;
    height: 100%;
    border-radius: 9999px;
    background: linear-gradient(90deg, #0891b2, #22d3ee);
    transition: width 0.4s ease;
}

.stamina-allowance-fill.spent {
    background: linear-gradient(90deg, #b45309, #f59e0b);
}

.stamina-allowance-count {
    font-size: 0.72rem;
    font-weight: 700;
    color: #22d3ee;
    white-space: nowrap;
}

.stamina-allowance-count.spent {
    color: #f59e0b;
}

/* ── "Your resources" card ──────────────────────────────────────────────
   ONE card for everything the customer already has, above everything for
   sale. Balance and included allowances answer the same question — "what do
   I have?" — and were previously a pill and a panel with the pack prices
   between them, so the reader had to assemble the answer themselves.

   Quiet by design: a reference figure while shopping, not a headline. It
   earns its place at exactly one moment — when a purchase credits — and the
   odometer is what makes that moment visible.

   Sized to the pack grid below it so the store reads as one column. */
.stamina-resources {
    width: 100%;
    max-width: 300px;
    margin-top: 0.9rem;
    padding: 10px 14px 12px;
    border-radius: 14px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    background: rgba(255, 255, 255, 0.03);
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.stamina-resource-pools {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 18px;
    flex-wrap: wrap;
}

/* CENTRE, never baseline. The odometer's digit columns are `overflow:
   hidden`, which hides their internal baseline — a browser then uses the
   element's bottom edge instead, so baseline alignment would sit the unit
   label and the icon visibly low against the digits. */
.stamina-resource-pool {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 1.05rem;
    font-weight: 700;
}

.stamina-resource-pool.free { color: #34d399; }
.stamina-resource-pool.paid { color: #22d3ee; }

/* The unit names the number so the card stands alone: two coloured figures
   with no labels only read as "Ki" and "Genki" to someone who already knows
   which is which. */
.stamina-resource-pool-unit {
    font-size: 0.65rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    color: #a1a1aa;
}

.stamina-resource-note {
    font-size: 0.62rem;
    line-height: 1.45;
    color: #71717a;
    text-align: center;
    margin-top: 2px;
}

/* ── RollingNumber (odometer balance counter) ───────────────────────────
   One column per digit, each holding 0–9 stacked vertically; the strip is
   translated to bring the current digit into the single-digit-tall window.

   `1em` is the column height and each digit is exactly `1em`, so the strip is
   10em and a digit's offset is a clean multiple of 10%. That keeps the counter
   locked to the surrounding font size — these balances render at several sizes
   (header, store pill) and must not need a per-site height.

   The transition lives ONLY under `.is-rolling`, which the component sets for
   the duration of an increase and clears afterwards. That is what makes a
   decrease snap: with no transition on the base rule there is nothing to
   animate through. */
.rolling-number {
    display: inline-flex;
    align-items: baseline;
    /* Tabular figures stop the width jittering as digits change. */
    font-variant-numeric: tabular-nums;
}

.rolling-number-col {
    display: inline-block;
    height: 1em;
    line-height: 1em;
    overflow: hidden;
    vertical-align: bottom;
}

.rolling-number-strip {
    display: flex;
    flex-direction: column;
    will-change: transform;
}

.rolling-number.is-rolling .rolling-number-strip {
    /* Slight overshoot-free ease-out: fast off the mark, settling into place —
       the digit should look like it landed, not like it drifted. */
    transition: transform 0.62s cubic-bezier(0.16, 0.84, 0.28, 1);
}

.rolling-number-digit {
    height: 1em;
    line-height: 1em;
}

.rolling-number-static {
    display: inline-block;
    height: 1em;
    line-height: 1em;
}

/* The value as a real text node for assistive tech, clipped away visually.
   Not `display: none` / `visibility: hidden`, which would remove it from the
   accessibility tree along with the pixels. */
.rolling-number-a11y {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

@media (prefers-reduced-motion: reduce) {
    .rolling-number.is-rolling .rolling-number-strip {
        transition: none;
    }
}

/* ── Purchase confirmation (in-app receipt) ─────────────────────────────
   Green, not the store's gold: this is a completed-successfully signal, and
   it must not read as another thing to buy. */
.purchase-confirm-icon {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #34d399;
    background: rgba(52, 211, 153, 0.1);
    border: 1px solid rgba(52, 211, 153, 0.25);
}

.purchase-confirm-title {
    margin-top: 1rem;
    font-size: 1.25rem;
    font-weight: 700;
    color: #f4f4f5;
    text-align: center;
}

.purchase-confirm-items {
    list-style: none;
    margin: 0.75rem 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
    text-align: center;
}

.purchase-confirm-items li {
    color: #d4d4d8;
    font-size: 0.85rem;
    font-weight: 600;
}

.purchase-confirm-credit {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    margin: 0.75rem 0 0;
    color: #22d3ee;
    font-size: 0.9rem;
    font-weight: 700;
}

/* The balance climbing inside the receipt — the payoff line, so it is the
   largest figure on the panel and sits right under the credit statement. */
.purchase-confirm-balance {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    margin: 0.85rem 0 0;
}

.purchase-confirm-balance-label {
    font-size: 0.6rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: #a1a1aa;
}

.purchase-confirm-balance-value {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: #f4f4f5;
    font-size: 1.5rem;
    font-weight: 800;
}

.purchase-confirm-charged {
    margin: 0.6rem 0 0;
    color: #a1a1aa;
    font-size: 0.75rem;
    text-align: center;
}

.purchase-confirm-charged strong {
    color: #f4f4f5;
    font-weight: 700;
}

.purchase-confirm-next-billing {
    margin: 0.75rem 0 0;
    color: #a1a1aa;
    font-size: 0.72rem;
    text-align: center;
}

.purchase-confirm-next-billing strong {
    color: #d4d4d8;
}

.purchase-confirm-cta {
    margin-top: 1.25rem;
    width: 100%;
    max-width: 240px;
    padding: 10px 14px;
    border: none;
    border-radius: 12px;
    background: linear-gradient(135deg, #059669, #34d399);
    color: #052e2b;
    font-weight: 700;
    font-size: 0.85rem;
    cursor: pointer;
    transition: transform 0.15s ease, box-shadow 0.2s ease;
}

.purchase-confirm-cta:hover {
    transform: translateY(-1px);
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
}

/* ── Modal viewport binding ─────────────────────────────────────────────
   Every overlay that CENTRES A CARD carries `modal-shell`. Without it a card
   taller than the phone is centred, overflows equally off the top and bottom,
   and the flex container cannot scroll to reach either — so the X in the
   card's top-right becomes physically unreachable and the only way out is to
   reload. That has bitten repeatedly; this is the one place it gets fixed.

   Binding the CARD (not scrolling the shell) is deliberate: once the card can
   never exceed the viewport, the centring is always safe, no shell needs an
   `align-items: safe center` override, and each card's own inner scroll keeps
   working. `dvh` follows mobile browser chrome as it collapses; the `vh` line
   above it is the fallback for engines without `dvh`. The 2rem allowance
   matches the shells' `p-4` gutter.

   The doubled class is a specificity device, not a typo: `.modal-shell.modal-shell > *`
   scores (0,2,0) and so beats any single Tailwind utility (`max-h-*`,
   `overflow-hidden`) whatever order the stylesheets happen to load in — this
   app has two deploy paths and cannot depend on that order.

   Full-screen surfaces that are NOT a centred card (the loading splash, the
   simulation player, the full-bleed pickers) do not take this class; they
   scroll themselves. See Requirements/ui_and_visual_design.md §1.8. */
.modal-shell {
    overscroll-behavior: contain;
}

.modal-shell.modal-shell > * {
    max-height: calc(100vh - 2rem);
    max-height: calc(100dvh - 2rem);
    overflow-y: auto;
    /* Vertical only. Several cards previously carried `overflow-hidden`; turning
       that into a bare `auto` would let a wide table raise a horizontal
       scrollbar on the card itself, which is a visual regression rather than a
       fix. Inner regions that genuinely scroll sideways still do. */
    overflow-x: hidden;
    overscroll-behavior: contain;
}

/* ── Beta Genki Plan offer (store card + store-pill badge) ──────────────
   The free beta offer competing for attention against PAID plan cards, so
   it is deliberately the only cyan-glowing thing in the store. `.glow` is
   applied ONLY while there is a live offer (available/active) — a spent
   trial keeps the card shape and loses the glow, so it reads as history
   rather than as another thing to press. Both the pulse and the badge's
   shimmer collapse to a static appearance under
   `prefers-reduced-motion`. */
.beta-plan-card {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 12px 12px;
    border-radius: 14px;
    border: 1px solid rgba(34, 211, 238, 0.3);
    background: linear-gradient(160deg, rgba(34, 211, 238, 0.08), rgba(34, 211, 238, 0.01));
}

.beta-plan-card.glow {
    border-color: rgba(34, 211, 238, 0.65);
    animation: beta-offer-pulse 2.6s ease-in-out infinite;
}

.beta-plan-card-title {
    display: flex;
    align-items: center;
    gap: 5px;
    color: #22d3ee;
    font-weight: 700;
    font-size: 0.8rem;
}

.beta-plan-card-body {
    margin: 0;
    color: #d4d4d8;
    font-size: 0.68rem;
    line-height: 1.35;
}

.beta-plan-card-cta {
    margin-top: 2px;
    padding: 8px 10px;
    border-radius: 10px;
    border: none;
    background: linear-gradient(135deg, #0891b2, #22d3ee);
    color: #082f49;
    font-weight: 700;
    font-size: 0.72rem;
    cursor: pointer;
    transition: transform 0.15s ease, box-shadow 0.2s ease, opacity 0.2s ease;
}

.beta-plan-card-cta:hover:not(:disabled) {
    transform: translateY(-1px);
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
}

.beta-plan-card-cta:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* The store pill's unclaimed-offer badge — cyan against the pill's gold so
   it reads as an offer, not another balance chip. */
.beta-offer-badge {
    font-size: 0.6rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: #a5f3fc;
    white-space: nowrap;
    border: 1px solid rgba(34, 211, 238, 0.6);
    border-radius: 9999px;
    padding: 2px 7px;
    background: rgba(34, 211, 238, 0.12);
    animation: beta-offer-pulse 2.6s ease-in-out infinite;
}

@keyframes beta-offer-pulse {
    0%, 100% { box-shadow: 0 0 6px rgba(34, 211, 238, 0.25); }
    50% { box-shadow: 0 0 18px rgba(34, 211, 238, 0.6); }
}

@media (prefers-reduced-motion: reduce) {
    .beta-plan-card.glow,
    .beta-offer-badge {
        animation: none;
        box-shadow: 0 0 14px rgba(34, 211, 238, 0.35);
    }
}

.genki-plan-cta {
    margin-top: 2px;
    padding: 8px 10px;
    border-radius: 10px;
    border: none;
    background: linear-gradient(135deg, #f59e0b, #fbbf24);
    color: #1c1917;
    font-weight: 700;
    font-size: 0.72rem;
    cursor: pointer;
    transition: transform 0.15s ease, box-shadow 0.2s ease, opacity 0.2s ease;
}

.genki-plan-cta.super {
    background: linear-gradient(135deg, #0891b2, #22d3ee);
    color: #082f49;
}

.genki-plan-cta.secondary {
    background: rgba(255, 255, 255, 0.06);
    color: #d4d4d8;
    border: 1px solid rgba(255, 255, 255, 0.12);
}

.genki-plan-cta:hover:not(:disabled) {
    transform: translateY(-1px);
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
}

.genki-plan-cta:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.genki-plan-status {
    margin-top: 2px;
    padding: 7px 8px;
    border-radius: 10px;
    text-align: center;
    font-size: 0.68rem;
    font-weight: 700;
    color: #a1a1aa;
    background: rgba(255, 255, 255, 0.04);
}

.genki-plan-status.current-badge {
    color: #34d399;
    background: rgba(52, 211, 153, 0.1);
}

.genki-plan-status.scheduled {
    color: #fcd34d;
    background: rgba(251, 191, 36, 0.1);
    line-height: 1.3;
}

.genki-plan-footnote {
    margin: 2px 0 0;
    color: #71717a;
    font-size: 0.6rem;
    line-height: 1.35;
    text-align: center;
}

/* Nested confirm dialog for upgrade / downgrade / revert — above the store. */
.genki-plan-confirm-backdrop {
    position: fixed;
    inset: 0;
    z-index: 3200;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    background: rgba(0, 0, 0, 0.6);
    backdrop-filter: blur(2px);
}

.genki-plan-confirm {
    width: 100%;
    max-width: 340px;
    padding: 18px;
    border-radius: 16px;
    border: 1px solid rgba(255, 255, 255, 0.1);
    background: #18181b;
    box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5);
}

.genki-plan-confirm-title {
    margin: 0 0 8px;
    color: #f4f4f5;
    font-size: 1rem;
    font-weight: 700;
}

.genki-plan-confirm-body {
    margin: 0 0 16px;
    color: #a1a1aa;
    font-size: 0.78rem;
    line-height: 1.5;
}

/* Scannable confirm notice: a short lead line + a tight bullet list, so the
   charge and what-you-get read at a glance instead of as a paragraph. */
.genki-plan-confirm-lead {
    margin: 0 0 10px;
    color: #e4e4e7;
    font-size: 0.82rem;
    line-height: 1.45;
}

.genki-plan-confirm-list {
    margin: 0 0 16px;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.genki-plan-confirm-list li {
    position: relative;
    padding-left: 16px;
    color: #a1a1aa;
    font-size: 0.78rem;
    line-height: 1.4;
}

.genki-plan-confirm-list li::before {
    content: "";
    position: absolute;
    left: 2px;
    top: 0.5em;
    width: 5px;
    height: 5px;
    border-radius: 9999px;
    background: #22d3ee;
}

.genki-plan-confirm-list li.genki-plan-confirm-bonus {
    color: #67e8f9;
    font-weight: 600;
}

.genki-plan-confirm-actions {
    display: flex;
    gap: 8px;
    justify-content: flex-end;
}

.genki-plan-confirm-cancel,
.genki-plan-confirm-ok {
    padding: 8px 14px;
    border-radius: 10px;
    font-weight: 700;
    font-size: 0.78rem;
    cursor: pointer;
    transition: opacity 0.2s ease;
}

.genki-plan-confirm-cancel {
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.12);
    color: #d4d4d8;
}

.genki-plan-confirm-ok {
    background: linear-gradient(135deg, #0891b2, #22d3ee);
    border: none;
    color: #082f49;
}

.genki-plan-confirm-cancel:disabled,
.genki-plan-confirm-ok:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

@media (max-width: 380px) {
    .genki-plans-grid {
        grid-template-columns: 1fr;
    }
}

/* Mobile adjustments for stamina counter */
@media (max-width: 480px) {
    .stamina-counter {
        font-size: 0.72rem;
        padding: 2px 5px;
    }

    .stamina-collect-btn {
        width: 16px;
        height: 16px;
    }

    /* Narrow header (phones, and especially the Z-Fold cover screen ~344px):
       stack the mode + persona pills vertically instead of side by side. This
       reclaims ~50px of header width so the stamina counter and the right-side
       action cluster (incl. the user avatar) all fit. Safe now that the turn
       counter renders below the header — vertical pills no longer overlap it. */
    .header-tags-row {
        flex-direction: column;
        align-items: flex-start;
        gap: 3px;
    }
}

/* ─── Header Controls Layout ────────────────────────────────────
   .header-controls--spaced  — adds flex:1 so the controls bar
   stretches and lets the right-side cluster auto-push to the edge.
   .header-controls-right    — groups the lesson plan button, nav
   arrows, mute button and profile picture so they stay anchored to
   the right regardless of sync/offline indicator visibility.      */

.header-controls--spaced {
    flex: 1 1 auto;
    min-width: 0;
}

/* Higher-specificity guarantee that the controls bar actually grows to fill the
   header. When the grow is defeated (the bar stays content-width), `.header`'s
   space-between shoves the stamina counter + action cluster to the far right
   with a wide empty gap before the stamina counter, and the user avatar at the
   end of the cluster overflows off the right edge. Growing the bar anchors the
   stamina counter next to the pills and pins the action cluster — with the user
   avatar — to the in-bounds right edge. */
.header .header-controls--spaced {
    flex: 1 1 auto;
    min-width: 0;
}

.header-controls-right {
    display: flex;
    align-items: center;
    gap: 4px;
    margin-left: auto;
    flex-shrink: 0;
}

/* ─── Accessibility: right-side action controls ──────────────────────
   Bookmark, nav arrows, mute and the profile button. Goals: (1) icons that
   grow with the user's text-size preference; (2) genuinely larger, better-
   spaced controls where there is room. */

/* Icons scale with the OS/browser font-size setting (rem), capped so the
   cover screen can't be blown out. Scoped to direct-child <svg> so the mute
   button's separate hold-progress ring is never resized. */
.header-controls-right .logo-button > svg {
    width: clamp(22px, 1.5rem, 28px);
    height: clamp(22px, 1.5rem, 28px);
}

/* NOTE: an invisible ::before hit-area expansion was tried here but removed —
   the cluster buttons sit only 4px apart, so the expanded zones overlapped and
   the later element (the avatar) stole the mute button's edge taps, and the
   overlay interfered with the mute's touch hold-gesture. Touch-target size is
   instead handled by enlarging the buttons where there's room (below). */

/* Where there is room (most phones and the unfolded inner screen) make the
   controls visibly larger and better spaced. */
@media (min-width: 500px) {
    .header-controls-right {
        gap: 8px;
    }
    .header-controls-right .logo-button {
        min-width: 40px;
        min-height: 40px;
    }
    .header-nav-arrows {
        gap: 6px !important;
    }
    /* More headroom here, so allow icons to grow further with the font. */
    .header-controls-right .logo-button > svg {
        width: clamp(24px, 1.7rem, 40px);
        height: clamp(24px, 1.7rem, 40px);
    }
}


/* ── Account dashboard ──────────────────────────────────────────────────
   The customer's own view of what the developer dashboard shows staff. Sized
   wider than the store card because its point is a LIST — a receipt reads fine
   at 300px, a history does not — and bound to the viewport by `modal-shell`
   on the overlay, which matters more here than anywhere: this is the one card
   whose content grows without limit, so the close button is the first thing an
   unbound layout would push off a phone. */
.account-dashboard-card {
    position: relative;
    width: 100%;
    max-width: 460px;
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding: 24px 20px 20px;
    border-radius: 1.25rem;
    border: 1px solid rgba(255, 255, 255, 0.08);
    background: linear-gradient(135deg, rgba(24, 24, 27, 0.98), rgba(39, 39, 42, 0.95));
    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.7);
}

.account-dashboard-close {
    position: absolute;
    top: 12px;
    right: 12px;
    padding: 4px;
    border: none;
    background: none;
    color: #a1a1aa;
    cursor: pointer;
    transition: color 0.2s ease;
}

.account-dashboard-close:hover { color: #f4f4f5; }

.account-dashboard-title {
    font-size: 1.25rem;
    font-weight: 700;
    color: #f4f4f5;
    text-align: center;
    margin: 0;
}

.account-dashboard-email {
    font-size: 0.72rem;
    color: #71717a;
    text-align: center;
    margin: -10px 0 0;
    /* An address can be long and is not worth a second line on a phone. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.account-dashboard-section {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 12px 14px;
    border-radius: 14px;
    border: 1px solid rgba(255, 255, 255, 0.07);
    background: rgba(255, 255, 255, 0.03);
}

.account-dashboard-plan {
    font-size: 0.95rem;
    font-weight: 700;
    color: #22d3ee;
    text-align: center;
    margin: 0;
}

.account-dashboard-empty,
.account-dashboard-sub {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    font-size: 0.72rem;
    color: #a1a1aa;
}

.account-dashboard-sub { justify-content: space-between; }
.account-dashboard-sub-line { color: #d4d4d8; }

/* The history itself. Scrolls WITHIN the card rather than growing it: the card
   is already viewport-bound, and a list that pushed the card taller would just
   move the scrolling somewhere less obvious. */
.account-history-list {
    display: flex;
    flex-direction: column;
    max-height: 40vh;
    overflow-y: auto;
}

.account-history-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    padding: 6px 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}

.account-history-row:last-child { border-bottom: none; }

.account-history-what {
    display: flex;
    flex-direction: column;
    font-size: 0.78rem;
    color: #e4e4e7;
    min-width: 0;
}

.account-history-when {
    font-size: 0.62rem;
    color: #71717a;
}

/* Tabular figures so the amounts form a column the eye can scan, rather than
   jittering with each digit width. */
.account-history-amount {
    font-size: 0.78rem;
    font-weight: 700;
    color: #a1a1aa;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.account-history-amount.credit { color: #34d399; }

.account-history-pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    font-size: 0.65rem;
    color: #71717a;
}

.account-history-pager button {
    padding: 3px 8px;
    border-radius: 6px;
    border: 1px solid rgba(255, 255, 255, 0.1);
    background: rgba(255, 255, 255, 0.04);
    color: #d4d4d8;
    font-size: 0.65rem;
    cursor: pointer;
}

.account-history-pager button:disabled {
    opacity: 0.35;
    cursor: not-allowed;
}
