/* CSS Reset - ensure no browser default margins */
*, *::before, *::after {
    box-sizing: border-box;
}

html {
    margin: 0;
    padding: 0;
    /* Always show scrollbar gutter to prevent layout shift on page navigation */
    overflow-y: scroll;
    overflow-x: hidden;
}

/* TEXT-SIZE-1 (2026-10-06, ticket #1079 "is this the only font size"): one global text
   scale. 97 % of font-size declarations in the app are rem/em, so scaling the root scales
   everything - chat, forms, tables - with no per-component work. Lives on a DATA ATTRIBUTE,
   not a class, because three places (_Layout.cshtml first-paint script, AppThemeService
   Set/Get) assign documentElement.className outright and would wipe a class. Persisted in
   localStorage `app_text_size` (per device, like the theme); written by textSize.js. */
html[data-text-size="sm"] { font-size: 87.5%; }
html[data-text-size="lg"] { font-size: 112.5%; }
html[data-text-size="xl"] { font-size: 125%; }

body {
    margin: 0;
    padding: 0;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
    /* DO NOT add overflow-x: hidden here — html already clips horizontal overflow.
       Adding it makes body a scroll container (implicit overflow-y: auto),
       and combined with overscroll-behavior: none in themes.css,
       wheel/touch scrolling breaks completely. */
}

/* Global button reset - remove browser default outline/appearance */
button {
    outline: none;
    appearance: none;
    -webkit-appearance: none;
    background: transparent;
    border: none;
    padding: 0;
    margin: 0;
    font: inherit;
    color: inherit;
}

/* Material Symbols Icon Font */
.material-symbols-rounded {
    font-family: 'Material Symbols Rounded';
    font-weight: normal;
    font-style: normal;
    font-size: 24px;
    line-height: 1;
    letter-spacing: normal;
    text-transform: none;
    display: inline-block;
    white-space: nowrap;
    word-wrap: normal;
    direction: ltr;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
    font-feature-settings: 'liga';
}

h1 {
    font-size:2em;
}

h1:focus {
    outline: none;
}

a, .btn-link {
    color: #0071c1;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

.content {
    padding-top: 1.1rem;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid red;
}

.validation-message {
    color: red;
}

#blazor-error-ui {
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 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-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI26My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMudHIgMjkxaHRtYWluOyI7Y3I2LjA0NjYgOC42NzUtLjI5IDE3LjU1MDMgNS43MzYgMTMuMTM0OSAwLTUzLjI1MDYgMCAyOC4wODYgOS4zMTM4MDUgMjAuMDY0NSA4LjU1MDYgMjMuNDI5NjUgODguNTUwMTUgMjAuMDY0NTI1OSAxMi41MDYgNS43MzYgMTIuNzg2MzYgMS4wMTQgMTguOC41NzYgNy44MTMgNzMuNzI1IDE1LjcyMyA1LjI2NTggMjIuMjA3NiBBLjcgMS41IDAgMTQyIDguMjQ0NSA4LjY0MiAxLjM2IDQuMSAxLjg0ODAgMCA4LjU3NSA3Ljc0MjQgMCAyOC4wODYgMTguMjI3MiAwLTUuMTg2My44NTcgMS4wMzU3OS44NTcgMi41MjY5LjQzMzc4IDEuMzg0NTUgOC4xNjY1IDcuNzMxOS42MjYgMy44NTMgMi4xMjgxIDIuMzg0NzogMy40NzY4NiAzLjExMDI3IDMuNDE3IDEuNzY1IDMuMjkgMy40NzY2idiB2NzcgNS4xdjQuNzM3IDguZS42NTUyOSAyLjc1NyAzLjI0IDMuMDAzMiB3LjEeDIuNjJhTDE4My42MzQgMCAwIDAuMTIyMyA5LjM4MTcgMCAwIQA9NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM6Lg==) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

.blazor-error-boundary::after {
    content: "An error has occurred."
}

.animation-container {
    position: relative;
    width: 100%;
    height: 300px;
    overflowx: hidden;
}

.landing-page header h1 {
    margin: 0;
    xfont-size: 2.8em;
}

.landing-page header p {
    margin: 5px 0;
    font-size: 1.2em;
}

.landing-page section {
    margin: 30px auto;
    padding: 20px;
    text-align: center;
}

.landing-page .features {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 20px;
    margin-top: 30px;
}

.landing-page .feature {
    background: white;
    border: 1px solid #ddd;
    border-radius: 8px;
    padding: 20px;
    width: 300px;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

.landing-page .feature h3 {
    color: #0077B6;
}

.cta {
    background: #0077B6;
    padding: 20px;
    border-radius: 8px;
    margin: 30px auto;
    max-width: 400px;
    z-index:10000;
}

.cta a.button {
    text-decoration: none;
    font-weight: bold;
    padding: 10px 20px;
    background: #005f86;
    border-radius: 4px;
}

.chat-button {
    position: fixed !important;
    bottom: 20px !important;
    right: 20px !important;
    z-index:1000;
}

.newsletter {
    display: flex;
    align-items: center;
}
.chat-container {
    padding: 10px;
}

.chat-message {
    display: flex;
    margin-bottom: 8px;
}

.chat-message.user {
    justify-content: flex-end;
}

.chat-message.agent {
    justify-content: flex-start;
}

.chat-bubble {
    max-width: 240px;
    padding: 6px 12px;
    border-radius: 16px;
    word-break: break-word;
}

.chat-bubble.user {
    background-color: #1b6ec2;
    color: #fff;
}

.chat-bubble.agent {
    background-color: #f1f1f1;
    color: #333;
}

/* ============================================
   HERO CAROUSEL ARROW STYLES
   ============================================ */

/* ============================================
   REVIEWS CAROUSEL STYLES
   ============================================ */

/* Section Layout */
.reviews-section {
    margin-top: 2rem;
}

/* Carousel Container */
.reviews-carousel {
    height: 600px;
    position: relative;
    border-radius: 24px;
    overflow: hidden;
    box-shadow: 0 8px 32px rgba(15, 23, 42, 0.25);
}

/* Slide Background - Dark navy/slate gradient for contrast with purple header */
.review-slide {
    background: linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #334155 100%);
    position: relative;
    overflow: hidden;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
}

.review-slide::before {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(circle at 30% 50%, rgba(99, 102, 241, 0.08) 0%, transparent 50%),
                radial-gradient(circle at 70% 80%, rgba(139, 92, 246, 0.06) 0%, transparent 40%);
    pointer-events: none;
}

/* Review Content */
.review-content {
    position: relative;
    z-index: 1;
    color: white;
    text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
    padding: 50px 80px 90px 80px;
    width: 100%;
    max-width: 1000px;
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100%;
    box-sizing: border-box;
}

/* Quote Icon */
.quote-icon {
    margin-bottom: 20px;
    color: rgba(147, 197, 253, 0.6);
}

.review-text {
    font-size: 1.1rem;
    line-height: 1.8;
    margin-bottom: 32px;
    font-style: italic;
    color: rgba(255, 255, 255, 0.95);
    max-width: 850px;
}

/* Author Info - Enhanced Layout */
.review-author {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;
}

.author-avatar {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
    box-shadow: 0 4px 16px rgba(99, 102, 241, 0.4);
    border: 3px solid rgba(255, 255, 255, 0.2);
}

.author-info {
    text-align: center;
}

.author-info p {
    margin: 4px 0;
}

.author-name {
    font-weight: 700;
    font-size: 1.2rem;
    color: white;
    margin-bottom: 2px;
}

.author-title {
    font-size: 0.95rem;
    color: rgba(147, 197, 253, 0.9);
    font-weight: 500;
}

.review-date {
    font-size: 0.85rem;
    color: rgba(255, 255, 255, 0.6);
}

/* Mobile Responsive */
@media (max-width: 960px) {
    .reviews-carousel {
        height: 680px;
    }
    .review-content {
        padding: 40px 50px 100px 50px;
    }
    .review-text {
        font-size: 1rem;
    }
    .author-avatar {
        width: 48px;
        height: 48px;
    }
}

@media (max-width: 600px) {
    .reviews-carousel {
        height: 720px;
        border-radius: 16px;
    }
    .review-content {
        padding: 30px 25px 110px 25px;
    }
    .review-text {
        font-size: 0.95rem;
        line-height: 1.7;
    }
    .author-avatar {
        width: 44px;
        height: 44px;
    }
    .author-name {
        font-size: 1.05rem;
    }
    .quote-icon {
        margin-bottom: 12px;
    }

    /* Mobile footer improvements */
    .footer {
        padding: 2.5rem 1rem !important;
        margin-top: 3rem !important;
    }
    
    .footer img {
        max-width: 160px !important;
        margin-bottom: 1.5rem;
        display: block;
        margin-left: auto;
        margin-right: auto;
    }
    
    .footer .container {
        text-align: center !important;
    }
    
    /* Copyright text */
    .footer-copyright {
        font-size: 0.8rem;
        color: #718096;
        margin: 1.5rem 0 1.25rem 0;
        display: block;
        line-height: 1.6;
        padding: 0 1rem;
    }
    
    /* Footer links container */
    .footer-links {
        display: flex;
        flex-direction: column;
        gap: 0.75rem;
        margin-top: 1rem;
    }
    
    /* Hide the pipe separator on mobile */
    .footer-separator {
        display: none !important;
    }
    
    /* Link styling on mobile */
    .footer-links a {
        color: #667eea;
        text-decoration: none;
        padding: 0.625rem 1rem;
        font-weight: 500;
        font-size: 0.9rem;
        display: block;
        background: #f7fafc;
        border-radius: 8px;
        transition: all 0.2s ease;
    }
    
    .footer-links a:hover {
        background: #edf2f7;
        color: #764ba2;
        transform: translateX(4px);
    }
}

/* ============================================
   GLOBAL PRINT STYLES
   ============================================ */
@media print {
    /* ============================================
       HIDE ALL LAYOUT CHROME
       ============================================ */

    /* Hide App Header and Navigation */
    .app-header,
    .visitor-drawer,
    .drawer-overlay {
        display: none !important;
        visibility: hidden !important;
        height: 0 !important;
        overflow: hidden !important;
    }

    /* Hide Footer */
    footer,
    .footer,
    .footer-links,
    .footer-copyright,
    .app-footer,
    footer.border-top {
        display: none !important;
        visibility: hidden !important;
    }

    /* Hide Stopwatch, Notes, and Productivity Panels */
    .stopwatch-panel-container,
    .notes-panel-container,
    .productivity-panel-container,
    .stopwatch-tab,
    .notes-tab,
    .productivity-tab,
    .stopwatch-drawer,
    .notes-drawer,
    .productivity-drawer {
        display: none !important;
        visibility: hidden !important;
    }

    /* Hide Floating Controls */
    .chat-button,
    .floating-chat-button,
    .floating-chat-dialog,
    [class*="FloatingRecorder"],
    [class*="GlobalSceneOverlay"],
    [class*="floating-recorder"],
    [class*="scene-overlay"] {
        display: none !important;
        visibility: hidden !important;
    }

    /* Hide Dialogs, Overlays, Toasts */
    [class*="overlay"],
    .toast-container {
        display: none !important;
        visibility: hidden !important;
    }

    /* Hide all no-print elements */
    .no-print,
    [class*="no-print"] {
        display: none !important;
        visibility: hidden !important;
    }

    /* ============================================
       RESET LAYOUT FOR PRINT
       ============================================ */

    html, body {
        background: white !important;
        margin: 0 !important;
        padding: 0 !important;
        overflow: visible !important;
        -webkit-print-color-adjust: exact !important;
        print-color-adjust: exact !important;
    }

    .container-fluid {
        max-width: 100% !important;
        padding: 10px !important;
        margin: 0 !important;
    }

    /* ============================================
       PRINT OPTIMIZATION
       ============================================ */

    /* Preserve background colors */
    * {
        -webkit-print-color-adjust: exact !important;
        print-color-adjust: exact !important;
    }

    /* Disable transitions */
    *, *::before, *::after {
        transition: none !important;
        animation: none !important;
    }

    /* Ensure links don't show URLs */
    a[href]:after {
        content: none !important;
    }

    /* Page break controls */
    .avoid-page-break {
        page-break-inside: avoid !important;
    }
}

/* ============================================
   SNOOZE-Z-1 (2026-07-10): DIALOG LAYER YIELD
   While any BaseDialog is open, the floating assistant (Glitch chat panel +
   companion/chat button) yields the top layer. BaseDialog mirrors its
   visibility into body.nts-dialog-open via js/dialogLayer.js (counter-based
   for stacked dialogs). Needed because dialogs render inside page content
   trees whose ancestors (HOME-SCROLL-3 backdrop wrappers, theme layers)
   create stacking contexts that trap the overlay's z-index 100000 below the
   root-level assistant's 1200 — the assistant was covering the Snooze
   button. !important intentionally: this must beat the scoped-CSS attribute
   selectors in Assistant.razor.css regardless of future specificity changes.
   Lives here (global, unscoped) because Blazor CSS isolation can't express a
   body-class ancestor selector. ============================================ */
body.nts-dialog-open .floating-chat-button,
body.nts-dialog-open .floating-chat-dialog {
    z-index: 0 !important;
}

/* ============================================
   AUTH-FAB-1 (2026-07-14): hide the public Glitch chat bubble on auth pages
   at phone widths. The bubble is viewport-anchored bottom-right; at ≤480px
   the Register/Login card is full-width, so the bubble sits ON the form —
   QA caught it covering the password-requirements checklist (emulator) and
   crowding Confirm Password (real iPhone, Meta in-app webview). The signup
   form is the money moment; the widget survives on desktop auth pages and
   everywhere else on mobile (homepage lead capture untouched).
   Lives here (global, unscoped) because PublicChatWidget.razor.css is
   isolated and can't see the .auth-layout ancestor — same constraint as
   SNOOZE-Z-1 above. !important for the same reason: beat the scoped
   attribute selectors regardless of future specificity drift. */
@media (max-width: 480px) {
    .auth-layout .public-chat-bubble,
    .auth-layout .public-chat-panel {
        display: none !important;
    }
}

/* CHAT-RESIZE-1 (2026-07-22): global drag-state cursor/selection lock for the
   resizable chat panels. Lives here (not in the scoped *.razor.css) because a
   `body` selector in a Blazor scoped sheet gets the scope attribute appended to
   `body` and never matches. The .chat-resize-handle reveal/hover styling stays
   scoped per-component; only this body-level rule needs to be global. */
body.nts-chat-resizing {
    cursor: nwse-resize !important;
    -webkit-user-select: none;
    user-select: none;
}

/* CHAT-FABMOVE-1 (2026-07-23): global drag cursor/selection lock while moving the
   FAB. Global (not scoped *.razor.css) for the same reason as nts-chat-resizing —
   a `body` selector in a scoped sheet gets the scope attribute and never matches. */
body.nts-fab-dragging {
    cursor: grabbing !important;
    -webkit-user-select: none;
    user-select: none;
}

/* ═══════════════════════════════════════════════════════════════════════
   ASSIST-SPEAK-2 (2026-08-20, Josh): the chat reading stage.
   While the assistant reads a CHAT message aloud (body.nts-chat-reading,
   toggled by audioBriefing.speakMessage / released by setCompanionTalking),
   the open chat panel would occlude the caption text - so the panel sleeps
   and the PEEK avatar takes the same bottom-center stage the briefing
   listen uses.
   - visibility (NOT display): the dialog is @if(_isOpen)-rendered in
     Blazor, and the in-flight read is OWNED by a bubble inside it -
     display:none or closing the panel would tear down layout/state we
     need alive. A visibility-hidden parent cannot hide a child that sets
     visibility:visible - that re-enable is the whole mechanism.
   - Lives in site.css, not Assistant.razor.css: a body-class selector in
     a SCOPED sheet is the silent-no-match trap (GOTCHAS #258 family).
   - Geometry mirrors .is-tracking-listen (Assistant.razor.css): lift =
     measured --nts-caption-height (published by audioBriefing.js) + gap;
     the dialog has no transform, so position:fixed here is viewport-true.
   - The bob animates margin, never transform - a transform keyframe would
     silently overwrite the centering translate (PRO-MOUTH-1 trap).
   z-index: caption bar 1190 < dialog 1200 < stage peek 1300. */
body.nts-chat-reading .floating-chat-dialog {
    visibility: hidden;
    pointer-events: none;
}
body.nts-chat-reading .floating-chat-dialog .companion-peek-avatar,
body.nts-chat-reading .floating-chat-dialog .pro-peek-bot {
    visibility: visible;
    pointer-events: auto;
    position: fixed;
    left: 50vw;
    right: auto;
    top: auto;
    /* 2026-08-20 v2 (Josh: "bring the assistant closer to the caption"): the
       drawn robot mark floats high inside its --peek-size box, so a positive
       gap read as mid-screen. Negative offset closes the visual distance -
       nudge this ONE number to taste. */
    bottom: calc(var(--nts-caption-height, 110px) - 24px);
    transform: translateX(-50%) scale(1.35);
    transform-origin: bottom center;
    transition: bottom 0.4s ease, transform 0.4s ease;
    z-index: 1300;
    animation: nts-chat-read-bob 4s ease-in-out infinite;
}
@keyframes nts-chat-read-bob {
    0%, 100% { margin-bottom: 0; }
    50%      { margin-bottom: 8px; }
}

/* TOUR-STAGE-2 (2026-09-05): the /home hero mark takes the reading stage.
   /home hides the FAB (ShouldHideFab, one AI presence per surface), so the
   TOUR-STAGE-1 lift had nothing to grab there and the explainer's Home half
   played with the robot parked at the top of the page. audioBriefing.js adds
   these two classes in enterChatStage() when there is no FAB and no open
   panel; leaveChatStage() removes them. Same geometry as the peek stage above
   (bottom-center, above the measured caption bar). The mark is 88px, so the
   scale is gentler than the FAB's 1.5. The wrapper keeps the mark's height so
   the hero does not jump when the button leaves flow. Lives in site.css for
   the same reason as the rules above: a body-class selector in a SCOPED sheet
   is the silent-no-match trap. */
body.nts-chat-reading .pro-hero-wrapper.nts-hero-staged-wrap {
    min-height: 88px;
}
body.nts-chat-reading .welcome-avatar-section.nts-hero-staged-wrap {
    min-height: 120px;
    /* The section's entrance animation leaves a translateY on it, and a transformed
       ancestor traps position:fixed (the staged orb landed 400px above the viewport
       on the first try). Clear it while staged; the robot's wrapper has no transform. */
    transform: none !important;
    animation: none !important;
    filter: none !important;
    will-change: auto !important;
}
/* TOUR-STAGE-3 (2026-09-05): the companion orb hero (.welcome-avatar-container)
   takes the same stage. Scale 1 - the orb is already 120px. */
body.nts-chat-reading .pro-hero-bot.nts-hero-staged,
body.nts-chat-reading .welcome-avatar-container.nts-hero-staged {
    position: fixed;
    left: 50vw;
    right: auto;
    top: auto;
    bottom: calc(var(--nts-caption-height, 110px) - 20px);
    transform: translateX(-50%) scale(1.15);
    transform-origin: bottom center;
    transition: bottom 0.4s ease, transform 0.4s ease;
    z-index: 1300;
    animation: nts-chat-read-bob 4s ease-in-out infinite;
}
body.nts-chat-reading .welcome-avatar-container.nts-hero-staged {
    transform: translateX(-50%) scale(1);
    /* The entity canvas (192px) overflows its 120px container and the higher tiers
       have legs below center, so the orb floated ~45px above where the robot sits
       (Josh's localhost run: over the card's tab row). Tuck it onto the bar. */
    bottom: calc(var(--nts-caption-height, 110px) - 60px);
}
@media (prefers-reduced-motion: reduce) {
    body.nts-chat-reading .pro-hero-bot.nts-hero-staged,
    body.nts-chat-reading .welcome-avatar-container.nts-hero-staged { animation: none; }
}
/* TOUR-WING-1 (2026-09-09, Josh's Home-tour screenshots): during a TOUR the staged hero
   mark sat dead-centre on the very content each step was spotlighting (the card's tab row,
   the routine rows). A chat read has nothing behind the mark; a tour does. On wide desktops
   the mark takes stage-LEFT - the empty margin beside the ~1150px content column - so the
   spotlight is never under it. `nts-tour-reading` is set by audioBriefing.playTour and
   cleared with the tour. Below 1400px the margin is too thin; centre stage stays. */
@media (min-width: 1400px) {
    body.nts-chat-reading.nts-tour-reading .pro-hero-bot.nts-hero-staged,
    body.nts-chat-reading.nts-tour-reading .welcome-avatar-container.nts-hero-staged,
    body.nts-chat-reading .pro-hero-bot.nts-hero-staged.nts-tour-reading,
    body.nts-chat-reading .welcome-avatar-container.nts-hero-staged.nts-tour-reading,
    /* TOUR-WING-3: the PEEK avatar is the staged mark when the tour starts from the chat chip */
    body.nts-chat-reading .floating-chat-dialog .companion-peek-avatar.nts-tour-reading,
    body.nts-chat-reading .floating-chat-dialog .pro-peek-bot.nts-tour-reading {
        left: max(90px, calc(50vw - 660px));
        transition: bottom 0.4s ease, transform 0.4s ease, left 0.5s ease;   /* glides between wings */
    }
    /* TOUR-PEEK-1 (2026-09-09): while a step holds a hover surface open (the productivity
       panel slides out over stage-left), the mark crosses to stage-right so it is not
       standing in front of the thing being shown. Set/cleared by audioBriefing.js. */
    body.nts-chat-reading.nts-tour-reading.nts-tour-peeking .pro-hero-bot.nts-hero-staged,
    body.nts-chat-reading.nts-tour-reading.nts-tour-peeking .welcome-avatar-container.nts-hero-staged,
    body.nts-chat-reading .pro-hero-bot.nts-hero-staged.nts-tour-peeking,
    body.nts-chat-reading .welcome-avatar-container.nts-hero-staged.nts-tour-peeking,
    body.nts-chat-reading .floating-chat-dialog .companion-peek-avatar.nts-tour-peeking,
    body.nts-chat-reading .floating-chat-dialog .pro-peek-bot.nts-tour-peeking {
        left: min(calc(100vw - 90px), calc(50vw + 660px));
    }
}

/* TOUR-STAGE-MOBILE-1 (2026-09-08): on a phone the caption bar is a third of the viewport, so
   "just above the bar" is mid-screen - the staged robot sat on top of the very content each
   step was spotlighting (Set a schedule, the task row; 9/8 mobile walk). On narrow screens the
   robot tucks into the bar's top-left corner instead: small, bottom-left origin, overlapping the
   bar's header strip where there is only the dots + "AI ASSISTANT IS READING" label. Applies to
   all three stage variants (FAB peek, hero mark, orb). Desktop geometry untouched. */
@media (max-width: 640px) {
    body.nts-chat-reading .floating-chat-dialog .companion-peek-avatar,
    body.nts-chat-reading .floating-chat-dialog .pro-peek-bot,
    body.nts-chat-reading .pro-hero-bot.nts-hero-staged,
    body.nts-chat-reading .welcome-avatar-container.nts-hero-staged {
        left: 10px;
        transform: translateX(0) scale(0.55);
        transform-origin: bottom left;
        bottom: calc(var(--nts-caption-height, 110px) - 58px);
        animation: none;
    }
    body.nts-chat-reading .welcome-avatar-container.nts-hero-staged {
        transform: translateX(0) scale(0.5);
        bottom: calc(var(--nts-caption-height, 110px) - 90px);
    }

    /* HERO-STAGE-Z-1 (2026-10-02, found walking /home at 440px with Josh): the two HOME hero
       marks live inside .home-main, which is position:relative + z-index:1 - a stacking context
       capped at 1, so their z-index:1300 only counts inside it. The caption bar (z 1190, child
       of body) therefore paints OVER them, and the corner tuck above put the robot under the
       bar: on a phone the Home read had no robot at all. (The FAB peek is outside .home-main,
       which is why other pages were fine; desktop never overlaps the bar.) Don't raise
       .home-main - its whole column would cover the bar. Sit the hero marks ON the bar's top
       edge instead, fully above it. Scale is applied from the bottom-left origin, so the
       offset is in unscaled px. */
    body.nts-chat-reading .pro-hero-bot.nts-hero-staged {
        bottom: calc(var(--nts-caption-height, 110px) + 2px);
    }
    body.nts-chat-reading .welcome-avatar-container.nts-hero-staged {
        bottom: calc(var(--nts-caption-height, 110px) - 20px);
        /* The orb's canvas overflows its box by ~18px each side, so at left:10px it was
           clipped by the screen edge (measured: canvas left -8px at 440px). */
        left: 26px;
    }

    /* HERO-STAGE-RIGHT-1 (2026-10-02, Josh's iPhone on Glitch): sitting fully above the bar
       (it cannot overlap it - see HERO-STAGE-Z-1) a tall avatar rises into the row above, and
       at stage-left it covered the start of that row's title ("10 Chin Ups"). Row text starts
       at the left; the right end of a title line is usually empty. Move both Home hero marks to
       the bottom-RIGHT corner and shrink them a step (robot 0.55 -> 0.45, orb 0.5 -> 0.4).
       The bar's own close button is inside the bar, below the mark. */
    body.nts-chat-reading .pro-hero-bot.nts-hero-staged {
        left: auto;
        right: 10px;
        transform: translateX(0) scale(0.45);
        transform-origin: bottom right;
    }
    body.nts-chat-reading .welcome-avatar-container.nts-hero-staged {
        left: auto;
        right: 26px;
        transform: translateX(0) scale(0.4);
        transform-origin: bottom right;
    }
}

/* ASSIST-STAGE-3 (2026-08-22, Josh on a phone: "he's visible - just off the top
   left screen"). AVATAR-RACE-1 got the companion RENDERING again; this is where it
   renders.

   Assistant.razor.css carries a mobile DOCK rule for the peek:

       @media (max-width: 768px) {
           .companion-peek-avatar { top: -36px !important; left: 8px !important; ... }
       }

   Its own comment calls that !important "belt-and-suspenders" against stale scoped-CSS
   caching. It was written for the DOCKED peek - the corner badge on an open panel -
   and the reading stage did not exist yet. No specificity beats !important, so at
   <=768px the stage's `left: 50vw; top: auto` lost: the mark became position:fixed at
   top:-36px left:8px - pinned 36px ABOVE the viewport's top-left corner - and the
   stage's own translateX(-50%) then dragged it further off-screen. GOTCHAS #372 again:
   an override outliving the condition it was written for.

   🔴 Only the COMPANION had that rule. `.pro-peek-bot` (robot AND halo) has exactly one
   media rule and it is `prefers-reduced-motion: animation: none` - which is why two of
   the three avatars looked perfect on the phone. Second time tonight that an asymmetry
   between sibling branches WAS the diagnosis (#395).

   Answering !important with !important is not an arms race here: the stage is a
   time-bounded MODE that has to out-declare a defensive dock rule, and it is scoped to
   body.nts-chat-reading so it applies for exactly the read's duration and no longer.
   Geometry ONLY - the dock rule's 56x56 sizing is deliberately left alone (the stage's
   scale(1.35) reads fine at that size, and width/height were never the bug). `bottom`
   is not contested by any !important, so `top: auto` is what lets it take effect. */
@media (max-width: 768px) {
    body.nts-chat-reading .floating-chat-dialog .companion-peek-avatar {
        top: auto !important;
        left: 50vw !important;
    }
}

/* ASSIST-STAGE-2 (2026-08-21, Josh: "still not centered"): while a chat read
   holds the stage the panel is HIDDEN - so the home page's chat-open courtesy
   shift (.home-main.chat-open { left:-180px } in Home.razor.css, there to keep
   the greeting clear of the OPEN panel) just left the backdrop hero sitting
   180px left of the robot + caption centerline. Cancel the shift for the
   read's duration; .home-main's existing 0.5s left transition slides the hero
   under the robot when the read starts and returns it when the read ends.
   Global sheet on purpose: body-class selectors in a scoped sheet silently
   never match (#258), and (0,3,1) here outranks the scoped rule's (0,2,1). */
body.nts-chat-reading .home-main.chat-open {
    left: 0;
}

/* ─────────────────────────────────────────────────────────────
   TOUR-1 (2026-08-21): guided tool tours.

   GLOBAL on purpose, twice over: the spotlight lands on ARBITRARY page
   elements owned by other components (a scoped sheet could never reach
   them — #258), and the class is applied from JS against body-level
   state, which is the same silent-no-match trap.

   The lit style uses ONLY outline + box-shadow: both paint outside the
   layout box, so spotlighting a stat card or a button cannot reflow the
   page mid-sentence. Deliberately NO transform/scale here — beyond the
   reflow risk, a transform makes the element a containing block for any
   fixed-position child (the SNOOZE-Z-1 trap).
   ───────────────────────────────────────────────────────────── */
.nts-tour-spotlight {
    outline: 2px solid var(--theme-primary, #6366f1);
    outline-offset: 4px;
    border-radius: 10px;
    box-shadow: 0 0 0 6px rgba(99, 102, 241, 0.14), 0 8px 28px rgba(15, 23, 42, 0.12);
    animation: nts-tour-pulse 2.4s ease-in-out infinite;
    transition: outline-color 0.3s ease, box-shadow 0.3s ease;
}

@keyframes nts-tour-pulse {
    0%, 100% { box-shadow: 0 0 0 6px rgba(99, 102, 241, 0.14), 0 8px 28px rgba(15, 23, 42, 0.12); }
    50%      { box-shadow: 0 0 0 10px rgba(99, 102, 241, 0.07), 0 8px 28px rgba(15, 23, 42, 0.12); }
}

/* ─────────────────────────────────────────────────────────────
   FOCUS-3b (2026-09-09) / FOCUS-3d (2026-10-09): the START WITH pick on /tasks.
   3b painted it as a separate card above the list, which then showed the same
   task again as a row. 3d (Josh: "just place it first and highlight it
   appropriately") makes the pick THE first row: a primary-tinted ring on the
   row, the eyebrow above the title, the reason under the meta - the same shape
   the Home card's Due Today block uses. Global because .list-page-item lives in
   utilities.css. FOCUS-INK-1 carried over: the eyebrow stays on --theme-primary.
   ───────────────────────────────────────────────────────────── */
.list-page-item.list-page-item--start-with {
    /* 3d-b (Josh, after the first local walk): no full primary border - it came out
       near-black on the dark-primary themes and fought the status stripe. The row
       keeps its status accent (overdue stays red); the pick reads from the tint +
       eyebrow + reason, exactly like the Home card. */
    background: var(--theme-surface-alt, #f8fafc);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--theme-primary, #6366f1) 18%, transparent), 0 2px 8px rgba(0, 0, 0, 0.04);
}
.list-page-item.list-page-item--start-with:hover {
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--theme-primary, #6366f1) 30%, transparent), 0 4px 16px rgba(0, 0, 0, 0.08);
}
.list-page-start-with-label {
    display: flex;
    align-items: center;
    gap: 4px;
    margin: 0 0 4px 0;
    font-size: 0.625rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--theme-primary, #6366f1);
}
.list-page-start-with-reason {
    margin: 6px 0 0 0;
    font-size: 0.8125rem;
    font-weight: 500;
    line-height: 1.4;
    color: var(--theme-text-secondary, #475569);
}

/* The entry point. Sized to sit beside a page's primary action without
   competing with it — this is an offer, never the main event. */
.nts-tour-btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 10px 16px;
    border: 1px solid var(--theme-border, var(--border-color, #d1d5db));
    border-radius: 8px;
    background: transparent;
    color: var(--theme-text-secondary, inherit);
    font-size: 0.9rem;
    font-weight: 500;
    cursor: pointer;
    white-space: nowrap;
    transition: border-color 0.2s ease, color 0.2s ease, background-color 0.2s ease;
}

/* TOUR-BTN-SLATE-1 (2026-09-06, Josh: "let's fix this on Slate theme"): the rest
   state inherited a muted hero colour and the HOVER painted text in --theme-primary,
   which on Slate (ExecutiveDark) is #475569 — dark slate ink on a #0f172a page, i.e.
   the label went BLACK on hover. --theme-primary is a BUTTON FILL token, not an ink
   token; on the dark themes it is only readable as a background. Ink comes from the
   text tokens (light on dark, dark on light by construction), the border from the
   theme's accent, and a whisper of --theme-link-hover-bg gives the hover a surface so
   it reads as a button on every theme. */
.nts-tour-btn:hover:not(:disabled) {
    border-color: var(--theme-accent, var(--theme-primary, #6366f1));
    color: var(--theme-text-primary, inherit);
    background: var(--theme-link-hover-bg, rgba(99, 102, 241, 0.08));
}

.nts-tour-btn:disabled { opacity: 0.6; cursor: default; }
/* TOUR-NEXT-1 (2026-09-09): the post-tour hand-off chip. Same silhouette as the tour
   button, accent border + primary ink so it reads as THE next thing, not a second tour. */
.nts-tour-btn--next {
    margin-left: 8px;
    border-color: var(--theme-accent, var(--theme-primary, #6366f1));
    color: var(--theme-text-primary, inherit);
    background: var(--theme-link-hover-bg, rgba(99, 102, 241, 0.08));
    animation: nts-tour-next-in 0.35s ease-out;
}
@keyframes nts-tour-next-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
/* TOUR-SWAP-1 (2026-09-09, Josh: "'Show me around' is demoted after users have seen it and we
   offer a new animation for 'what should I start with' - that would be prominent"). Two roles
   for the same silhouette once the Home tour has been completed:
     --lead   the prominent chip ("What should I start with?"): the --next treatment, at rest,
              plus the entrance so the swap is SEEN the moment the tour ends.
     --ghost  the demoted tour: no border at rest, secondary ink, sent to the end of the row.
              Hover brings the border back - it is still a button, just not the loud one. */
.nts-tour-btn--lead {
    border-color: var(--theme-accent, var(--theme-primary, #6366f1));
    color: var(--theme-text-primary, inherit);
    background: var(--theme-link-hover-bg, rgba(99, 102, 241, 0.08));
    animation: nts-tour-next-in 0.35s ease-out;
}
.nts-tour-btn--ghost {
    order: 2;
    margin-left: 8px;
    border-color: transparent;
    color: var(--theme-text-secondary, inherit);
    opacity: 0.85;
}
.nts-tour-btn--ghost:hover:not(:disabled) { opacity: 1; }
.nts-tour-btn--ghost.is-playing { opacity: 1; border-color: var(--theme-accent, var(--theme-primary, #6366f1)); }
.nts-tour-btn.is-playing {
    border-color: var(--theme-accent, var(--theme-primary, #6366f1));
    color: var(--theme-text-primary, inherit);
}

/* ─────────────────────────────────────────────────────────────
   HALO-1c (2026-08-21, Josh: "not so great with some of these darker themes"):
   the ring was inheriting treatments authored for the ROBOT.

   Two inherited problems, one root cause — `.pro-hero-mark` / `.pro-peek-mark`
   describe a SOLID mark:
     1. `color: var(--theme-fab-robot, #334155)`. That variable is REFERENCED in
        three places and DEFINED in none, so every theme silently falls back to
        the same dark slate. ProBot survives it (big filled shape + the rim
        below); a 6px ring at 0.18 opacity in dark slate on a dark theme is
        simply not there.
     2. PRO-PEEK-EDGE-1's chained white rim — a crisp silhouette edge on a solid
        head, a fuzzy white cloud around a thin ring.

   Fix keeps BOTH marks correct instead of retuning one and breaking the other:
   the halo gets its own wrapper modifier with an ink that is theme-DEFINED
   (--theme-primary is set per theme: slate on light, and a bright accent on
   every dark one, which is exactly the "visible against this background" token)
   and a plain depth shadow. #301's lesson holds — a monochrome mark over
   opposite-luminance surfaces needs a background-ADAPTIVE treatment, and the
   theme's own accent variable IS that adaptation, already authored per theme.
   ───────────────────────────────────────────────────────────── */
/* HALO-1e: the COLOUR moved into the component's own sheet (an element that paints
   itself cannot be out-specified by its host). What stays here is the one thing a host
   must be able to say: when the FAB keeps its CTA GRADIENT background, a theme accent
   would vanish into it, so that host asks for white. A custom property, not a colour
   rule: variables inherit and never enter a specificity contest with the component.

   HALO-1i (2026-08-21, Josh: "oops we missed one"): :not(.halo-mode) is LOAD-BEARING.
   The halo FAB renders as `halo-mode robot-mode`, and `.chat-fab.robot-mode` sets
   `background: transparent` — so there is no gradient under it and the white ink was
   landing on the PAGE: a white ring on a white admin page (invisible) and a glaring
   white ring on every dark theme (the one surface that ignored the theme entirely).
   The premise of the rule was true for the gradient FAB and false for this one; the
   halo now falls through to its own theme-aware ink like every other surface. */
.chat-fab:not(.halo-mode) {
    --nts-halo-ink: #ffffff;
}

/* ACTIVATE-CUE-1 (2026-09-02): honest "not yet interactive" cue.
   _Layout stamps <html data-nts-activating>; reconnectionHandler.js removes it
   when Blazor.start() resolves. Before that, prerendered buttons are painted
   but dead. Only fires on full page loads (in-circuit navigation never
   re-adds it). Bar colour = the reconnect modal purple, on purpose. */
html[data-nts-activating] body::before {
    content: "";
    position: fixed;
    top: 0;
    left: 0;
    height: 3px;
    width: 100%;
    z-index: 100000;
    pointer-events: none;
    background: linear-gradient(90deg, transparent 0%, #594ae2 40%, #a855f7 60%, transparent 100%);
    background-size: 200% 100%;
    animation: nts-activating-sweep 1.1s linear infinite;
}
@keyframes nts-activating-sweep {
    from { background-position: 200% 0; }
    to   { background-position: -200% 0; }
}
html[data-nts-activating] button,
html[data-nts-activating] [role="button"],
html[data-nts-activating] input,
html[data-nts-activating] select,
html[data-nts-activating] a {
    cursor: progress;
}
@media (prefers-reduced-motion: reduce) {
    html[data-nts-activating] body::before { animation: none; background: #594ae2; }
}

/* MOBILE-TASK-OVERFLOW-1 (2026-09-08): the task dialog scrolled SIDEWAYS on a phone - the Goal /
   Objective SearchableSelects in Goal Linking carry long nowrap labels, and a flex/grid child's
   default min-width:auto lets that nowrap text set the column's minimum, wider than the dialog.
   min-width:0 on the form groups lets the select's own ellipsis do its job. Applies to every
   .task-inputs-row (task + time entry dialogs share the class), harmless on desktop. */
.task-inputs-row > .task-form-group { min-width: 0; }
.task-inputs-row > .task-form-group > * { max-width: 100%; }

/* CAPTION-SIZE-1 (2026-09-11, Josh: "if the text reads better a bit bigger on mobile - it
   should be fixed for users not just for marketing content"). The read-aloud caption bar
   (audioBriefing.js builds it) was a hard-coded inline 14px with 48px side padding on every
   viewport - the smallest running text in the app, on the one surface the user is meant to
   read along with while it is spoken, and on a phone the padding took 96px of a 390px column.
   Sizes live here now, so they can be responsive at all. 16px matches body text; the bar's
   height is measured live (--nts-caption-height) so the staged avatar and page lift follow. */
#nts-audio-caption-bar { padding: 14px 48px; }
#nts-audio-caption-bar .nts-caption-eyebrow { font-size: 11px; }
#nts-audio-caption-bar .nts-caption-text { font-size: 16px; max-width: 820px; }
@media (max-width: 640px) {
    #nts-audio-caption-bar { padding: 12px 40px 12px 16px; }   /* room for the close button on the right only */
    #nts-audio-caption-bar .nts-caption-text { font-size: 16px; line-height: 1.45; }
}

/* AVATAR-ARRIVE-1 (2026-09-11): the /home hero mark's descent to the stage. audioBriefing.js
   FLIPs it (starts it from its hero position via an inline inverse transform, then releases);
   this class only lengthens and shapes the ride for that one trip - the staged rule's own
   0.4s transition serves every later move (wing glides, peeks). The bob waits until it lands
   so the two animations do not fight over margin/transform mid-flight. */
body.nts-chat-reading .pro-hero-bot.nts-hero-staged.nts-hero-arriving,
body.nts-chat-reading .welcome-avatar-container.nts-hero-staged.nts-hero-arriving {
    transition: transform 0.65s cubic-bezier(0.2, 0.8, 0.2, 1), bottom 0.65s cubic-bezier(0.2, 0.8, 0.2, 1), left 0.65s cubic-bezier(0.2, 0.8, 0.2, 1);
    animation-delay: 0.65s;
}

/* MOBILE-ZOOM-1 (2026-10-02): iOS Safari auto-zooms the page when a focused field's
   font-size is under 16px, and the page stays zoomed after blur (Josh had to pinch
   back out). Chat inputs are 0.85rem/0.95rem (13.6/15.2px). Raise every text field to
   16px on iOS only, so desktop sizing is unchanged. Deliberately NOT done by adding
   maximum-scale=1 to the viewport: that blocks pinch-zoom on Android (accessibility). */
@supports (-webkit-touch-callout: none) {
    input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]),
    textarea,
    select {
        font-size: 16px !important;
    }
}

/* BRIEF-ASSIST-1 (2026-10-09): one AI presence per surface (the /home rule, TOUR-STAGE-2). On the
   briefing view the assistant card at the top IS the presence, so the corner FAB stands down - on a
   phone it sat over the briefing's own text. It is hidden, not removed: Listen still lifts it to
   centre stage to read (.is-tracking-listen, LISTEN-STAGE-1), so it appears exactly while it talks.
   Keyed on the card's class with :has() so no page has to reach into the Assistant component. */
body:has(.bac) .floating-chat-button {
    display: none !important;
}

/* BRIEF-ASSIST-6 (2026-10-09, Josh's Listen run): the FAB was still what Listen lifted to the
   caption bar, so the page showed TWO orbs - the hero and the staged FAB floating over the
   email mid-page. Home's answer (TOUR-STAGE-2/3): stage the HERO mark. ListenCard flags the FAB
   with .is-tracking-listen; while it does, the briefing hero's mark takes the stage with the
   same geometry as Home's .nts-hero-staged orb. Its slot keeps the hero's height. */
body:has(.floating-chat-button.is-tracking-listen) .bac-hero-mark {
    position: fixed;
    left: 50vw;
    right: auto;
    top: auto;
    bottom: calc(var(--nts-caption-height, 110px) - 20px);
    transform: translateX(-50%);
    transform-origin: bottom center;
    transition: bottom 0.4s ease, transform 0.4s ease;
    z-index: 1300;
    animation: nts-chat-read-bob 4s ease-in-out infinite;
}
body:has(.floating-chat-button.is-tracking-listen) .bac-hero-mark--orb {
    /* Orb canvas overflows its box and the higher tiers have legs - tuck it onto the bar. */
    bottom: calc(var(--nts-caption-height, 110px) - 60px);
}
@media (prefers-reduced-motion: reduce) {
    body:has(.floating-chat-button.is-tracking-listen) .bac-hero-mark { animation: none; }
}

/* GLITCH-PHONE-7 (2026-10-14, QA pass at 393px): with the chat docked on a phone the page was still
   25px taller than the viewport (header + layout runway + fold remainder), enough for a scrollbar in
   a desktop emulation and a rubber-band bounce on the phone. Everything the user sees is fixed to the
   viewport (slot, dialog, strip), so the document itself has nothing to scroll: lock it. Global on
   purpose - Home's scoped sheet cannot reach <body>. */
@media (max-width: 480px) {
    body:has(.home-main--docked) {
        overflow: hidden;
    }

    /* HUB-PHONE-4 (2026-10-15, Josh's PWA on Today's Work): an iOS rubber-band at the end of the
       page shifts the layout viewport and the fixed strip rides off the bottom with it; a JS pinner
       (chatDock.js) corrects after the fact and visibly lags. Stop the bounce at the source: on a
       phone with the hub card up, the document does not over-scroll past its end, and the layout's
       bottom runway (HOME-RUNWAY-1, 32px + safe area) is cut to the strip's own clearance so there is
       no empty band to scroll into in the first place. */
    html:has(.todays-work-card),
    body:has(.todays-work-card) {
        overscroll-behavior-y: none;
    }

    .main-content.home-main-content:has(.todays-work-card) {
        padding-bottom: 0 !important;
    }
}

/* PWA-2: stamp bump 2026-10-16 00:44 */

/* FAB-SNAP-1 (2026-10-18): the ghost ring fabDrag.js drops at the bottom-centre target while a
   drag is inside the snap zone. Global because fabDrag.js creates it on <body>, outside any
   component scope. Dashed, in the theme's primary, no fill - a target, not a second avatar. */
.fab-snap-target {
    position: fixed;
    z-index: 1199;
    border-radius: 50%;
    border: 2px dashed color-mix(in srgb, var(--theme-primary, #6366f1) 70%, transparent);
    box-shadow: 0 0 0 6px color-mix(in srgb, var(--theme-primary, #6366f1) 12%, transparent);
    pointer-events: none;
    opacity: 0;
    transform: scale(0.85);
    transition: opacity 0.15s ease, transform 0.15s ease;
}

.fab-snap-target.is-on {
    opacity: 1;
    transform: scale(1);
}

@media (prefers-reduced-motion: reduce) {
    .fab-snap-target { transition: none; }
}
