/* === APP-LEVEL STYLES === */
/* NOTE: MudBlazor now handles primary layout, forms, buttons, and cards
   for migrated pages. These legacy styles remain for unmigrated components
   (StatCard, EmptyState, KartCard, TierLimitBanner, etc.) and will be
   removed incrementally as components are migrated to MudBlazor. */

html, body {
    margin: 0;
    padding: 0;
}

#blazor-error-ui {
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    color: #333;
    display: none;
    left: 0;
    padding: 0.6rem 3rem 0.6rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
}

#blazor-error-ui .reload {
    color: #0066cc;
    margin-left: 0.5rem;
}

/* Loading screen */
.loading-screen {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    min-height: 100dvh;
}

.loading-progress {
    position: relative;
    display: block;
    width: 8rem;
    height: 8rem;
}

.loading-progress circle {
    fill: none;
    stroke: var(--color-gray-200);
    stroke-width: 0.6rem;
    transform-origin: 50% 50%;
    transform: rotate(-90deg);
}

.loading-progress circle:last-child {
    stroke: var(--color-primary-500);
    stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
    transition: stroke-dasharray 0.05s ease-in-out;
}

.loading-progress-text {
    text-align: center;
    font-weight: var(--font-medium);
    color: var(--color-text-secondary);
}

.loading-progress-text:after {
    content: var(--blazor-load-percentage-text, "Loading");
}

/* === SHARED COMPONENT STYLES === */

/* LoadingSpinner — track + kart logo animation */
.loading-spinner {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: var(--space-12);
    gap: var(--space-4);
}

.loading-track {
    position: relative;
    width: 440px;
    height: 112px;
    --track-width: 440px;
    overflow: hidden;
}

.loading-track__rail {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: 6px;
    background: var(--color-gray-300);
    border-radius: var(--radius-full);
}

.loading-track__kart {
    position: absolute;
    bottom: 6px;
    left: 0;
    animation: slideAlongTrack 1.8s ease-in-out infinite;
}

.loading-track__logo {
    width: 88px;
    height: 88px;
    display: block;
    color: var(--mud-palette-text-primary);
}

/* Dark mode: keep the rail visible against a darker background.
   Targets both MudBlazor's body class and the karting-theme-dark class
   bound on MudLayout in MainLayout.razor (belt-and-suspenders).
   The logo itself uses currentColor (driven by --mud-palette-text-primary),
   so it adapts to the active theme without a separate override. */
.mud-theme-dark .loading-track__rail,
.karting-theme-dark .loading-track__rail {
    background: var(--color-gray-600);
}

/* Nav bar karting logo — silhouette that contrasts with the app bar background.
   Sized to fit comfortably inside the 56px AppbarHeight. */
.karting-nav-logo {
    color: var(--mud-palette-text-primary);
    width: 40px;
    height: 40px;
}

/* Auth page hero logo — picks up theme-aware foreground color via currentColor.
   CSS-driven size override clamps to viewport on phones so the 128 px hero
   doesn't crush the rest of the auth card on iPhone SE-class screens. */
.auth-layout-logo {
    color: var(--mud-palette-text-primary);
    width: clamp(80px, 25vw, 128px);
    height: clamp(80px, 25vw, 128px);
}

/* Public shared-setup page logo — primary brand color works on the unauthenticated
   branding strip regardless of system theme. */
.shared-setup-logo {
    color: var(--mud-palette-primary);
}

/* Responsive logo sizing: keep within 56 px AppbarHeight on every viewport. */
@media (max-width: 959px) {
    .karting-nav-logo { width: 40px; height: 40px; }
}
@media (max-width: 599px) {
    .karting-nav-logo { width: 36px; height: 36px; }
    .karting-nav-brand-text { display: none; }
}
/* Brand text is inline by default (≥ 600 px). The class is always present so
   the display:none rule above has something to target without JS. */
.karting-nav-brand-text { display: inline; }

.loading-spinner__message {
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    margin: 0;
}

/* TierLimitBanner */
.tier-limit-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3) var(--space-4);
    padding: var(--space-3) var(--space-4);
    background-color: rgba(245, 158, 11, 0.08);
    border: var(--border-width) solid rgba(245, 158, 11, 0.2);
    border-radius: var(--radius-lg);
}

.tier-limit-banner__content {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    font-size: var(--text-sm);
    color: var(--color-text-primary);
}

.tier-limit-banner__actions {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-shrink: 0;
}

@media (max-width: 480px) {
    .tier-limit-banner__actions {
        flex-basis: 100%;
        justify-content: flex-start;
    }
}

.tier-limit-banner__token-btn {
    padding: var(--space-1) var(--space-3);
    background-color: var(--color-primary-500);
    color: var(--color-text-inverse);
    border: none;
    border-radius: var(--radius-md);
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    cursor: pointer;
}

.tier-limit-banner__upgrade {
    font-size: var(--text-sm);
    color: var(--color-primary-text);
    text-decoration: none;
    font-weight: var(--font-medium);
}

/* FeaturePreview */
.feature-preview {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: var(--space-8);
    gap: var(--space-6);
}

.feature-preview__image-container {
    position: relative;
    border-radius: var(--radius-lg);
    overflow: hidden;
    max-width: 800px;
    width: 100%;
}

.feature-preview__image {
    width: 100%;
    height: auto;
    opacity: 0.5;
    display: block;
}

.feature-preview__fallback {
    width: 100%;
    aspect-ratio: 16 / 9;
    opacity: 0.7;
}

.feature-preview-fallback-svg {
    width: 100%;
    height: 100%;
    display: block;
}

.feature-preview__overlay {
    position: absolute;
    inset: 0;
    backdrop-filter: blur(4px);
    background: rgba(255, 255, 255, 0.3);
}

.feature-preview__content {
    max-width: 480px;
}

.feature-preview__title {
    font-family: var(--font-heading);
    font-size: var(--text-2xl);
    font-weight: var(--font-bold);
    color: var(--color-text-primary);
    margin: 0 0 var(--space-2);
}

.feature-preview__description {
    color: var(--color-text-secondary);
    margin: 0 0 var(--space-6);
    line-height: 1.5;
}

.feature-preview__cta {
    padding: var(--space-3) var(--space-6);
    background-color: var(--color-primary-500);
    color: var(--color-text-inverse);
    border: none;
    border-radius: var(--radius-md);
    font-weight: var(--font-semibold);
    font-size: var(--text-base);
    cursor: pointer;
    transition: background-color var(--transition-fast);
    display: block;
    width: 100%;
    margin-bottom: var(--space-3);
}

.feature-preview__cta:hover {
    background-color: var(--color-primary-600);
}

.feature-preview__learn-more {
    font-size: var(--text-sm);
    color: var(--color-text-muted);
}

/* AI components */
.k-ai-avatar {
    display: inline-flex;
    flex-shrink: 0;
}

.k-ai-avatar--sm svg { width: 32px; height: 32px; }
.k-ai-avatar--md svg { width: 40px; height: 40px; }
.k-ai-avatar--lg svg { width: 56px; height: 56px; }

.k-ai-message-bubble {
    display: flex;
    gap: var(--space-3);
    padding: var(--space-3) 0;
}

.k-ai-message-bubble__avatar {
    flex-shrink: 0;
    padding-top: var(--space-1);
}

.k-ai-message-bubble__body {
    flex: 1;
    min-width: 0;
}

.k-ai-message-bubble__content {
    font-size: var(--text-sm);
    line-height: 1.6;
    color: var(--color-text-primary);
}

.k-ai-message-bubble__timestamp {
    display: block;
    font-size: var(--text-xs);
    color: var(--color-text-muted);
    margin-top: var(--space-1);
}

.k-ai-message-bubble__feedback {
    display: flex;
    gap: var(--space-2);
    margin-top: var(--space-2);
}

.k-ai-message-bubble__feedback-btn {
    background: none;
    border: var(--border-width) solid var(--border-color);
    border-radius: var(--radius-md);
    padding: var(--space-1);
    cursor: pointer;
    color: var(--color-text-muted);
    transition: all var(--transition-fast);
}

.k-ai-message-bubble__feedback-btn:hover {
    border-color: var(--color-primary-500);
    color: var(--color-primary-text);
}

/* KSplitPane — two-pane resizable layout (EPIC §12.2).
   Side-by-side on wide viewports; stacks full-width when container width <
   CollapseBelowPx. Gutter hosts the drag handle and is hidden when stacked.
   The width value lives in inline style (width + flex-basis) so it survives
   re-renders; the component owns its persisted value in localStorage. */
.k-split-pane {
    display: flex;
    flex-direction: row;
    width: 100%;
    min-height: 0;
    align-items: stretch;
    /* Bound the container's height so the children's overflow:auto actually
       engages — without this the content drives the height and both panes
       move together with the document scroll. --karting-appbar-offset is
       published by app.css/appBarScrollHide.js and tracks the visible app
       bar height (collapses to 0 when the mobile bar slides off). */
    height: calc(100vh - var(--karting-appbar-offset, 56px));
    height: calc(100dvh - var(--karting-appbar-offset, 56px));
    overflow: hidden;
}

.k-split-pane--stacked {
    flex-direction: column;
    /* On stacked (narrow) viewports, fall back to natural height so the
       document scroll moves the whole page. The split pane is a single
       column at this point and competes with the bottom-of-page panels
       for the same scroll context. */
    height: auto;
    overflow: visible;
}

.k-split-pane__left {
    flex: 0 0 auto;
    min-width: 0;
    min-height: 0;
    overflow: auto;
}

.k-split-pane--stacked .k-split-pane__left {
    width: 100% !important;
    flex: 0 0 auto !important;
}

.k-split-pane__right {
    flex: 1 1 auto;
    min-width: 0;
    min-height: 0;
    overflow: auto;
}

.k-split-pane--stacked .k-split-pane__right {
    width: 100%;
}

/* 8 px gutter with an always-visible 1 px divider line and a 6-dot
   grab-handle glyph that fades in on hover / focus / drag. The wider hit
   target makes it easier to grab, while the resting divider line lets
   users see where the split edge is without any interaction. */
.k-split-pane__gutter {
    flex: 0 0 8px;
    width: 8px;
    position: relative;
    cursor: ew-resize;
    background-color: transparent;
    user-select: none;
    /* Prevent the browser from claiming the gesture as scroll/zoom while
       the user drags the gutter. Without this, iOS Safari intercepts after
       a few pixels of pointermove. */
    touch-action: none;
}

/* Touch / coarse-pointer devices (iPad, phones): expand the hit target so
   the gutter is reachable without sub-pixel precision. The visual divider
   stays centered on the 1-px line via the ::before pseudo, so widening the
   gutter only enlarges the hit area, not the visible divider. */
@media (pointer: coarse) {
    .k-split-pane__gutter {
        flex: 0 0 24px;
        width: 24px;
    }
}

/* Always-on 1 px divider line — subtle at rest so the split edge is
   visible even before the user hovers. */
.k-split-pane__gutter::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 1px;
    background-color: var(--mud-palette-divider);
    transition: background-color 120ms ease, width 120ms ease;
}

/* 6-dot grab-handle glyph — fades in on hover / focus / drag to signal
   the handle is draggable. Pointer-events none so it doesn't block hits. */
.k-split-pane__gutter::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 4px;
    height: 32px;
    background-image: radial-gradient(
        circle at center,
        var(--mud-palette-action-disabled) 0.8px,
        transparent 1.6px
    );
    background-size: 4px 8px;
    background-repeat: repeat-y;
    opacity: 0;
    transition: opacity 120ms ease;
    pointer-events: none;
}

.k-split-pane__gutter:hover::before,
.k-split-pane__gutter:focus-visible::before,
.k-split-pane__gutter--dragging::before {
    width: 2px;
    background-color: var(--mud-palette-primary);
}

.k-split-pane__gutter:hover::after,
.k-split-pane__gutter:focus-visible::after,
.k-split-pane__gutter--dragging::after {
    opacity: 1;
    background-image: radial-gradient(
        circle at center,
        var(--mud-palette-primary) 0.8px,
        transparent 1.6px
    );
}

.k-split-pane__gutter:focus-visible {
    outline: none;
}

.k-split-pane--stacked .k-split-pane__gutter {
    display: none;
}

/* Applied to <body> during an active drag so the cursor stays ew-resize even
   when the pointer leaves the gutter element, and text selection doesn't
   flicker while dragging. */
.k-split-pane--dragging,
.k-split-pane--dragging * {
    cursor: ew-resize !important;
    user-select: none !important;
}

@media print {
    .k-split-pane {
        flex-direction: column !important;
    }
    .k-split-pane__left,
    .k-split-pane__right {
        width: 100% !important;
        flex: 0 0 auto !important;
    }
    .k-split-pane__gutter {
        display: none !important;
    }
}

/* Drill-down stacked mode: a sticky back affordance pinned to the top of the
   right pane. Hidden by default; only shown when KSplitPane is both stacked
   AND drill-down is engaged. The component sets display:none on whichever
   pane is inactive, so the visible pane (with back bar above it) fills the
   viewport without competing with its sibling. */
.k-split-pane__back-bar {
    position: sticky;
    /* Anchor below the iOS notch in case the app bar has scroll-hidden away
       above this back-bar — keeps the Back affordance below the system clock. */
    top: var(--safe-top);
    z-index: 1;
    background-color: var(--mud-palette-surface);
    border-bottom: 1px solid var(--mud-palette-divider);
    padding: 4px 8px;
    display: none;
}

.k-split-pane--stacked.k-split-pane--drilldown .k-split-pane__back-bar {
    display: block;
}

/* CornerDataTable — horizontal scroll container so the table never bleeds
   past its parent card on narrow viewports. Combined with MudTable's stacked-
   card mode below the Md breakpoint, this keeps the table inside its card
   at every viewport width. */
.corner-data-table__shell {
    container-type: inline-size;
}

.corner-data-table__scroll {
    width: 100%;
    overflow-x: auto;
}

@media (min-width: 960px) {
    .corner-data-table__scroll table {
        min-width: 720px;
    }
}

/* When the host container is narrower than 720 px (typical inside a 640 px
   split-pane right pane on 1280 px viewports), drop the min-width so the
   table reflows in place instead of forcing horizontal scroll. Browsers
   without container-query support fall through to the @media rule above
   and keep the previous behaviour. */
@container (max-width: 720px) {
    .corner-data-table__scroll table {
        min-width: 0;
    }
}

/* SessionCard — selected state for master-detail mode (SessionList).
   Subtle left-edge highlight in the active driver accent so the user can
   tell which row drives the right pane. */
.session-card--selected {
    border-left: 3px solid var(--mud-palette-primary);
}

/* SessionCard composes .k-interactive-card, which now owns the hover-lift
   (transform-only, @media hover:hover-gated — see animations.css). The
   previous .session-card transition + :hover lift here duplicated that and
   animated box-shadow; removed to keep one source of truth. */

/* RaceDaySetupList mobile cards — tap feedback (RDS-006). MudCard 7.x has
   no Ripple parameter, so use an :active state. Same global-CSS rationale
   as .session-card: the class lands on the MudCard's rendered root div,
   which is not tagged with the scoped-CSS attribute. */
.race-day-plan-card {
    transition: transform 120ms ease-out, box-shadow 120ms ease-out, background-color 120ms ease-out;
}

.race-day-plan-card:active {
    transform: scale(0.99);
    background-color: var(--mud-palette-action-default-hover);
}

@media (hover: hover) {
    .race-day-plan-card:hover {
        box-shadow: var(--shadow-md);
    }
}

/* KBreadcrumb */
.k-breadcrumb {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
    padding: var(--space-2) 0;
}

.k-breadcrumb__link {
    color: var(--color-text-muted);
    text-decoration: none;
}

.k-breadcrumb__link:hover {
    color: var(--color-primary-text);
}

.k-breadcrumb__sep {
    color: var(--color-text-muted);
}

.k-breadcrumb__current {
    color: var(--color-text-secondary);
    font-weight: var(--font-medium);
}

/* KAccordionSection - now uses MudExpansionPanels */

/* ProcessingStatusBadge + ProcessingStatusPanel — migrated to MudIconButton
   and MudDrawer (Variant.Temporary). MudBlazor supplies the overlay scrim,
   ESC handling, focus trap, and ARIA semantics. */

/* === AUTH LAYOUT & FORM STYLES === */

.auth-layout {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    min-height: 100dvh;
    padding: var(--space-4);
    background-color: var(--color-gray-50);
}

.auth-layout__card {
    width: 100%;
    max-width: 420px;
    background-color: var(--color-surface);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-lg);
    padding: var(--space-8);
}

.auth-layout__logo {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    margin-bottom: var(--space-6);
}

.auth-layout__title {
    font-family: var(--font-heading);
    font-size: var(--text-2xl);
    font-weight: var(--font-bold);
    color: var(--color-text-primary);
}

.auth-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.auth-form__heading {
    font-family: var(--font-heading);
    font-size: var(--text-xl);
    font-weight: var(--font-semibold);
    color: var(--color-text-primary);
    margin: 0 0 var(--space-1);
    text-align: center;
}

.auth-form__text {
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
    margin: 0 0 var(--space-4);
    text-align: center;
    line-height: 1.5;
}

.auth-form__field {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.auth-form__label {
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    color: var(--color-text-primary);
}

.auth-form__input {
    padding: var(--space-2) var(--space-3);
    border: var(--border-width) solid var(--border-color);
    border-radius: var(--radius-md);
    font-size: var(--text-base);
    font-family: var(--font-body);
    color: var(--color-text-primary);
    background-color: var(--color-surface);
    transition: border-color var(--transition-fast);
    width: 100%;
    box-sizing: border-box;
}

.auth-form__input:focus {
    outline: none;
    border-color: var(--color-primary-500);
    box-shadow: 0 0 0 3px rgba(20, 126, 251, 0.1);
}

.auth-form__input--code {
    text-align: center;
    font-family: var(--font-mono);
    font-size: var(--text-2xl);
    letter-spacing: 0.5em;
    padding: var(--space-3);
}

.auth-form__validation {
    font-size: var(--text-sm);
    color: var(--color-error);
}

.auth-form__checkbox {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    font-size: var(--text-sm);
    color: var(--color-text-primary);
}

.auth-form__checkbox input[type="checkbox"] {
    margin-top: 2px;
    accent-color: var(--color-primary-500);
}

.auth-form__link-row {
    display: flex;
    justify-content: flex-end;
}

.auth-form__link {
    font-size: var(--text-sm);
    color: var(--color-primary-text);
    text-decoration: none;
    font-weight: var(--font-medium);
}

.auth-form__link:hover {
    text-decoration: underline;
}

.auth-form__error {
    padding: var(--space-2) var(--space-3);
    background-color: rgba(239, 68, 68, 0.08);
    border: var(--border-width) solid rgba(239, 68, 68, 0.2);
    border-radius: var(--radius-md);
    font-size: var(--text-sm);
    color: var(--color-error);
}

.auth-form__submit {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    width: 100%;
    padding: var(--space-2) var(--space-4);
    background-color: var(--color-primary-500);
    color: var(--color-text-inverse);
    border: none;
    border-radius: var(--radius-md);
    font-weight: var(--font-semibold);
    font-size: var(--text-base);
    cursor: pointer;
    transition: background-color var(--transition-fast);
    text-decoration: none;
    text-align: center;
}

.auth-form__submit:hover:not(:disabled) {
    background-color: var(--color-primary-600);
}

.auth-form__submit:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.auth-form__submit--link {
    display: inline-block;
    margin-top: var(--space-4);
}

.auth-form__spinner {
    display: inline-block;
    width: 16px;
    height: 16px;
    border: 2px solid rgba(255, 255, 255, 0.3);
    border-top-color: white;
    border-radius: 50%;
    animation: spin 0.6s linear infinite;
}

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

.auth-form__divider {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin: var(--space-4) 0;
}

.auth-form__divider::before,
.auth-form__divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background-color: var(--border-color);
}

.auth-form__divider span {
    font-size: var(--text-xs);
    color: var(--color-text-muted);
    white-space: nowrap;
}

.auth-form__oauth {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.auth-form__oauth-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    width: 100%;
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-md);
    font-weight: var(--font-medium);
    font-size: var(--text-sm);
    cursor: pointer;
    transition: opacity var(--transition-fast);
}

.auth-form__oauth-btn:hover:not(:disabled) {
    opacity: 0.9;
}

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

.auth-form__oauth-btn--google {
    background-color: var(--color-surface);
    color: var(--color-text-primary);
    border: var(--border-width) solid var(--border-color);
}

.auth-form__oauth-btn--apple {
    background-color: #000000;
    color: #ffffff;
    border: var(--border-width) solid #000000;
}

.auth-form__footer {
    text-align: center;
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
    margin-top: var(--space-4);
}

.auth-form__success {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--space-3);
    padding: var(--space-4) 0;
}

.auth-form__error-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--space-3);
    padding: var(--space-4) 0;
}

.auth-form__qr {
    display: flex;
    justify-content: center;
    margin: var(--space-4) 0;
}

.auth-form__qr-container {
    width: 200px;
    height: 200px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: var(--border-width) solid var(--border-color);
    border-radius: var(--radius-md);
    padding: var(--space-2);
}

.auth-form__manual-entry {
    text-align: center;
    margin-bottom: var(--space-4);
}

.auth-form__secret {
    display: inline-block;
    padding: var(--space-2) var(--space-3);
    background-color: var(--color-gray-50);
    border: var(--border-width) solid var(--border-color);
    border-radius: var(--radius-md);
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    letter-spacing: 0.1em;
    word-break: break-all;
    user-select: all;
}

/* === COMMON BUTTON STYLES === */

.btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-4);
    font-size: var(--text-sm);
    font-weight: var(--font-semibold);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: all var(--transition-fast);
    border: var(--border-width) solid transparent;
    text-decoration: none;
    font-family: var(--font-body);
}

.btn--primary {
    background-color: var(--color-primary-500);
    color: var(--color-text-inverse);
}

.btn--primary:hover:not(:disabled) { background-color: var(--color-primary-600); }

.btn--secondary {
    background-color: var(--color-surface);
    color: var(--color-text-primary);
    border-color: var(--border-color);
}

.btn--secondary:hover:not(:disabled) { background-color: var(--color-gray-50); }

.btn--danger {
    background-color: var(--color-error);
    color: var(--color-text-inverse);
}

.btn--danger:hover:not(:disabled) { opacity: 0.9; }

.btn--danger-ghost {
    background: none;
    border: none;
    color: var(--color-text-muted);
    padding: var(--space-1);
}

.btn--danger-ghost:hover { color: var(--color-error); }

.btn--small { padding: var(--space-1) var(--space-2); font-size: var(--text-xs); }
.btn--lg { padding: var(--space-3) var(--space-6); font-size: var(--text-base); }
.btn--icon { padding: var(--space-1); min-width: 32px; min-height: 32px; justify-content: center; }

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

/* Overlay backdrop */
.overlay-backdrop {
    position: fixed;
    inset: 0;
    background-color: rgba(0, 0, 0, 0.3);
    z-index: var(--z-modal);
    display: flex;
    justify-content: flex-end;
}

/* Slide-over Panel */
.slide-over {
    width: 400px;
    max-width: 90vw;
    height: 100vh;
    height: 100dvh;
    background-color: var(--color-surface);
    box-shadow: var(--shadow-xl);
    display: flex;
    flex-direction: column;
}

.slide-over__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-4);
    border-bottom: var(--border-width) solid var(--border-color);
}

.slide-over__header h3 { margin: 0; font-size: var(--text-lg); }

.slide-over__close {
    background: none;
    border: none;
    cursor: pointer;
    color: var(--color-text-muted);
    font-size: var(--text-lg);
}

.slide-over__body {
    flex: 1;
    overflow-y: auto;
    padding: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

/* === KART LIST === */

.kart-list__grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: var(--space-3);
}

/* KartCard */
.kart-card {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-4);
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--border-color);
    border-radius: var(--radius-lg);
    text-decoration: none;
    color: inherit;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.kart-card:hover {
    border-color: var(--color-primary-300);
    box-shadow: var(--shadow-sm);
}

.kart-card__icon {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    background-color: var(--color-primary-50);
    border-radius: var(--radius-lg);
}

.kart-card__body { flex: 1; min-width: 0; }

.kart-card__name {
    display: block;
    font-weight: var(--font-semibold);
    font-size: var(--text-sm);
    color: var(--color-text-primary);
}

.kart-card__class {
    display: block;
    font-size: var(--text-xs);
    color: var(--color-text-secondary);
    margin-top: 2px;
}

.kart-card__arrow {
    flex-shrink: 0;
    color: var(--color-text-muted);
}

/* === KART DETAIL === */

.kart-detail__info {
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--border-color);
    border-radius: var(--radius-lg);
    padding: var(--space-4);
    margin-bottom: var(--space-4);
}

.kart-detail__row {
    display: flex;
    justify-content: space-between;
    padding: var(--space-2) 0;
    border-bottom: var(--border-width) solid var(--border-color);
}

.kart-detail__row:last-child { border-bottom: none; }

.kart-detail__label {
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
}

.kart-detail__value {
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    color: var(--color-text-primary);
}

.kart-detail__section {
    margin-bottom: var(--space-4);
}

.kart-detail__section-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: var(--space-3);
}

.kart-detail__section-title {
    font-family: var(--font-heading);
    font-size: var(--text-lg);
    font-weight: var(--font-semibold);
    color: var(--color-text-primary);
    margin: 0 0 var(--space-3);
}

.kart-detail__setup-summary {
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--border-color);
    border-radius: var(--radius-lg);
    padding: var(--space-4);
    margin-bottom: var(--space-3);
}

.kart-detail__empty-text {
    font-size: var(--text-sm);
    color: var(--color-text-muted);
}

.kart-detail__class-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-2) 0;
    border-bottom: var(--border-width) solid var(--border-color);
}

.kart-detail__class-name {
    font-weight: var(--font-medium);
    font-size: var(--text-sm);
}

.kart-detail__class-weight {
    font-size: var(--text-xs);
    color: var(--color-text-secondary);
    margin-left: var(--space-2);
}

.kart-detail__add-class {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-top: var(--space-3);
    padding: var(--space-3);
    background-color: var(--color-gray-50);
    border-radius: var(--radius-md);
}

.kart-detail__add-class-actions {
    display: flex;
    gap: var(--space-2);
    justify-content: flex-end;
}

.kart-detail__edit-section {
    background-color: var(--color-gray-50);
    border-radius: var(--radius-lg);
    padding: var(--space-4);
    margin-bottom: var(--space-4);
}

.kart-detail__edit-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-3);
}

.kart-detail__edit-actions {
    display: flex;
    gap: var(--space-2);
    justify-content: flex-end;
    margin-top: var(--space-3);
}

/* === SETUP EDITOR === */

.setup-editor {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
    /* Guard against horizontal scroll on narrow viewports when the collapsed
       summary text in an accordion header is longer than the viewport width. */
    min-width: 0;
    max-width: 100%;
    overflow-x: hidden;
}

/* The collapsed accordion header wraps the summary in a MudText that lives
   inside .mud-expand-panel-text. By default MudBlazor gives that element
   `flex: 1 1 auto` with no `min-width: 0`, which means flex children cannot
   shrink below their intrinsic content size and long summary strings spill
   out past the panel edge — very visible on mobile widths. Forcing
   `min-width: 0` here lets the ellipsis inside SetupParameterGroup actually
   truncate the string, and hiding horizontal overflow prevents any residual
   overrun from pushing the page off-canvas. */
.setup-editor .mud-expand-panel .mud-expand-panel-header {
    /* The header is a flex row containing .mud-expand-panel-text and the
       chevron icon. Without min-width: 0 on the header, the icon can be
       pushed off-screen by a wide text child. */
    min-width: 0;
    overflow: hidden;
}

.setup-editor .mud-expand-panel .mud-expand-panel-header .mud-expand-panel-text {
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
}

.setup-editor__footer {
    position: sticky;
    bottom: 0;
    padding: var(--space-4);
    padding-bottom: calc(var(--space-4) + var(--safe-bottom));
    background-color: var(--color-surface);
    border-top: var(--border-width) solid var(--border-color);
    display: flex;
    justify-content: flex-end;
    /* Keep the Save button surface above any nested cards/inputs that
       would otherwise scroll over the sticky bar. */
    z-index: 2;
}

.setup-editor__toast {
    padding: var(--space-2) var(--space-4);
    background-color: var(--color-success);
    color: var(--color-text-inverse);
    border-radius: var(--radius-md);
    font-size: var(--text-sm);
    font-weight: var(--font-semibold);
    margin-bottom: var(--space-4);
    text-align: center;
}

/* Setup Parameter Input */
.setup-param {
    transition: opacity var(--transition-base), background-color var(--transition-base);
}

.setup-param--locked {
    opacity: 0.7;
    background-color: var(--color-surface-locked, var(--color-gray-50));
    padding: var(--space-2);
    border-radius: var(--radius-md);
}

/* Header label inside .setup-param — flexes to fill remaining width but
   word-breaks long parameter names so long display labels don't push the
   lock/help icons off-row. */
.setup-param__label {
    font-weight: var(--font-medium);
    flex: 1 1 auto;
    min-width: 0;
    word-break: break-word;
}

@media (max-width: 768px) {
    .kart-detail__edit-grid { grid-template-columns: 1fr; }
}

/* === DASHBOARD === */

.dashboard__onboarding {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: var(--space-12) var(--space-4);
    text-align: center;
}

.dashboard__onboarding-heading {
    font-family: var(--font-heading);
    font-size: var(--text-3xl);
    font-weight: var(--font-bold);
    color: var(--color-text-primary);
    margin: 0 0 var(--space-2);
}

.dashboard__onboarding-text {
    font-size: var(--text-lg);
    color: var(--color-text-secondary);
    margin: 0 0 var(--space-8);
}

.dashboard__onboarding-cards {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    width: 100%;
    max-width: 520px;
}

.dashboard__dynamic-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: var(--space-4);
    margin-bottom: var(--space-6);
}

.dashboard__section {
    margin-bottom: var(--space-6);
}

.dashboard__section-title {
    font-family: var(--font-heading);
    font-size: var(--text-lg);
    font-weight: var(--font-semibold);
    color: var(--color-text-primary);
    margin: 0 0 var(--space-3);
}

.dashboard__stats-row {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: var(--space-4);
    margin-bottom: var(--space-6);
}

.dashboard__quick-actions {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: var(--space-3);
}

/* Dashboard Card */
.dashboard-card {
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--border-color);
    border-radius: var(--radius-lg);
    padding: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.dashboard-card__header {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.dashboard-card__badge {
    font-size: var(--text-xs);
    font-weight: var(--font-semibold);
    padding: 2px var(--space-2);
    border-radius: var(--radius-full);
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.dashboard-card__badge--info {
    background-color: var(--color-primary-50);
    color: var(--color-primary-text);
}

.dashboard-card__badge--warning {
    background-color: rgba(245, 158, 11, 0.1);
    color: var(--color-warning);
}

.dashboard-card__badge--success {
    background-color: rgba(34, 197, 94, 0.1);
    color: var(--color-success);
}

.dashboard-card__badge--secondary {
    background-color: rgba(6, 182, 212, 0.1);
    color: var(--color-secondary-500);
}

.dashboard-card__title {
    font-family: var(--font-heading);
    font-size: var(--text-base);
    font-weight: var(--font-semibold);
    color: var(--color-text-primary);
    margin: 0;
}

.dashboard-card__detail {
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
    margin: 0;
    line-height: 1.5;
}

.dashboard-card__cta {
    display: inline-flex;
    align-items: center;
    align-self: flex-start;
    margin-top: var(--space-1);
    padding: var(--space-1) var(--space-3);
    font-size: var(--text-sm);
    font-weight: var(--font-semibold);
    color: var(--color-text-inverse);
    background-color: var(--color-primary-500);
    border-radius: var(--radius-md);
    text-decoration: none;
    transition: background-color var(--transition-fast);
}

.dashboard-card__cta:hover {
    background-color: var(--color-primary-600);
}

/* Recent Changes Timeline */
.dashboard-timeline {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--border-color);
    border-radius: var(--radius-lg);
    padding: var(--space-3);
}

.dashboard-timeline__entry {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) 0;
    border-bottom: var(--border-width) solid var(--border-color);
}

.dashboard-timeline__entry:last-child {
    border-bottom: none;
}

.dashboard-timeline__badge {
    font-size: var(--text-xs);
    font-weight: var(--font-semibold);
    padding: 2px var(--space-2);
    border-radius: var(--radius-full);
    white-space: nowrap;
    flex-shrink: 0;
}

.dashboard-timeline__badge--manual {
    background-color: var(--color-primary-50);
    color: var(--color-primary-text);
}

.dashboard-timeline__badge--plan {
    background-color: rgba(34, 197, 94, 0.1);
    color: var(--color-success);
}

.dashboard-timeline__badge--favorite {
    background-color: rgba(139, 92, 246, 0.1);
    color: #8B5CF6;
}

.dashboard-timeline__badge--mechanic {
    background-color: rgba(6, 182, 212, 0.1);
    color: var(--color-secondary-500);
}

.dashboard-timeline__badge--default {
    background-color: var(--color-gray-100);
    color: var(--color-text-secondary);
}

.dashboard-timeline__summary {
    flex: 1;
    font-size: var(--text-sm);
    color: var(--color-text-primary);
    min-width: 0;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

.dashboard-timeline__date {
    font-size: var(--text-xs);
    color: var(--color-text-muted);
    white-space: nowrap;
    flex-shrink: 0;
}

/* StatCard */
.stat-card {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--border-color);
    border-radius: var(--radius-lg);
    padding: var(--space-4);
    text-decoration: none;
    color: inherit;
    transition: border-color var(--transition-fast);
}

.stat-card--link:hover {
    border-color: var(--color-primary-300);
}

.stat-card__icon {
    flex-shrink: 0;
}

.stat-card__body {
    display: flex;
    flex-direction: column;
}

.stat-card__value {
    font-family: var(--font-heading);
    font-size: var(--text-2xl);
    font-weight: var(--font-bold);
    color: var(--color-text-primary);
    line-height: 1;
}

.stat-card__label {
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
}

/* SubscriptionStatusWidget */
.sub-widget {
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--border-color);
    border-radius: var(--radius-lg);
    padding: var(--space-4);
}

.sub-widget__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: var(--space-3);
}

.sub-widget__tier {
    font-size: var(--text-sm);
    font-weight: var(--font-bold);
    padding: 2px var(--space-2);
    border-radius: var(--radius-full);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.sub-widget__tier--free {
    background-color: var(--color-gray-100);
    color: var(--color-text-secondary);
}

.sub-widget__tier--pro {
    background-color: var(--color-primary-50);
    color: var(--color-primary-text);
}

.sub-widget__tier--max {
    background: linear-gradient(135deg, var(--color-primary-50), rgba(6, 182, 212, 0.1));
    color: var(--color-primary-text);
}

.sub-widget__tier--unlimited {
    background: linear-gradient(135deg, rgba(139, 92, 246, 0.1), var(--color-primary-50));
    color: #8B5CF6;
}

.sub-widget__upgrade {
    font-size: var(--text-sm);
    font-weight: var(--font-semibold);
    color: var(--color-primary-text);
    text-decoration: none;
}

.sub-widget__upgrade:hover {
    text-decoration: underline;
}

.sub-widget__usage {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.sub-widget__usage-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.sub-widget__usage-label {
    font-size: var(--text-xs);
    color: var(--color-text-secondary);
}

.sub-widget__usage-count {
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    color: var(--color-text-primary);
    font-family: var(--font-mono);
}

.sub-widget__bar {
    height: 4px;
    background-color: var(--color-gray-100);
    border-radius: var(--radius-full);
    overflow: hidden;
}

.sub-widget__bar-fill {
    height: 100%;
    background-color: var(--color-primary-500);
    border-radius: var(--radius-full);
    transition: width var(--transition-base);
}

.sub-widget__bar-fill--warning {
    background-color: var(--color-warning);
}

.sub-widget__bar-fill--full {
    background-color: var(--color-error);
}

/* QuickActionCard */
.quick-action-card {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-4);
    background-color: var(--color-surface);
    border: var(--border-width) solid var(--border-color);
    border-radius: var(--radius-lg);
    text-decoration: none;
    color: inherit;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
    cursor: pointer;
}

.quick-action-card:hover {
    border-color: var(--color-primary-300);
    box-shadow: var(--shadow-sm);
}

.quick-action-card__icon {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    background-color: var(--color-primary-50);
    border-radius: var(--radius-lg);
}

.quick-action-card__body {
    flex: 1;
    min-width: 0;
}

.quick-action-card__title {
    display: block;
    font-weight: var(--font-semibold);
    font-size: var(--text-sm);
    color: var(--color-text-primary);
}

.quick-action-card__subtitle {
    display: block;
    font-size: var(--text-xs);
    color: var(--color-text-secondary);
    margin-top: 2px;
}

.quick-action-card__arrow {
    flex-shrink: 0;
    color: var(--color-text-muted);
}

/* NotFound */
.not-found-page {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: var(--space-16);
    gap: var(--space-3);
}

.not-found-page h1 {
    font-family: var(--font-heading);
    font-size: var(--text-3xl);
}

.not-found-page a {
    color: var(--color-primary-text);
    font-weight: var(--font-medium);
}

/* === PIT MECHANIC CHAT === */

.streaming-cursor {
    animation: cursorBlink 800ms infinite;
    font-weight: bold;
}

.chat-message-content {
    word-wrap: break-word;
    overflow-wrap: break-word;
    line-height: 1.5;
}

.chat-message-content p {
    margin: 0 0 0.5rem 0;
}

.chat-message-content p:last-child {
    margin-bottom: 0;
}

.run-entry-card {
    border-left: 3px solid var(--mud-palette-secondary);
}

.k-typing-bubble {
    /* Tighter padding than a normal AI bubble — the dots are centered
       vertically and the bubble should feel "compact" rather than empty. */
    padding-top: 10px !important;
    padding-bottom: 10px !important;
    min-width: 56px;
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

.k-typing-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background-color: var(--mud-palette-text-secondary);
    display: inline-block;
    animation: typingDot 1200ms infinite ease-in-out;
}

.k-typing-dot:nth-child(2) {
    animation-delay: 150ms;
}

.k-typing-dot:nth-child(3) {
    animation-delay: 300ms;
}

/* Paint the NavMenu "Karts" link icon with the karting logo via CSS mask
   so it can inherit MudBlazor palette vars (default gray, primary when
   active) and pixel-align with sibling Material icons. NavMenu passes a
   tiny placeholder SVG as Icon so MudBlazor renders .mud-nav-link-icon,
   and we override its background + hide the inner <svg>. */
.nav-kart-link .mud-nav-link-icon {
    display: inline-flex;
    width: 24px;
    height: 24px;
    background-color: var(--mud-palette-action-default);
    -webkit-mask-image: url('../images/karting-logo-transparent.png');
    mask-image: url('../images/karting-logo-transparent.png');
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
}

.nav-kart-link .mud-nav-link-icon > svg {
    display: none;
}

.mud-nav-link.nav-kart-link.active .mud-nav-link-icon,
.nav-kart-link.active .mud-nav-link-icon {
    background-color: var(--mud-palette-primary);
}

/* When the nested Add Motor drawer is open, slide the Add Kart drawer 80px
   to the left so the motor drawer can land at the kart drawer's original
   right:0 position. Uses transform (not right/margin) because:
   - MudBlazor's `.mud-drawer.mud-drawer-temporary` base rule already has
     `transition: transform 225ms cubic-bezier(0, 0, 0.2, 1) 0ms`, so the
     transform change is animated automatically with no custom keyframes.
   - transform is independent of `right`, so it doesn't interfere with
     MudBlazor's own slide-in/out keyframe animation on the kart drawer. */
.mud-drawer.mud-drawer-temporary.kart-drawer--motor-open {
    transform: translateX(-80px) !important;
}

/* Two ~400px drawers can't fit side-by-side on a 390px phone, so on
   narrow viewports hide the kart drawer while the motor drawer is open.
   Visibility (not display) keeps the form mounted so its in-progress
   field values survive the round trip. */
@media (max-width: 480px) {
    .mud-drawer.mud-drawer-temporary.kart-drawer--motor-open {
        transform: none !important;
        visibility: hidden;
    }
}

/* Nested Add Motor drawer renders in front of the kart drawer.
   - The drawer is rendered with Overlay="false" so MudBlazor doesn't add
     its own overlay (a custom MudOverlay sibling at z-index 1303 dims the
     kart drawer behind us instead).
   - z-index 1304 layers it ABOVE the custom overlay (1303), the kart
     drawer (runtime: calc(--mud-zindex-appbar + 2) = 1302 with the
     default appbar value of 1300), and the kart drawer's built-in
     overlay (1301). Still well below the dialog layer (1400). */
.mud-drawer.mud-drawer-temporary.nested-motor-drawer {
    z-index: 1304;
}

/* Ensure MudBlazor popovers (used by MudSelect, MudAutocomplete) render
   above both the nested motor drawer (z-index 1304) and the MudDialog
   layer (z-index 1400). Without this, dropdown items appear behind
   drawers/dialogs and are invisible/inaccessible. */
.mud-popover-provider .mud-popover {
    z-index: 1500 !important;
}

/* Mobile auto-hide for the top app bar.
   The JS module window.karting.appBarScrollHide toggles the .karting-appbar-hidden
   class on MudAppBar based on scroll direction. This media query ensures the
   actual translate transform only applies on mobile viewports (< 960px) — on
   desktop the class is harmless, so we never need to track viewport changes
   in JS or re-register the listener on resize. */
/* iOS notch / home-indicator handling. padding-top pushes the bar's content
   below the system status bar / dynamic island when the page is launched
   from the iOS home screen with apple-mobile-web-app-status-bar-style
   = black-translucent. The padding inflates the bar's offsetHeight, which
   appBarScrollHide.js publishes as --karting-appbar-height — so all sticky
   elements anchored to --karting-appbar-offset automatically clear the
   notch as well. The .mud-layout left/right padding handles landscape
   notches on iPhone. */
.mud-appbar {
    padding-top: var(--safe-top);
    transition: transform 220ms ease-in-out;

    /* Opaque top bar (pre-batch baseline). MudThemeProvider redefines
       --mud-palette-appbar-background per theme, so this single declaration
       covers light and dark with no per-theme override. No backdrop-filter:
       re-blurring the framebuffer behind a position:fixed bar on every scroll
       frame and route transition was the primary site-wide paint regression. */
    background-color: var(--mud-palette-appbar-background);
}

@media (max-width: 959px) {
    .mud-appbar.karting-appbar-hidden {
        transform: translateY(-100%);
    }
}

/* --karting-appbar-offset is the distance from the top of the viewport to
   the bottom edge of the visible app bar. Sticky elements (KB topnav,
   KB sticky section header) anchor to it so they sit flush against the
   bar with zero visible gap, regardless of how the brand logo inflates
   the toolbar past MudBlazor's default height.

   --karting-appbar-height is declared in design-tokens.css :root at 56px
   (matching KartingTheme.AppbarHeight) and refined by appBarScrollHide.js
   once it measures the rendered .mud-appbar. The bar's padding-top
   = var(--safe-top), so the measured height already includes the iOS
   notch — anything anchoring to --karting-appbar-offset clears the
   notch automatically.

   On mobile, when the app bar slides off-screen via translateY(-100%),
   the offset collapses to var(--safe-top) — only the bar-height portion
   disappears; the iOS notch space stays so sticky elements keep clear
   of the system status bar. The collapse is gated to mobile because the
   desktop bar never actually translates. */
.mud-layout {
    --karting-appbar-offset: var(--karting-appbar-height, 56px);
    padding-left: var(--safe-left);
    padding-right: var(--safe-right);
}

@media (max-width: 959px) {
    .mud-layout.karting-appbar-hidden-state {
        --karting-appbar-offset: var(--safe-top);
    }
}

/* Snackbar provider lifted above every drawer in the stack — the nested
   motor drawer sits at z-index 1304 (above) and the dialog layer at 1400,
   so 1500 guarantees toasts stay visible regardless of context. */
.mud-snackbar-provider {
    z-index: 1500 !important;
}

/* Snackbar sits above the iOS home indicator. */
.mud-snackbar-provider .mud-snackbar {
    padding-bottom: var(--safe-bottom, 0) !important;
}

/* Snackbar entrance polish — overshoots into place with --ease-out-back
   (gentle bounce) so toasts confidently announce themselves rather than
   sliding limply. The duration matches MudBlazor's ShowTransitionDuration
   (200 ms in Program.cs); the easing curve is the only override. */
.mud-snackbar-provider .mud-snackbar {
    animation-timing-function: var(--ease-out-back, cubic-bezier(0.34, 1.46, 0.64, 1)) !important;
}

/* === MODAL / DIALOG SCALE-IN ===
   MudBlazor's default dialog enter is a plain fade, which reads dated
   against contemporary apps (Apple sheets, Linear modals, GitHub
   command palette all spring into existence). Reusing the existing
   scaleIn keyframe gives dialogs a confident "appearing decisively"
   entrance with a 200 ms ease-out, then prefers-reduced-motion replays
   the plain fade. */
/* :not(.k-no-anim) lets a dialog opt out of the scale-in. A scale() transform
   on the dialog distorts getBoundingClientRect()/clientWidth on its descendants
   while it plays, so an in-dialog Leaflet map (LayoutDetailsDialog) mis-measures
   and visibly re-settles after the entrance. That dialog sets Class="k-no-anim".
   Non-map dialogs are unaffected and still scale in. */
.mud-dialog:not(.k-no-anim) {
    animation: scaleIn var(--motion-base, 250ms) var(--ease-out-quart, cubic-bezier(0.16, 1, 0.3, 1)) backwards;
    transform-origin: center center;
}

@media (prefers-reduced-motion: reduce) {
    .mud-dialog {
        animation: fadeIn var(--motion-base, 250ms) ease forwards;
    }
}

/* Utility for sticky bottom CTAs (e.g. mobile setup-editor footer) that need
   to clear the iOS home indicator. */
.k-bottom-safe {
    padding-bottom: var(--safe-bottom);
}

/* Active driver accent band — 3px stripe just below the app bar.
   Positioned fixed so it spans the full viewport width. On mobile,
   it slides with the app bar via the same translateY transition. */
.karting-accent-band {
    position: fixed;
    top: var(--karting-appbar-height, 56px);
    left: 0;
    right: 0;
    height: 3px;
    background-color: var(--karting-active-accent, var(--mud-palette-primary));
    z-index: 1299;
    transition: transform 220ms ease-in-out;
    pointer-events: none;
}

@media (max-width: 959px) {
    .mud-layout.karting-appbar-hidden-state .karting-accent-band {
        transform: translateY(calc(-1 * var(--karting-appbar-height, 56px)));
    }
}

/* Hide the nav menu while NavMenu.razor rehydrates persisted expanded state
   from LocalStorage on first render. Prevents the all-groups-open flash. */
.navmenu--initializing {
    visibility: hidden;
}

/* Driver accent dot — shown next to "Driver Profile" in the nav, tinted to
   match the active driver's accent color. Layout / shape lives here; the
   dynamic color is supplied via the --nav-accent-color CSS variable so the
   markup keeps only one inline property. */
.nav-accent-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    margin-left: 6px;
    vertical-align: middle;
    background-color: var(--nav-accent-color);
}

/* === DATA SOURCE SELECTOR CARDS === */

.data-source-card {
    cursor: pointer;
    border: 2px solid transparent;
    transition: background-color 0.2s, border-color 0.2s, box-shadow 0.2s;
}

.data-source-card:hover {
    background-color: var(--mud-palette-action-hover);
}

.data-source-card.data-source-card--selected {
    border: 2px solid var(--mud-palette-primary);
    background-color: var(--mud-palette-primary-hover);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
}

/* === ACCESSIBILITY === */

/* Focus ring for keyboard-navigable clickable cards (A2) */
.mud-card[tabindex="0"]:focus-visible,
[role="button"][tabindex="0"]:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 2px;
}

/* Accessible data table toggle below charts (A1) */
.chart-data-table {
    margin-top: 0.5rem;
}

.chart-data-table summary {
    cursor: pointer;
    color: var(--mud-palette-text-secondary);
    font-size: 0.75rem;
    padding: 0.25rem 0;
    user-select: none;
}

.chart-data-table summary:hover {
    color: var(--mud-palette-primary);
}

/* === COMMAND PALETTE === */

.command-palette-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    z-index: 1400;
}

.command-palette-overlay {
    position: fixed;
    /* Clamp top to at least safe-top so the overlay never sits under the
       iOS notch on landscape phones where 15vh < safe-top. */
    top: max(15vh, var(--safe-top));
    left: 50%;
    transform: translateX(-50%);
    width: min(640px, 92vw);
    background: var(--mud-palette-surface);
    border-radius: 12px;
    box-shadow: var(--shadow-xl);
    z-index: 1401;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    /* Use dynamic-viewport-height (dvh) so the overlay shrinks when iOS
       Safari's URL bar is visible, and subtract both safe insets so it
       never clips against the home indicator on landscape. */
    max-height: min(70dvh, calc(100vh - var(--safe-top) - var(--safe-bottom)));
}

.command-palette-search-row {
    display: flex;
    align-items: center;
    padding: 0.75rem 1rem;
    border-bottom: 1px solid var(--mud-palette-divider);
    gap: 0.5rem;
}

.command-palette-search-icon {
    color: var(--mud-palette-text-secondary);
    flex-shrink: 0;
}

.command-palette-input {
    flex: 1;
    border: none;
    outline: none;
    background: transparent;
    font-size: 1rem;
    color: var(--mud-palette-text-primary);
    font-family: var(--font-body);
}

.command-palette-input::placeholder {
    color: var(--mud-palette-text-disabled);
}

.command-palette-results {
    list-style: none;
    margin: 0;
    padding: var(--space-1) 0;
    overflow-y: auto;
    flex: 1;
}

.command-palette-item {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    min-height: 44px;
    box-sizing: border-box;
    cursor: pointer;
    color: var(--mud-palette-text-primary);
    font-size: 0.9375rem;
    transition: background-color 0.1s;
}

@media (pointer: coarse) {
    .command-palette-item {
        padding: var(--space-3) var(--space-4);
        min-height: 48px;
    }
}

.command-palette-item:hover,
.command-palette-item--active {
    background: var(--mud-palette-action-hover);
}

.command-palette-item-icon {
    color: var(--mud-palette-text-secondary);
    flex-shrink: 0;
    font-size: 1.1rem !important;
    width: 1.1rem !important;
    height: 1.1rem !important;
}

.command-palette-empty {
    padding: 1.5rem 1rem;
    text-align: center;
    color: var(--mud-palette-text-secondary);
    font-size: 0.875rem;
}

.command-palette-footer {
    display: flex;
    gap: 1.25rem;
    padding: 0.5rem 1rem;
    border-top: 1px solid var(--mud-palette-divider);
    color: var(--mud-palette-text-disabled);
    font-size: 0.75rem;
}

.command-palette-footer kbd {
    font-family: var(--font-mono);
    background: var(--mud-palette-background);
    border: 1px solid var(--mud-palette-divider);
    border-radius: 3px;
    padding: 2px 6px;
    font-size: var(--text-xs);
}

/* TrackMapCanvas — touch-action: none on the inner Leaflet div captures
   one-finger drags as map pans instead of letting the browser interpret
   them as page scrolls. Without this, every attempt to pan the map on a
   phone scrolled the page instead. The figure wrapper keeps the browser
   default so the page is still scrollable via gestures that start above
   or below the map area. */
.track-map-canvas__leaflet {
    touch-action: none;
}

/* Floating chrome layer above the Leaflet container (Phase 1 of the Track
   Map Elevation plan). The wrapper itself is pointer-events: none so map
   gestures pass through any gap; the > * rule opts immediate children back
   in. z-index 450 sits above Leaflet's max layer pane (400). */
.track-map-canvas-chrome {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 450;
}

.track-map-canvas-chrome > * {
    pointer-events: auto;
}

/* MapChromeLayer — slotted positioning primitive used inside a
   TrackMapCanvas ChromeContent slot. Phase 2 of the Track Map Elevation
   plan. Each named slot is pinned to its corner with safe-area-inset
   padding so theatre mode respects iOS notches and Android cutouts. */
.map-chrome-layer {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 450;
}

.map-chrome-slot {
    position: absolute;
    display: flex;
    gap: 8px;
    pointer-events: auto;
    padding: 12px;
    padding-top: max(12px, env(safe-area-inset-top));
    padding-bottom: max(12px, env(safe-area-inset-bottom));
    padding-left: max(12px, env(safe-area-inset-left));
    padding-right: max(12px, env(safe-area-inset-right));
}

.map-chrome-slot--top-left { top: 0; left: 0; }
.map-chrome-slot--top-right { top: 0; right: 0; }
.map-chrome-slot--bottom-left { bottom: 0; left: 0; }
.map-chrome-slot--bottom-right { bottom: 0; right: 0; }
.map-chrome-slot--center {
    top: 50%; left: 50%; transform: translate(-50%, -50%);
    pointer-events: none;
}

/* Frosted-glass chip shared by every map chrome consumer (MapToolbar,
   LapSelector trigger, fullscreen button). Uses MudBlazor surface tokens
   so it matches the active theme automatically. */
.map-chrome-chip {
    background-color: color-mix(in srgb, var(--mud-palette-surface) 92%, transparent);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border-radius: 24px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08), 0 4px 12px rgba(0, 0, 0, 0.12);
    color: var(--mud-palette-text-primary);
}

/* MapInsightSheet — right-anchored docked panel on >=768px, MudDrawer
   bottom-sheet on phones. Used to surface per-corner AI insights when
   a pin is clicked or hovered. Phase 2 of the Track Map Elevation plan. */
.map-insight-sheet--docked {
    position: absolute;
    top: 12px;
    right: 12px;
    bottom: 12px;
    width: clamp(320px, 25%, 600px);
    background: var(--mud-palette-surface);
    border-radius: 12px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
    display: flex;
    flex-direction: column;
    z-index: 460; /* above chrome */
    overflow: hidden;
    animation: insight-slide-in 240ms cubic-bezier(0.4, 0, 0.2, 1);
}
.map-insight-sheet__header {
    display: flex;
    align-items: center;
    padding: 12px 16px;
    border-bottom: 1px solid var(--mud-palette-divider);
}
.map-insight-sheet__body {
    flex: 1 1 auto;
    overflow: auto;
    padding: 16px;
}
@keyframes insight-slide-in {
    from { transform: translateX(110%); opacity: 0; }
    to { transform: translateX(0); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
    .map-insight-sheet--docked { animation: none; }
}

/* LapSelector — row swatch matches the lap colour for that row. */
.lap-selector__swatch {
    display: inline-block;
    width: 18px;
    height: 8px;
    border-radius: 2px;
    margin-left: 8px;
}

/* Compact OverlayToggleBar — used inside MapToolbar's chrome chip.
   Removes the wide button padding so the icon-only group fits the
   floating chip surface. */
.overlay-toggle-bar--compact .mud-icon-button {
    padding: 4px;
}

/* MiniTrackThumbnail — pin-free, gesture-free decorative thumbnail used
   by list pages and cards. The inner TrackMapCanvas paints the polyline
   without satellite tiles for a cleaner look. */
.mini-thumbnail {
    position: relative;
    display: block;
    flex-shrink: 0;
}

.mini-thumbnail__empty {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--mud-palette-text-disabled);
}

/* LayoutGrid / LayoutCard — the grid-of-cards layout selector that
   replaces the collapsed accordion on TrackDetail. Each card embeds a
   mini map, layout metadata, and a kebab menu. The horizontal strip
   variant kicks in at <=767px so phones can still scan layouts. Phase 2
   of the Track Map Elevation plan; wired in Phase 3. */
.layout-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    align-content: flex-start;
}
.layout-card {
    flex: 1 1 240px;
    min-width: 240px;
    max-width: 320px;
    border: 1px solid var(--mud-palette-divider);
    border-radius: 10px;
    overflow: hidden;
    cursor: pointer;
    transition: border-color 120ms ease-out, box-shadow 120ms;
    background: var(--mud-palette-surface);
    position: relative;
    display: flex;
    flex-direction: column;
}
@media (min-width: 1280px) {
    .layout-card {
        max-width: 380px;
    }
}
.layout-card:hover,
.layout-card:focus-visible {
    border-color: var(--mud-palette-primary);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.layout-card--selected {
    border-color: var(--mud-palette-primary);
    border-width: 2px;
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--mud-palette-primary) 20%, transparent);
}
.layout-card__map {
    height: 120px;
    background: var(--mud-palette-background);
    position: relative;
}
.layout-card__map-empty {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--mud-palette-text-disabled);
}
.layout-card__body {
    padding: 8px 12px;
}
.layout-card__title {
    margin-bottom: 4px;
}
.layout-card__menu {
    position: absolute;
    top: 4px;
    right: 4px;
}
.layout-card--add {
    flex: 1 1 240px;
    min-height: 188px;
    border-style: dashed;
}
.layout-strip {
    display: flex;
    gap: 8px;
    overflow-x: auto;
    padding: 4px;
}

/* TrackDetail right-pane map wrapper — fills the KSplitPane right pane
   and gives the embedded TrackMapCanvas a meaningful floor on every
   viewport. Phase 3 of the Track Map Elevation plan. */
.track-detail-map {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: clamp(420px, 66vh, 800px);
}
.track-detail-map > * { flex: 1 1 0; }

/* Rounded outer corners on the large map figure. overflow: hidden clips
   Leaflet tiles and chrome chips to the radius so the wrapper presents
   as a cohesive surface. Chrome chips sit at 12 px from each corner so
   they remain fully visible inside the rounded rect. */
.track-detail-map--rounded .track-map-canvas {
    border-radius: 12px;
    overflow: hidden;
}

/* Auto-sizing corners panel that lives below the rounded map. The native
   <details> element collapses to a single summary row by default and
   only expands when the user opens it, so the right pane keeps its
   vertical space for the map. */
.track-detail-corner-table {
    flex: 0 0 auto;
    margin-top: var(--space-3);
}

/* Corner chip strip pinned bottom-left of the map. Scrolls horizontally
   on phones so a 20-corner layout doesn't overflow the chrome slot. */
.track-detail-corner-strip {
    max-width: min(90vw, 480px);
    overflow-x: auto;
    white-space: nowrap;
}

/* Detailed pin callout — anchored to the right of the top-3 time-losing
   corner pins. Frosted-glass surface so it stays legible over the
   satellite imagery. */
.leaflet-tooltip.track-map-corner-callout {
    background: rgba(20, 126, 251, 0.92);
    color: #fff;
    border: none;
    font-weight: 600;
    font-size: 11px;
    padding: 2px 6px;
    border-radius: 4px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
}
.leaflet-tooltip.track-map-corner-callout::before {
    border-right-color: rgba(20, 126, 251, 0.92);
}

/* TrackMapCanvas overlay hints — keep readable in bright sunlight by using a
   solid background and a 12px floor (bumps further to 13px on phones). The
   keyboard hint sits at the bottom-left, the Azure Maps attribution at the
   bottom-right. Both have pointer-events: none so they never intercept map
   clicks underneath them. */
.track-map-canvas-keyboard-hint {
    position: absolute;
    bottom: 8px;
    left: 8px;
    z-index: 1000;
    pointer-events: none;
    background: rgba(255, 255, 255, 0.92);
    color: #222;
    padding: 4px 8px;
    border-radius: 4px;
    font-size: 12px;
    line-height: 1.2;
}

.track-map-canvas-attribution {
    position: absolute;
    bottom: 5px;
    right: 5px;
    z-index: 1000;
    pointer-events: none;
    color: #222;
    background: rgba(255, 255, 255, 0.92);
    padding: 4px 8px;
    border-radius: 4px;
    font-size: 11px;
    line-height: 1.2;
}

@media (max-width: 600px) {
    .track-map-canvas-keyboard-hint {
        font-size: 13px;
    }
    /* Attribution stays small on mobile — required by Azure Maps ToS, but
       we shouldn't dedicate more screen real estate to it than necessary. */
    .track-map-canvas-attribution {
        font-size: 10px;
        padding: 3px 6px;
    }
}

/* AiCostDashboard summary cards — the h4 dollar values dwarf the body2
   labels on phone-class viewports. Demote to h5 size below 600 px so the
   value/label pair reads as a coherent statistic instead of a billboard. */
@media (max-width: 600px) {
    .ai-cost-headline {
        font-size: 1.125rem;
    }
}

/* Feedback description cells — wrap to 2 lines on phone so the meaning isn't
   lost behind an ellipsis; desktop keeps a single-line preview with a tooltip
   on hover to reveal the full text. */
.feedback-description-clamp {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    word-break: break-word;
}

@media (min-width: 1280px) {
    .feedback-description-clamp {
        display: block;
        max-width: 400px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

/* Pit Mechanic chat page — flex layout anchored to the visible app bar height.
   Using --karting-appbar-offset (which collapses to 0 when the bar slides off)
   prevents the 56 px dead zone that the old "calc(100vh - 320px)" magic number
   produced when the mobile auto-hide triggered. */
.pit-mechanic-page-root {
    display: flex;
    flex-direction: column;
    height: calc(100vh - var(--karting-appbar-offset, 56px));
    height: calc(100dvh - var(--karting-appbar-offset, 56px));
    min-height: 480px;
}

/* On phone-sized viewports, drop the 480 px floor so the page root
   actually shrinks to the dvh-derived height when the iOS soft keyboard
   opens. Without this, min-height: 480px wins over the dvh height,
   pushing the chat input off-screen behind the keyboard. */
@media (max-width: 600px) {
    .pit-mechanic-page-root {
        min-height: 0;
    }
}

.pit-mechanic-chat-area {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

/* Keep the focus/scroll target above the sticky input bar — without
   scroll-padding the keyboard nav (PIT-003) and the auto-scroll on a new
   AI chunk both park the last message under the input bar (PIT-004). 80px
   matches the input-bar height (single-line MudTextField + padding) with
   a touch of slack so the safe-area-bottom on iOS doesn't eat into it. */
#chat-message-list {
    scroll-padding-bottom: 80px;
}

/* Keep the chat input bar in view when the iOS soft keyboard opens.
   100dvh on the page-root already shrinks the chat area, but pinning
   the input via position:sticky guarantees it sits above safe-area-bottom
   even on browsers that don't yet honour dvh. */
.pit-mechanic-chat-area > :last-child,
.pit-mechanic-message-input-bar {
    position: sticky;
    bottom: var(--safe-bottom, 0);
    background-color: var(--mud-palette-surface);
    z-index: 1;
}

/* Minimum 44×44 px tap targets on touch-primary devices (EPIC §12.2 web subset) */
@media (pointer: coarse) {
    .mud-icon-button { min-width: 44px; min-height: 44px; }
}

/* Avoid the iOS Safari 300 ms tap delay on buttons and clickable surfaces.
   `manipulation` allows pinch-zoom while disabling double-tap-to-zoom on the
   element, which is the source of the delay. */
button,
.mud-button-root,
.mud-icon-button,
.mud-list-item,
.cursor-pointer,
[role="button"] {
    touch-action: manipulation;
}

/* ─── Calculations left rail ──────────────────────────────────────────────
   Surfaces every deterministic-engine calculator inside a search-filterable,
   categorised accordion. Lives in app.css (not a *.razor.css scoped file)
   because CalculationsPage's render tree is mostly child components — the
   ::deep scope attribute would not reliably penetrate to the rail's inner
   markup, especially after KSplitPane stacks for narrow viewports. */
.calc-left-rail {
    /* Rail content sizes naturally; the surrounding KSplitPane left pane
       handles its own overflow so the rail does not need height: 100%. */
}

.calc-left-rail__scroll {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.calc-left-rail__scroll:focus {
    outline: none;
}

/* Category section — distinct card with a header button that toggles the
   item list beneath. Styled as an obvious tappable surface so the rail's
   visual hierarchy reads at a glance. */
.calc-cat {
    border: 1px solid var(--mud-palette-divider);
    border-radius: 10px;
    background-color: var(--mud-palette-surface);
    overflow: hidden;
    transition: border-color 120ms ease, box-shadow 120ms ease;
}

.calc-cat--open {
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}

.calc-cat:focus-within {
    border-color: var(--mud-palette-primary);
}

.calc-cat__header {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 12px 14px;
    border: 0;
    background-color: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
    font-size: 0.95rem;
    font-weight: 600;
    cursor: pointer;
    text-align: left;
    transition: background-color 120ms ease;
}

.calc-cat__header:hover {
    background-color: var(--mud-palette-action-default-hover);
}

.calc-cat__header:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 0;
}

.calc-cat--open .calc-cat__header {
    border-bottom: 1px solid var(--mud-palette-divider);
}

.calc-cat__chevron {
    flex: 0 0 auto;
    color: var(--mud-palette-text-secondary);
}

.calc-cat__label {
    flex: 1 1 auto;
    min-width: 0;
}

.calc-cat__count {
    flex: 0 0 auto;
    min-width: 26px;
    height: 22px;
    padding: 0 8px;
    border-radius: 999px;
    background-color: var(--mud-palette-action-default-hover);
    border: 1px solid var(--mud-palette-divider);
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--mud-palette-text-secondary);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
}

.calc-cat__items {
    display: flex;
    flex-direction: column;
    padding: 6px;
    gap: 4px;
    background-color: var(--mud-palette-background);
}

/* Calculator row — rendered as a real button with column-flex content
   (title on top, KB reference on a second line in a smaller secondary
   color). Active state uses the driver accent color (set inline as
   --calc-accent) for the left border and a tinted background, plus a
   subtle box-shadow so the selected row reads from across the rail. */
.calc-left-rail-item {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    width: 100%;
    padding: 10px 12px;
    border: 1px solid transparent;
    background-color: var(--mud-palette-surface);
    color: inherit;
    text-align: left;
    border-left: 3px solid transparent;
    border-radius: 6px;
    cursor: pointer;
    transition: background-color 120ms ease, border-color 120ms ease, box-shadow 120ms ease;
    /* Tap target floor — applies to all viewports, not just touch */
    min-height: 44px;
}

.calc-left-rail-item:hover {
    background-color: var(--mud-palette-action-default-hover);
    border-color: var(--mud-palette-divider);
}

.calc-left-rail-item:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 0;
}

@media (prefers-reduced-motion: reduce) {
    .calc-left-rail-item, .calc-cat__header { transition: none; }
}

.calc-left-rail-item--active {
    background-color: var(--mud-palette-action-default-hover);
    border-color: var(--mud-palette-divider);
    border-left-color: var(--calc-accent, var(--mud-palette-primary));
    box-shadow: inset 0 0 0 1px var(--calc-accent, var(--mud-palette-primary));
}

.calc-left-rail-item__title {
    font-size: 0.92rem;
    line-height: 1.25;
    font-weight: 500;
    word-break: break-word;
    width: 100%;
}

.calc-left-rail-item__kb {
    font-size: var(--text-xs);
    line-height: 1.2;
    color: var(--mud-palette-text-secondary);
}

/* On narrow viewports the rail is full-width thanks to KSplitPane stacking;
   give the items a touch more breathing room so they read as proper buttons. */
@media (max-width: 600px) {
    .calc-cat__header { padding: 14px 16px; font-size: 1rem; }
    .calc-left-rail-item { padding: 12px 14px; min-height: 52px; }
    .calc-left-rail-item__title { font-size: 1rem; }
    .calc-left-rail-item__kb { font-size: 0.78rem; }
}

/* Race Day Setup detail page — mobile card styles (B3.1, B3.2, B3.3).
   Component-namespaced so they only apply to the card layouts that the
   sm-and-below MudHidden branches render. The desktop tables continue to
   render under their own (unstyled) classes. */
.raceday-detail__param-card-toggle {
    cursor: pointer;
}

.raceday-detail__param-card-name {
    word-break: break-word;
}

.raceday-detail__watch-card .mud-card-content {
    padding: 12px;
}

/* Apply-to-current-setup mobile card. Highlights an actively-changing row
   with a subtle left-edge accent so the user can scan the changeset like
   they would the desktop table's highlighted rows. */
.plan-apply-diff-card--changed {
    border-left: 3px solid var(--mud-palette-primary);
}

/* Apply-to-current-setup sticky-bottom CTA — replaces the buried top
   button when the parent KSplitPane is in stacked (single-column) mode so
   the karter can apply the plan without scrolling past dozens of diff
   rows. Hidden by default; the .k-split-pane--stacked ancestor class is
   toggled by KSplitPane's container-width JS, so this correctly tracks
   stacking regardless of viewport vs. CollapseBelowPx. */
.plan-apply-diff-panel__sticky-cta {
    display: none;
}

.k-split-pane--stacked .plan-apply-diff-panel {
    /* Reserve room at the bottom of the panel for the sticky bar so the
       last diff row isn't covered. */
    padding-bottom: 96px;
}

.k-split-pane--stacked .plan-apply-diff-panel__sticky-cta {
    display: block;
    position: sticky;
    bottom: var(--safe-bottom, 0);
    background-color: var(--mud-palette-surface);
    border-top: 1px solid var(--mud-palette-divider);
    padding: var(--space-3);
    /* Negative horizontal margin extends the sticky bar across the
       MudPaper's pa-3 padding so the surface band spans the full row
       width instead of leaving a gap on either side. */
    margin: 0 calc(-1 * var(--space-3));
    z-index: 2;
}

.k-split-pane--stacked .plan-apply-diff-panel__top-cta {
    display: none;
}

/* PlanDiffDrawer — sit below the app bar instead of underneath it, and
   never exceed the viewport on phones. Without these rules, MudBlazor's
   temporary drawer renders full-height and full-width, hiding the title
   row and close button under the navbar and overflowing on a 390-wide
   phone. --karting-appbar-height is published by appBarScrollHide.js.

   Specificity uses .mud-drawer-temporary so we win against MudBlazor's own
   `.mud-drawer-fixed.mud-drawer-temporary` rule (also 0,2,0). The !important
   on top/height matches the kart-drawer--motor-open precedent below — MudBlazor
   applies inline transform/dimension styles on the temporary drawer that would
   otherwise re-anchor it to the viewport top. */
.mud-drawer.mud-drawer-temporary.plan-diff-drawer {
    /* CRITICAL: Update --mud-drawer-width-right alongside `width`. MudBlazor
       hides a closed end-anchored temporary drawer by sliding it off-screen
       with `right: calc(-1 * var(--mud-drawer-width-right))`. If we widen
       the drawer without updating that variable, the closed drawer sticks
       out by `width − default-var-value` pixels — which is what produced
       the "drawer permanently visible on the right" bug. */
    --mud-drawer-width-right: min(480px, 80vw);
    width: min(480px, 80vw);
    top: var(--karting-appbar-height, 56px) !important;
    height: calc(100vh - var(--karting-appbar-height, 56px)) !important;
    height: calc(100dvh - var(--karting-appbar-height, 56px)) !important;
    max-width: 100vw;
}

/* On phones the 80vw clamp leaves only ~75px of scrim on a 375px viewport,
   so the diff content gets squeezed below readability. Go full-width — the
   close button in the header is the dismiss affordance on mobile. */
@media (max-width: 600px) {
    .mud-drawer.mud-drawer-temporary.plan-diff-drawer {
        --mud-drawer-width-right: 100vw;
        width: 100vw;
    }
}

/* Tighter cluster headers + content on phone-sized viewports — the
   outer page container already supplies enough horizontal breathing
   room and the default MudExpansionPanel padding eats too much of a
   390-wide screen (B3.6). */
@media (max-width: 600px) {
    .raceday-cluster-panel .mud-expand-panel-header {
        padding: 8px 8px;
    }

    .raceday-cluster-panel .mud-expand-panel-content {
        padding: 8px 6px;
    }
}

/* Mobile padding/bezel reductions scoped to the Race Day Setup detail page.
   The page nests several layers of MudBlazor cards/panels (PlanSection
   MudExpansionPanel → SetupClusterGrid raceday-cluster-panel → SetupParameterCard
   MudCard → MudCardContent), and each layer's default 16px LR padding compounds
   to over a third of a 390-wide screen before any content renders. These rules
   trim the LR (not vertical) padding so the actual recommendation text is
   readable on phones, while leaving desktop layout untouched.

   The .raceday-detail wrapper is set in RaceDaySetupDetail.razor so the rules
   only affect that page — the underlying components (MudCard, MudExpansionPanel,
   MudPaper) are reused throughout the app and must keep their defaults. */
@media (max-width: 600px) {
    .raceday-detail .mud-expand-panel-header {
        padding-left: 12px;
        padding-right: 12px;
    }
    .raceday-detail .mud-expand-panel-content {
        padding-left: 8px;
        padding-right: 8px;
    }
    .raceday-detail .mud-card-header {
        padding-left: 12px;
        padding-right: 12px;
    }
    .raceday-detail .mud-card-content {
        padding-left: 12px;
        padding-right: 12px;
    }
    /* Setup parameter cards sit inside three nested padded layers already, so
       trim their inner padding even further. */
    .raceday-detail .raceday-detail__param-card .mud-card-content {
        padding-left: 6px;
        padding-right: 6px;
    }
    /* Confidence chip + Impact progress bar share a flex row at desktop sizes,
       but the impact column's `flex: 1 1 140px` is wider than ~160px once the
       parent layers' padding is accounted for, so the two children clip each
       other on phones (MOB-C004). Stack vertically below sm so both labels
       and the progress bar render at full width. */
    .raceday-detail__param-card .raceday-detail__confidence-impact-row {
        flex-direction: column;
        align-items: stretch;
    }
    .raceday-detail__param-card .raceday-detail__confidence-impact-row > div {
        flex-basis: auto !important;
        width: 100%;
    }
    /* "Questions about this plan?" MudPaper uses .pa-4 (16px). MudBlazor's
       utility class is 0,1,0 so the unscoped selector below is enough — !important
       still guards against the utility class shifting in future versions. */
    .raceday-detail #qa-section.mud-paper {
        padding-left: 12px !important;
        padding-right: 12px !important;
    }
}

/* When KSplitPane is stacked (single-column on viewports below
   CollapseBelowPx), the left and right panes must let their content
   flow at natural height — otherwise the desktop overflow:auto rule
   clips the bottom of one section into the top of the next, which
   was visibly happening on phones between Setup Recommendations and
   "If You Feel… Then Try…" (B3.11). */
.k-split-pane--stacked .k-split-pane__left,
.k-split-pane--stacked .k-split-pane__right {
    overflow: visible;
    max-height: none;
}

/* Wrap-friendly chip variant for the Race Day Setup grid. MudChip's
   default style is a single-line pill with text-overflow: ellipsis,
   which clips long content (e.g. parameter names like "Front Wheel
   Hub Length", or "Horizontal Position" recommendation values) inside
   the constrained left pane of KSplitPane. This class lets the chip's
   height grow to fit a 2- or 3-line label so the full text is always
   readable without a tooltip or hover. Keep the pill aesthetic by
   keeping padding/border-radius from the default chip style. */
.mud-chip.raceday-chip-wrap {
    height: auto;
    min-height: 24px;
    white-space: normal;
    overflow-wrap: anywhere;
    word-break: break-word;
    line-height: 1.35;
    padding-top: 3px;
    padding-bottom: 3px;
    max-width: 100%;
}

.mud-chip.raceday-chip-wrap .mud-chip-content,
.mud-chip.raceday-chip-wrap .mud-chip-label {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
}

/* Gearing sweep grid (used by GearingPanel and the Race Day Setup
   GearingSweepCard via the shared GearingSweepGrid component). The grid
   uses a click-to-select pattern with a details panel below — the per-cell
   MudTooltip pattern was abandoned because mobile browsers latch tooltips
   open on tap and don't dismiss them when a different cell is tapped,
   producing a stack of overlapping bubbles that obscured the grid.
   `cursor: pointer` and `user-select: none` make the cell read as
   tappable and keep iOS from showing a text-selection caret on tap. */
.gearing-sweep-cell {
    cursor: pointer;
    -webkit-user-select: none;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
}

/* Selected cell gets a 2 px primary-coloured outline drawn inside the cell
   bounds (outline-offset: -2px) so it doesn't shift the table layout. Used
   together with the inline background colour from CellStyle(...) so the
   selection ring sits on top of warning/success/info backgrounds. */
.gearing-sweep-cell--selected {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: -2px;
}

/* Phone-only ranked-candidate cards (MOB-C034). The 11-column sweep table is
   unreadable below ~720 px, so GearingSweepGrid swaps in a vertical card list
   under SmAndDown and tucks the full grid behind a "Show full sweep" toggle
   that opens a horizontal scroll container. The card selection ring mirrors
   the desktop cell outline (drawn inside the bounds via outline-offset so the
   layout doesn't shift between selected and unselected states). */
.gearing-sweep-card {
    -webkit-tap-highlight-color: transparent;
}

.gearing-sweep-card--selected {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: -2px;
}

.gearing-sweep-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.gearing-sweep-scroll .gearing-sweep-grid {
    min-width: 720px;
}

/* Chat bubble timestamp — context-sensitive demotion without using opacity on text.
   User bubbles render white-on-primary, so we use a slightly translucent white via
   rgba() (foreground alpha, not element opacity). AI bubbles render on the default
   surface, so we use the AA-passing tertiary text token. */
.chat-bubble--user .chat-bubble__timestamp {
    color: rgba(255, 255, 255, 0.85);
}

.chat-bubble--ai .chat-bubble__timestamp {
    color: var(--color-text-tertiary);
}

/* Chat bubble width — desktop keeps 75% so the avatar gutter is visually balanced.
   On phones the 25% gutter wastes viewport and pushes AI replies to 8+ lines per
   paragraph, so we widen AI bubbles to 92% and user bubbles to 88% (MOB-C043). */
.chat-bubble {
    max-width: 75%;
}

@media (max-width: 600px) {
    .chat-bubble--ai {
        max-width: 92%;
    }
    .chat-bubble--user {
        max-width: 88%;
    }
}

/* Calculation picker dialogs (Session/Kart/Motor/Track) — fullscreen on phone
   so long track and session strings don't wrap to 4 lines inside a cramped
   320 px-wide modal (MOB-C038). Desktop keeps the MaxWidth=Small layout
   provided by the call site. The Class lands on the inner `.mud-dialog` via
   <MudDialog Class="calc-picker-dialog">. */
@media (max-width: 600px) {
    .mud-dialog.calc-picker-dialog {
        width: 100vw;
        height: 100dvh;
        max-width: 100vw !important;
        max-height: 100dvh !important;
        margin: 0 !important;
        border-radius: 0 !important;
        display: flex;
        flex-direction: column;
    }

    .mud-dialog.calc-picker-dialog .mud-dialog-content {
        flex: 1 1 auto;
        overflow-y: auto;
        max-height: none;
    }

    .mud-dialog.calc-picker-dialog .mud-dialog-actions {
        flex: 0 0 auto;
    }

    /* Prevent runaway text in list items inside the picker dialogs. The
       single-line title truncates with ellipsis; the caption wraps to two
       lines max via -webkit-line-clamp so we never blow past the dialog. */
    .mud-dialog.calc-picker-dialog .mud-list-item .picker-line-1 {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        display: block;
    }

    .mud-dialog.calc-picker-dialog .mud-list-item .picker-line-2 {
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
    }
}

/* === TRACK MAP CORNERS TABLE FALLBACK === */
/* Screen-reader/keyboard alternative for TrackMapCanvas (MOB-G016) — the
   Leaflet map is exposed as role="img"; this <details> exposes corner data
   as semantic content for users who can't operate the canvas. Styled to
   look like a proper expandable section rather than an unstyled details
   element flush against the map. */
.track-map-table-fallback {
    margin-top: var(--space-3);
    font-size: var(--text-sm);
    border: 1px solid var(--mud-palette-divider);
    border-radius: 6px;
    background: var(--mud-palette-surface);
    overflow: hidden;
}

.track-map-table-fallback summary {
    cursor: pointer;
    color: var(--mud-palette-text-secondary);
    padding: var(--space-2) var(--space-3);
    user-select: none;
    font-weight: 500;
    list-style: none;
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.track-map-table-fallback summary::-webkit-details-marker {
    display: none;
}

.track-map-table-fallback summary::before {
    content: '▶';
    font-size: 10px;
    transition: transform 160ms ease;
    display: inline-block;
    color: var(--mud-palette-text-secondary);
}

.track-map-table-fallback[open] summary::before {
    transform: rotate(90deg);
}

.track-map-table-fallback summary:hover,
.track-map-table-fallback summary:focus-visible {
    color: var(--mud-palette-primary);
    background: var(--mud-palette-action-default-hover);
}

.track-map-table-fallback[open] summary {
    border-bottom: 1px solid var(--mud-palette-divider);
}

.track-map-table-fallback table {
    width: 100%;
    border-collapse: collapse;
}

.track-map-table-fallback th,
.track-map-table-fallback td {
    padding: var(--space-2) var(--space-3);
    text-align: left;
    border-bottom: 1px solid var(--mud-palette-divider);
}

.track-map-table-fallback th {
    background: var(--mud-palette-action-default-hover);
    font-weight: 600;
}

.track-map-table-fallback tr:last-child td {
    border-bottom: none;
}

@media (prefers-reduced-motion: reduce) {
    .track-map-table-fallback summary::before {
        transition: none;
    }
}

/* === COLOR SWATCH RADIOGROUP === */
/* DriverProfileForm accent color picker (MOB-G024) — native button +
   role=radio so keyboard/AT users get proper radiogroup semantics. */
.k-color-swatch {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background-color: var(--swatch-color);
    border: 2px solid transparent;
    cursor: pointer;
    padding: 0;
    transition: transform 120ms ease;
    position: relative;
}

/* Expand the hit target to 44×44 on every pointer type without growing
   the visible disc. The pseudo-element sits behind the visible swatch
   and intercepts clicks/taps within the WCAG 2.5.5 minimum target. */
.k-color-swatch::before {
    content: '';
    position: absolute;
    inset: -4px;
    border-radius: 50%;
}

.k-color-swatch:hover {
    transform: scale(1.05);
}

.k-color-swatch:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 4px;
}

.k-color-swatch--selected {
    border-color: var(--mud-palette-text-primary);
    box-shadow: 0 0 0 2px var(--swatch-color);
}

@media (pointer: coarse) {
    .k-color-swatch {
        width: 44px;
        height: 44px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .k-color-swatch {
        transition: none;
    }
    .k-color-swatch:hover {
        transform: none;
    }
}

/* === DRIVER AVATAR ===
   Reusable circular avatar tinted by --driver-accent (per-driver accent color).
   Replaces dozens of inline Style="" interpolations across the leaderboard and
   profile chrome so size/border/dark-mode tokens come from CSS, not razor. */
.k-driver-avatar {
    width: var(--driver-avatar-size, 64px);
    height: var(--driver-avatar-size, 64px);
    background-color: var(--driver-accent, var(--mud-palette-primary));
    color: var(--mud-palette-primary-text, white);
    border: 3px solid var(--driver-accent, var(--mud-palette-primary));
    border-radius: 50%;
}

.k-driver-avatar--small {
    --driver-avatar-size: 32px;
    border-width: 2px;
}

@media (max-width: 600px) {
    .k-driver-avatar {
        --driver-avatar-size: 56px;
    }
    .k-driver-avatar--small {
        --driver-avatar-size: 32px;
    }
}

/* === PAGE HEADER ===
   Title + Actions row used by every page. On wide viewports the title sits
   left and the action buttons right (Justify.SpaceBetween). On phone-class
   viewports we let the actions wrap onto their own row(s) so a long set of
   buttons (KartDetail: Edit / Edit Setup / History & Favorites / Delete)
   no longer overflows the screen and forces horizontal scrolling. */
@media (max-width: 600px) {
    .page-header {
        /* Push the actions to their own row by letting the title claim the
           whole first row. min-width: 0 on the title makes long custom kart
           names truncate via the inner MudText rather than blow the layout
           wider than the screen. */
        align-items: flex-start;
    }
    .page-header__title {
        min-width: 0;
        flex: 1 1 100%;
    }
    .page-header__actions {
        flex: 1 1 100%;
        justify-content: flex-start;
    }
    /* Action buttons should also size sensibly — give each at least its
       natural width so labels never clip, but allow the row to wrap. */
    .page-header__actions > .mud-button-root {
        flex: 0 1 auto;
    }
}

/* === TIER COMPARISON ===
   Highlights the column that matches the user's current plan. The same CSS
   variables (--mud-palette-primary*) participate in dark-mode swaps, so this
   replaces the literal #147EFB / lightened-blue inline style. */
.tier-comparison-cell--current {
    border: 2px solid var(--mud-palette-primary);
    background-color: var(--mud-palette-primary-hover);
}

/* === SESSION HERO BAND ===
   Full-width hero map at the top of /sessions/{id} and the embedded
   master-detail detail pane. Phase 4 of the Track Map Elevation plan. The
   inner band uses a clamped responsive height so it fills the visible
   viewport on big monitors without exceeding the user's screen on laptops. */
.session-hero {
    position: relative;
    overflow: hidden;
    border-radius: 12px;
}
.session-hero__map {
    width: 100%;
    height: clamp(420px, 60vh, 720px);
    position: relative;
}

/* === TRACK THEATRE ===
   Full-viewport immersive mode for /tracks/{id}/map/{layoutId?},
   /sessions/{id}/map, and /driving-analysis/{id}/map. Phase 6 of the
   Track Map Elevation plan. The chrome top bar fades after 3 s of idle
   to reduce clutter on long focus sessions; pointer-move / tap brings
   it back. */
.track-theatre {
    position: fixed;
    inset: 0;
    background: #000;
    color: var(--mud-palette-text-primary);
    overflow: hidden;
    z-index: 1000;
}
.track-theatre__map {
    position: absolute;
    inset: 0;
}
.track-theatre__centered {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    color: var(--mud-palette-text-primary);
    text-align: center;
    padding: 24px;
}
.track-theatre__chrome {
    position: absolute;
    display: flex;
    align-items: center;
    gap: 12px;
    transition: opacity 280ms ease, transform 280ms ease;
    z-index: 1010;
    pointer-events: auto;
}
.track-theatre__chrome--top {
    top: max(12px, env(safe-area-inset-top));
    left: max(12px, env(safe-area-inset-left));
    right: max(12px, env(safe-area-inset-right));
}
.track-theatre__title {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
}
.track-theatre__title .mud-typography {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.track-theatre--chrome-hidden .track-theatre__chrome--top {
    opacity: 0;
    transform: translateY(-8px);
    pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
    .track-theatre__chrome { transition: none; }
}

/* === TRACK MAP ELEVATION — REDUCED-MOTION ROLL-UP (Phase 7) ===
   Single block that disables every animation/transition the Track Map
   Elevation phases introduced. Individual selectors already have their
   own reduced-motion guards (insight sheet, theatre chrome, table fallback)
   — this is the catch-all for any newer surfaces (corner card highlight,
   layout-card hover, Leaflet polyline cross-fade during swapLayout). */
@media (prefers-reduced-motion: reduce) {
    .map-insight-sheet--docked,
    .corner-card-highlighted,
    .track-theatre__chrome,
    .layout-card,
    .leaflet-overlay-pane {
        animation: none !important;
        transition: none !important;
    }
}

.karting-main-content { max-width: 100%; margin: 0 auto; }
@media (min-width: 2200px) {
    .karting-main-content { max-width: 2000px; }
}

/* Prompt 3 (audit FOCUS findings): single focus-visible policy */
.mud-icon-button:focus-visible,
.mud-nav-link:focus-visible,
.mud-button-root:focus-visible,
a:focus-visible,
.session-card:focus-visible,
.layout-card:focus-visible,
.kart-card:focus-visible,
.motor-card:focus-visible,
.dashboard-stat-card:focus-visible,
.mud-table-row:focus-visible,
.chart-data-table summary:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 2px;
    border-radius: 2px;
}

/* Prompt 5: responsive chart container */
.k-chart { height: 350px; position: relative; }
@media (min-width: 1280px) { .k-chart { height: 420px; } }
@media (min-width: 1920px) { .k-chart { height: 500px; } }
@media (min-width: 2560px) { .k-chart { height: 600px; } }

.k-chart--hero { height: 420px; position: relative; }
@media (min-width: 1280px) { .k-chart--hero { height: 540px; } }
@media (min-width: 1920px) { .k-chart--hero { height: 700px; } }
