/* ════════════════════════════════════════════════════════════
   TRMNL 303 — Mobile Layer
   Loaded last so it can refine any component for touch + narrow
   viewports. Nothing here should change the desktop rendering.

   Breakpoints
     ≤1024px  "compact"  — sidebar becomes a drawer, top app bar appears
     ≤768px   "phone"    — bottom tab bar, single column, condensed density
     ≤380px   "small"    — tightest gutters, shortest labels

   Sections
     1.  SHELL — top app bar, bottom tab bar, drawer, safe areas
     2.  TOUCH HYGIENE — tap targets, input zoom, scroll behaviour
     3.  DASHBOARD
     4.  MARKETS
     5.  ASSET PAGE — chart frame, analyze buttons, results
     6.  CHAT DRAWER
     7.  MODALS & SHEETS
     8.  SETTINGS
     9.  MY ANALYSES
     10. PREDICTIONS
   ════════════════════════════════════════════════════════════ */

:root {
    --topbar-height: 52px;
    --tabbar-height: 56px;
    --safe-top: env(safe-area-inset-top, 0px);
    --safe-bottom: env(safe-area-inset-bottom, 0px);
    --safe-left: env(safe-area-inset-left, 0px);
    --safe-right: env(safe-area-inset-right, 0px);
}

/* The mobile chrome only exists below the desktop breakpoint. */
.mobile-topbar,
.mobile-tabbar {
    display: none;
}

/* ════════════════════════════════════════════════════════════
   1. SHELL
   ════════════════════════════════════════════════════════════ */

@media (max-width: 1024px) {

    /* ── Top app bar ──
       Replaces the floating hamburger that used to sit on top of every
       page title. It is sticky rather than fixed so it participates in
       layout and never overlaps content. */
    .mobile-topbar {
        display: flex;
        align-items: center;
        gap: var(--space-sm);
        position: sticky;
        top: 0;
        z-index: var(--z-sticky);
        height: calc(var(--topbar-height) + var(--safe-top));
        padding: var(--safe-top) calc(var(--space-sm) + var(--safe-right)) 0 calc(var(--space-sm) + var(--safe-left));
        background: color-mix(in srgb, var(--bg-canvas) 92%, transparent);
        backdrop-filter: saturate(140%) blur(12px);
        -webkit-backdrop-filter: saturate(140%) blur(12px);
        border-bottom: 1px solid var(--border-default);
    }

    /* The bar is a sibling of .main-content inside the flex .app-shell —
       take it out of the flex row so it spans the full width. */
    .app-shell {
        display: block;
    }

    .mobile-topbar-title {
        flex: 1;
        min-width: 0;
        font-family: var(--font-mono);
        font-size: var(--text-base);
        font-weight: 600;
        letter-spacing: 0.04em;
        color: var(--text-primary);
        text-transform: uppercase;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .mobile-topbar-title .accent {
        color: var(--accent);
    }

    .mobile-topbar-actions {
        display: flex;
        align-items: center;
        gap: var(--space-xs);
        flex-shrink: 0;
    }

    /* Hamburger: now a normal item inside the bar, not a floating button. */
    .hamburger-btn {
        display: flex;
        position: static;
        top: auto;
        left: auto;
        width: 40px;
        height: 40px;
        flex-shrink: 0;
        background: transparent;
        border: 1px solid transparent;
        border-radius: var(--btn-radius);
        gap: 4px;
    }

    .hamburger-btn:hover {
        border-color: var(--border-default);
    }

    /* ── Drawer ──
       Wider than the desktop rail so watchlist rows breathe, and capped
       so the dimmed page stays visible as an affordance to close. */
    .sidebar,
    .sidebar.collapsed {
        width: min(86vw, 320px);
        padding-top: var(--safe-top);
        padding-bottom: var(--safe-bottom);
    }

    .sidebar-collapse-btn {
        display: none;
    }

    /* Roomier drawer rows — the desktop rail is built for a mouse. */
    .sidebar-nav-item {
        padding: 13px var(--space-md);
    }

    .sidebar-nav-label {
        font-size: var(--text-base);
    }

    .sidebar-watchlist-item,
    .sidebar-recent-item {
        padding: 11px var(--space-md);
        font-size: var(--text-sm);
    }

    .sidebar-section-action {
        width: 32px;
        height: 32px;
    }

    /* Remove buttons are hover-revealed on desktop; there is no hover on
       touch, so show them permanently at a usable size. */
    .sidebar-watchlist-remove {
        visibility: visible;
        width: 32px;
        height: 32px;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: var(--text-md);
    }

    .sidebar-add-input {
        font-size: 16px; /* below 16px iOS zooms the page on focus */
        padding: 9px var(--space-sm);
    }

    .sidebar-add-result {
        padding: 10px 2px;
        font-size: var(--text-sm);
    }

    .auth-logout-btn,
    .auth-login-btn {
        min-height: 40px;
        padding: var(--space-sm) var(--space-md);
    }

    .sidebar-footer {
        padding: var(--space-sm) var(--space-md) var(--space-md);
    }

    .sidebar-attribution {
        display: inline-block;
        padding: var(--space-xs) 0;
    }

    /* Content no longer needs to clear a floating button. */
    .main-content .container {
        padding: var(--space-md) calc(var(--space-md) + var(--safe-right)) var(--space-md) calc(var(--space-md) + var(--safe-left));
    }
}

@media (max-width: 768px) {

    /* ── Bottom tab bar ──
       Primary navigation inside thumb reach. The drawer keeps the
       secondary surfaces (watchlist, recent analyses, account). */
    .mobile-tabbar {
        display: grid;
        grid-template-columns: repeat(4, 1fr);
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        /* Below the drawer scrim (--z-sidebar - 1) so an open drawer dims
           and covers the tab bar instead of the bar punching through it. */
        z-index: calc(var(--z-sidebar) - 2);
        height: calc(var(--tabbar-height) + var(--safe-bottom));
        padding-bottom: var(--safe-bottom);
        background: color-mix(in srgb, var(--bg-surface) 94%, transparent);
        backdrop-filter: saturate(140%) blur(12px);
        -webkit-backdrop-filter: saturate(140%) blur(12px);
        border-top: 1px solid var(--border-default);
    }

    .mobile-tab {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 3px;
        text-decoration: none;
        color: var(--text-muted);
        border-top: 2px solid transparent;
        transition: color var(--transition-fast), border-color var(--transition-fast);
    }

    .mobile-tab-icon {
        font-size: 1.05rem;
        line-height: 1;
    }

    .mobile-tab-label {
        font-family: var(--font-mono);
        font-size: var(--text-xs);
        letter-spacing: 0.04em;
        text-transform: uppercase;
    }

    .mobile-tab.active {
        color: var(--accent);
        border-top-color: var(--accent);
        background: var(--accent-dim);
    }

    /* Every page scrolls clear of the tab bar. */
    .main-content .container {
        padding-bottom: calc(var(--tabbar-height) + var(--safe-bottom) + var(--space-lg));
    }

    /* The page name is already in the app bar. Keeping a second copy at
       the top of the scroll area costs ~60px of a ~790px screen, so hide
       it visually while leaving the heading in the accessibility tree. */
    .page-title,
    .my-analyses-header h1,
    .settings-header h1 {
        position: absolute;
        width: 1px;
        height: 1px;
        margin: -1px;
        padding: 0;
        overflow: hidden;
        clip: rect(0 0 0 0);
        clip-path: inset(50%);
        white-space: nowrap;
    }

    .page-header,
    .my-analyses-header,
    .settings-header {
        margin-bottom: var(--space-md);
    }

    /* A header whose only child is the now-hidden title collapses away
       entirely rather than leaving a band of empty space under the bar. */
    .page-header:not(:has(.page-subtitle)),
    .my-analyses-header:not(:has(p)),
    .settings-header:not(:has(p)) {
        margin-bottom: 0;
    }

    .page-subtitle,
    .my-analyses-header .muted-text,
    .settings-header p {
        margin-top: 0;
    }

    /* "Back to Home" duplicates the tab bar. */
    .settings-container .back-link {
        display: none;
    }
}

/* ════════════════════════════════════════════════════════════
   2. TOUCH HYGIENE
   ════════════════════════════════════════════════════════════ */

@media (max-width: 1024px) {

    html {
        /* Stops iOS from re-flowing text at its own scale in landscape. */
        -webkit-text-size-adjust: 100%;
    }

    body {
        /* No rubber-band chaining into the drawer/sheet behind. */
        overscroll-behavior-y: none;
    }

    /* A grey flash on every tap reads as jank on a dark UI. */
    a, button, select, input, textarea, [role="button"], [onclick] {
        -webkit-tap-highlight-color: transparent;
    }

    /* Anything focusable keeps a visible ring for keyboard/switch users. */
    a:focus-visible,
    button:focus-visible,
    select:focus-visible,
    input:focus-visible,
    textarea:focus-visible {
        outline: 2px solid var(--accent);
        outline-offset: 2px;
    }

    /* 16px is the threshold below which iOS Safari zooms the viewport on
       focus and never zooms back out. Applies to every text entry. */
    input[type="text"],
    input[type="search"],
    input[type="number"],
    input[type="email"],
    input:not([type]),
    select,
    textarea {
        font-size: 16px;
    }

    /* Scroll lock while the drawer or a sheet is open. */
    body.mobile-scroll-locked {
        overflow: hidden;
        position: fixed;
        width: 100%;
    }

    /* The decorative grid/gradient is fixed and 100vw — on iOS the
       dynamic toolbar makes 100vh taller than the visual viewport, so
       pin it to the layout viewport instead. */
    .bg-grid,
    .bg-gradient {
        width: 100%;
        height: 100dvh;
    }
}

/* ════════════════════════════════════════════════════════════
   3. DASHBOARD
   ════════════════════════════════════════════════════════════ */

@media (max-width: 768px) {

    /* Pulse strip: swipes horizontally instead of forcing the page wide. */
    .market-pulse {
        gap: var(--space-sm);
        padding: var(--space-sm) var(--space-md);
        scroll-snap-type: x proximity;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        margin-left: calc(-1 * var(--space-md));
        margin-right: calc(-1 * var(--space-md));
        border-left: 0;
        border-right: 0;
        border-radius: 0;
        margin-bottom: var(--space-md);
    }

    .market-pulse::-webkit-scrollbar {
        display: none;
    }

    .market-pulse > * {
        flex: 0 0 auto;
        scroll-snap-align: center;
    }

    .dashboard-grid {
        grid-template-columns: 1fr;
        gap: var(--space-md);
    }

    .dash-card {
        margin-bottom: var(--space-md);
    }

    .dash-card-header {
        padding: var(--space-sm) var(--space-md);
    }

    /* "VIEW ALL →" / "REFRESH ↻" are 18px-tall text links on desktop. */
    .dash-card-action {
        display: inline-flex;
        align-items: center;
        min-height: 36px;
        padding-left: var(--space-xs);
    }

    /* Gainers and losers stack rather than squeezing into two columns. */
    .top-movers {
        grid-template-columns: 1fr;
        gap: var(--space-md);
    }

    /* Watchlist rows: symbol + sparkline + price, comfortable height. */
    .watchlist-row {
        padding: var(--space-sm) var(--space-md);
        min-height: 56px;
        gap: var(--space-sm);
    }

    .watchlist-row .watchlist-spark {
        display: none; /* the number is the signal; the sparkline is decoration at this width */
    }

    .recent-analysis-item {
        padding: var(--space-sm) var(--space-md);
        min-height: 56px;
    }

    .recent-analysis-info {
        min-width: 0;
    }

    .recent-analysis-title,
    .recent-analysis-meta {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

/* ════════════════════════════════════════════════════════════
   4. MARKETS
   ════════════════════════════════════════════════════════════ */

@media (max-width: 768px) {

    /* Market/asset-class tabs swipe rather than wrap or clip. */
    .asset-tabs {
        flex-wrap: nowrap;
        overflow-x: auto;
        scroll-snap-type: x proximity;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        gap: var(--space-sm);
        margin-left: calc(-1 * var(--space-md));
        margin-right: calc(-1 * var(--space-md));
        padding: 0 var(--space-md) var(--space-xs);
    }

    .asset-tabs::-webkit-scrollbar {
        display: none;
    }

    .asset-tab {
        flex: 0 0 auto;
        scroll-snap-align: start;
        min-height: 44px;
        padding: var(--space-sm) var(--space-md);
    }

    /* ── Market table → list rows ──
       A four-column financial table cannot survive 393px. Each <tr>
       becomes a two-row grid: identity on the left spanning both rows,
       price above change on the right. The table markup (and therefore
       its semantics) is untouched — only the display is re-laid out. */
    .market-table,
    .market-table tbody {
        display: block;
        width: 100%;
    }

    .market-table thead {
        display: none;
    }

    /* Specificity note: this must out-rank `.market-table tbody` above and
       any element-level `tr` rule, hence the qualified selector. */
    .market-table tr.market-row {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        align-items: center;
        column-gap: var(--space-sm);
        padding: var(--space-sm) var(--space-md);
        border-bottom: 1px solid var(--border-default);
        min-height: 64px;
    }

    .market-table tr.loading-row,
    .market-table tr.empty-row {
        display: block;
        padding: var(--space-md);
    }

    .market-row td {
        display: block;
        padding: 0;
        border: 0;
    }

    .market-row .col-name {
        grid-column: 1;
        grid-row: 1 / span 2;
        min-width: 0;
    }

    .market-row .col-price {
        grid-column: 2;
        grid-row: 1;
        text-align: right;
        font-size: var(--text-base);
        white-space: nowrap;
    }

    .market-row .col-change {
        grid-column: 2;
        grid-row: 2;
        text-align: right;
        font-size: var(--text-sm);
        white-space: nowrap;
    }

    .market-row .col-volume,
    .market-row .col-sector,
    .market-row .col-marketcap {
        display: none;
    }

    .asset-name-wrapper,
    .asset-text {
        min-width: 0;
    }

    .asset-symbol {
        font-size: var(--text-base);
    }

    /* No hard cap — the grid's minmax(0,1fr) column already bounds the
       name, so it uses whatever width the price leaves and ellipses
       only when it actually has to. */
    .asset-full-name {
        display: block;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* Search + filters */
    .stocks-search-bar {
        flex-direction: column;
        align-items: stretch;
        gap: var(--space-sm);
        padding: var(--space-sm);
    }

    .search-input-wrapper {
        width: 100%;
        min-width: 0;
        padding: 0.45rem 0.75rem;
    }

    .stock-search-input {
        min-width: 0;
        font-size: 16px;
    }

    .stocks-count,
    .predictions-count {
        text-align: center;
        width: 100%;
    }

    /* The card gutter is a desktop-scale 24px; on a phone it eats a tenth
       of the width before any content is drawn. */
    .control-panel {
        padding: var(--space-md) var(--space-sm);
    }

    .market-overview-header {
        margin-bottom: var(--space-md);
        padding: 0 var(--space-sm);
    }

    /* Also the predictions category strip, which reuses this class. */
    .stocks-filter-badges {
        flex-wrap: nowrap;
        overflow-x: auto;
        scroll-snap-type: x proximity;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        justify-content: flex-start;
        margin-left: calc(-1 * var(--space-md));
        margin-right: calc(-1 * var(--space-md));
        padding: 0 var(--space-md);
    }

    .stocks-filter-badges::-webkit-scrollbar {
        display: none;
    }

    .stocks-filter-badges > * {
        flex: 0 0 auto;
        scroll-snap-align: start;
        min-height: 40px;
    }

    /* Prediction market cards go single-column. */
    .predictions-grid {
        grid-template-columns: 1fr;
    }
}

/* ════════════════════════════════════════════════════════════
   5. ASSET PAGE
   ════════════════════════════════════════════════════════════ */

@media (max-width: 768px) {

    /* The chat button must clear the tab bar, and it sits on top of the
       analysis it is meant to discuss — so it drops the label and
       becomes a 52px circular FAB, the smallest shape that still reads
       as "chat" while covering the least content. */
    .floating-chat-btn {
        bottom: calc(var(--tabbar-height) + var(--safe-bottom) + var(--space-md));
        right: var(--space-md);
        width: 52px;
        height: 52px;
        padding: 0;
        justify-content: center;
        border-radius: 50%;
        background: var(--bg-elevated);
        border-color: var(--border-active);
    }

    .floating-chat-btn .chat-text {
        display: none;
    }

    .floating-chat-btn .chat-icon-small {
        font-size: 1.35rem;
    }

    /* Section headers are flex rows; at this width each word-group gets
       squeezed into its own narrow column instead of flowing as one
       sentence. Inline flow wraps the way prose should. */
    .section-title {
        display: block;
    }

    .section-title .title-icon {
        margin-right: 0.4rem;
    }

    /* The tab bar already offers Markets, and the app bar names the
       asset — so the back control shrinks to a compact text link
       instead of a full-width button eating 70px of the first screen. */
    .back-navigation {
        margin-bottom: var(--space-sm);
    }

    .back-btn {
        min-height: 40px;
        padding: 0 var(--space-sm) 0 0;
        border: 0;
        font-size: var(--text-xs);
        color: var(--text-muted);
    }

    .back-btn .back-text {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* Replay transport: keep the counter on one line. */
    .tchart-scrub-label {
        white-space: nowrap;
    }

    /* ── Chart frame ──
       The desktop chrome is five stacked rows of controls above a 420px
       canvas; on a phone that leaves no chart. Everything collapses to
       one header line plus swipeable control rows, and the canvas gets
       the height back. */
    .tchart-frame {
        margin-left: calc(-1 * var(--space-md));
        margin-right: calc(-1 * var(--space-md));
        border-left: 0;
        border-right: 0;
        border-radius: 0;
    }

    .tchart-toolbar {
        gap: var(--space-sm);
        padding: var(--space-sm) var(--space-md);
        flex-wrap: wrap;
        min-width: 0;
    }

    .tchart-title-group {
        flex: 1 1 100%;
        align-items: center;
        gap: var(--space-sm);
        flex-wrap: wrap;
    }

    .tchart-live .tchart-title,
    .tchart-title {
        font-size: var(--text-sm);
    }

    .tchart-price {
        font-size: var(--text-md);
    }

    /* The whole control cluster goes: pinch replaces the zoom steppers and
       double-tap-to-fit replaces FIT, leaving only fullscreen — which is
       better placed on the chart itself than in a row of its own. */
    .tchart-controls {
        display: none;
    }

    .tchart-expand-fab {
        display: flex;
    }

    .tchart-btn {
        height: 36px;
        min-width: 40px;
    }

    .tchart-btn-icon {
        width: 40px;
    }

    /* ── Chip bar → three dropdowns ──
       Three always-visible lanes (11 timeframes, 4 indicators, 7 tools) cost
       three rows of chrome above the chart and read as a wall of buttons.
       Each lane collapses behind a trigger, so the bar is one row and only
       the lane you opened is on screen. */
    .tchart-bar {
        flex-direction: row;
        flex-wrap: wrap;
        align-items: center;
        gap: 6px;
        padding: var(--space-sm) var(--space-md);
    }

    .tchart-lane {
        display: block;   /* `contents` on desktop — a real box here */
        position: relative;
    }

    .tchart-lane-trigger {
        display: inline-flex;
        align-items: center;
        gap: 4px;
    }

    .tchart-lane-trigger.open {
        color: var(--accent);
        background: var(--accent-dim);
        border-color: var(--border-active);
    }

    /* The lane itself becomes the dropdown panel. Anchored to the viewport
       rather than the trigger so a lane near the right edge cannot open
       off-screen. */
    .tchart-bar .tchart-chips {
        display: none;
        position: absolute;
        top: calc(100% + 6px);
        left: 0;
        z-index: var(--z-dropdown);
        min-width: max(160px, 100%);
        max-width: calc(100vw - var(--space-md) * 2);
        max-height: 52vh;
        overflow-y: auto;
        overscroll-behavior: contain;
        flex-wrap: wrap;
        gap: 6px;
        padding: var(--space-sm);
        background: var(--bg-elevated);
        border: 1px solid var(--border-default);
        border-radius: var(--card-radius);
    }

    .tchart-bar .tchart-chips.open {
        display: flex;
    }

    /* The tools lane is the widest and sits last — open it leftwards. */
    .tchart-lane[data-lane="tools"] .tchart-chips {
        left: auto;
        right: 0;
    }

    /* An armed drawing tool has to be visible with the lane shut. */
    .tchart-lane-trigger.armed {
        color: var(--accent);
        background: var(--accent-dim);
        border-color: var(--border-active);
    }

    /* Timeframes are uniform tokens — a fixed grid reads far tidier than
       ragged wrapping across three different chip widths. Needs the
       `.tchart-bar` qualifier to out-rank the `display: flex` panel rule
       above; grid items stretch on their own, so no width is set (a
       `width: 100%` here would stack them if the flex rule ever won). */
    .tchart-bar .tchart-timeframes.open {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        min-width: 190px;
    }

    .tchart-bar .tchart-timeframes .tchart-chip {
        min-width: 0;
        justify-content: center;
    }

    /* Inside the indicators dropdown the SMA/EMA/RSI period lists become an
       inline accordion. A popover opening on top of a popover is unusable on
       a phone, and this needs no extra JS — the existing chip handler
       already toggles `.open`. */
    .tchart-indicators .tchart-chip-group { width: 100%; }

    .tchart-indicators .tchart-popover {
        position: static;
        min-width: 0;
        max-height: none;
        margin-top: 6px;
        background: transparent;
        border: 0;
        padding: 0;
    }

    .tchart-indicators .tchart-popover-items {
        display: flex;
        flex-wrap: wrap;
        gap: 6px;
    }

    .tchart-save-state { align-self: center; }

    .tchart-chip {
        min-height: 34px;
        padding: 6px 12px;
        display: inline-flex;
        align-items: center;
    }

    .tchart-timeframes .tchart-chip {
        min-width: 42px;
        justify-content: center;
    }

    .tchart-popover-item {
        min-height: 40px;
    }

    /* Give the chart real estate back. */
    .tchart-canvas {
        height: 46vh;
        min-height: 300px;
    }

    /* Drag-to-resize is a mouse affordance; pinch/expand replaces it. */
    .tchart-resize-handle {
        display: none;
    }

    /* Transport (3×40) + scrubber + counter + speed needs 322px of run;
       the desktop 16px side gutter left only 323px at 393px wide and
       the row broke. Trim the gutter and the scrubber's basis so it
       holds one line down to ~360px, and still wrap below that. */
    .tchart-replay {
        padding: var(--space-sm);
        gap: var(--space-sm);
        flex-wrap: wrap;
        min-width: 0;
    }

    .tchart-scrub {
        flex: 1 1 60px;
        min-width: 50px;
    }

    .tchart-scrub {
        height: 4px;
    }

    .tchart-scrub::-webkit-slider-thumb {
        width: 18px;
        height: 18px;
    }

    .tchart-scrub-label {
        min-width: 62px;
        font-size: var(--text-xs);
        white-space: nowrap;
    }

    /* ── Fullscreen (EXPAND) ──
       The frame is naturally taller than a phone viewport, and the
       centred overlay pushed its toolbar — the only way back out — off
       the top of the screen. Pin the frame to the viewport and let the
       stage flex into whatever the chrome leaves; TrmnlChart's
       ResizeObserver picks the new canvas box up on its own. */
    .tchart-overlay {
        padding: 0;
        align-items: stretch;
    }

    .tchart-overlay-inner {
        width: 100%;
        height: 100dvh;
        display: flex;
    }

    .tchart-overlay .tchart-frame {
        /* cancel the edge-bleed margins used in the page flow */
        margin: 0;
        flex: 1;
        display: flex;
        flex-direction: column;
        min-height: 0;
    }

    .tchart-overlay .tchart-stage {
        flex: 1 1 auto;
        min-height: 0;
    }

    .tchart-overlay .tchart-canvas,
    .tchart-expanded .tchart-canvas {
        height: 100%;
        min-height: 0;
    }

    /* The HUD sits over the candles. Reserve the price-axis gutter so a long
       OHLC line wraps before it reaches the axis labels instead of sitting
       on top of them. */
    .tchart-hud {
        font-size: 0.65rem;
        padding: 2px 6px;
        max-width: calc(100% - 96px);
        gap: 4px var(--space-sm);
    }

    /* ── Analysis controls ── */
    .analyze-buttons-grid {
        grid-template-columns: 1fr;
        gap: var(--space-sm);
    }

    .analyze-btn {
        height: 52px;
    }

    .ai-provider-container {
        flex-direction: column;
        align-items: stretch;
        gap: var(--space-xs);
    }

    .ai-provider-select {
        width: 100%;
        min-height: 44px;
    }

    /* ── Results ──
       Analysis output is long-form: give it comfortable measure, and
       make sure nothing inside can push the page sideways. */
    .results-section,
    .result-card,
    .mv2-domain-card {
        min-width: 0;
    }

    /* Nested analysis containers each carry a desktop-scale gutter; the
       chain container(16) + card(16) + body(32) + section(24) left an
       embedded chart just 207px of a 393px screen. Flatten them. */
    .result-card,
    .analysis-section {
        padding: var(--space-sm);
    }

    .result-body,
    .candle-config-section {
        padding: var(--space-sm);
    }

    .result-header {
        padding: var(--space-sm) var(--space-md);
    }

    /* ── Detected-pattern chips (candle / pattern analysis) ──
       Wrapping, 20-40 of them build a 700-1400px wall above the chart
       they annotate. A two-row horizontal carousel keeps the chart in
       view and halves the swipe distance of a single lane. */
    .tchart-frame > .tchart-chips {
        display: grid;
        grid-auto-flow: column;
        grid-template-rows: repeat(2, auto);
        grid-auto-columns: max-content;
        gap: 6px;
        overflow-x: auto;
        overscroll-behavior-x: contain;
        scroll-snap-type: x proximity;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        min-width: 0;
        padding: var(--space-sm) var(--space-md);
    }

    .tchart-frame > .tchart-chips::-webkit-scrollbar {
        display: none;
    }

    .tchart-frame > .tchart-chips > * {
        scroll-snap-align: start;
        max-width: 190px;
    }

    /* Candle/pattern timeframe tabs (4H / 1D) */
    .candle-tabs-bar {
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
    }

    .candle-tabs-bar::-webkit-scrollbar {
        display: none;
    }

    .candle-tab-btn {
        flex: 0 0 auto;
        min-height: 44px;
    }

    .indicators-grid,
    .metrics-grid,
    .domain-grid,
    .grid-factors {
        grid-template-columns: 1fr;
        gap: var(--space-sm);
    }

    /* Verdict headers put a label and a badge on one row with a 16px gap;
       below ~400px that becomes two cramped columns. */
    .verdict-header {
        flex-wrap: wrap;
        padding: var(--space-md);
        margin-bottom: var(--space-md);
    }

    /* Long numbers, tickers and URLs must wrap rather than overflow. */
    .results-section table {
        display: block;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }

    .results-section pre,
    .message-text pre {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }

    .results-section a,
    .message-text a,
    .source-link {
        overflow-wrap: anywhere;
    }

    .source-item {
        padding: var(--space-sm);
    }

    /* Meta V2 metric row + back control */
    .mv2-metrics-row {
        flex-wrap: wrap;
        gap: var(--space-sm);
    }

    .mv2-metric-btn,
    .mv2-back-btn {
        min-height: 40px;
    }
}

/* ════════════════════════════════════════════════════════════
   6. CHAT DRAWER
   Full-screen sheet on a phone. The desktop drawer is driven by inline
   `right`/`width` written by layout.js, so these rules use !important
   to stay authoritative regardless of the persisted desktop width.
   ════════════════════════════════════════════════════════════ */

@media (max-width: 768px) {

    .chat-drawer {
        width: 100% !important;
        max-width: 100% !important;
        min-width: 0 !important;
        right: 0 !important;
        left: 0 !important;
        top: 0;
        height: 100dvh;
        border-left: 0;
        /* Slide from the bottom — the natural direction for a sheet. */
        transform: translateY(100%);
        transition: transform var(--transition-normal);
        padding-bottom: var(--safe-bottom);
    }

    .chat-drawer.active {
        transform: translateY(0);
    }

    .chat-resize-handle {
        display: none;
    }

    .chat-drawer-header {
        padding: calc(var(--safe-top) + var(--space-sm)) var(--space-md) var(--space-sm);
        min-height: 52px;
    }

    .close-chat-btn {
        width: 40px;
        height: 40px;
        font-size: var(--text-md);
    }

    .chat-messages {
        padding: var(--space-md);
        overscroll-behavior: contain;
    }

    .message-text {
        font-size: var(--text-base);
        overflow-wrap: anywhere;
    }

    /* The composer sits above the keyboard; controls wrap to their own
       row so the text field keeps its full width. */
    .chat-input-container {
        padding: var(--space-sm) var(--space-md) calc(var(--space-sm) + var(--safe-bottom));
        gap: var(--space-sm);
    }

    /* Stacked one-per-line the controls cost ~112px of a sheet that also
       has to hold the keyboard. Two rows — model picker, then both
       toggles side by side — costs ~72px and keeps every control on
       screen, which a horizontal scroller would not. */
    .chat-controls-row {
        flex-wrap: wrap;
        align-items: center;
        gap: var(--space-sm) var(--space-md);
        margin-bottom: 0;
        min-width: 0;
    }

    .chat-model-selector {
        flex: 1 1 100%;
        min-width: 0;
    }

    .chat-model-select {
        width: 100%;
        min-height: 40px;
    }

    .live-data-toggle {
        flex: 0 0 auto;
        gap: 6px;
        font-size: var(--text-xs);
        white-space: nowrap;
    }

    /* The "?" affordance only reveals a title tooltip on hover, which
       touch never fires — it is dead weight on a phone. */
    .live-data-toggle .toggle-info {
        display: none;
    }

    .chat-input {
        font-size: 16px;
        min-height: 44px;
        max-height: 35vh;
    }

    .send-chat-btn {
        width: 44px;
        height: 44px;
        flex-shrink: 0;
    }

    /* The sheet covers the viewport, so the scrim behind it is noise. */
    .chat-overlay {
        display: none;
    }
}

/* ════════════════════════════════════════════════════════════
   7. MODALS & SHEETS
   ════════════════════════════════════════════════════════════ */

@media (max-width: 768px) {

    .login-modal-card,
    .wl-modal-card {
        width: 100%;
        max-width: 100%;
        max-height: 92dvh;
        margin: 0;
        border-radius: var(--card-radius) var(--card-radius) 0 0;
        padding: var(--space-lg) var(--space-md) calc(var(--space-lg) + var(--safe-bottom));
        overflow-y: auto;
        overscroll-behavior: contain;
    }

    .login-modal-overlay,
    .wl-modal-overlay {
        align-items: flex-end;
        padding: 0;
    }

    .login-modal-close,
    .wl-modal-close {
        width: 44px;
        height: 44px;
        font-size: var(--text-lg);
    }

    .login-oauth-btn {
        min-height: 48px;
        width: 100%;
    }

    .wl-columns {
        grid-template-columns: 1fr;
    }

    .wl-btn {
        min-height: 40px;
    }

    /* Row-level icon buttons (remove, rename) are 12px glyphs on desktop —
       far under a fingertip. Pad them out without changing the glyph. */
    .wl-icon-btn {
        min-width: 36px;
        min-height: 36px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        font-size: var(--text-md);
    }

    .wl-select,
    .wl-item-move {
        min-height: 36px;
        font-size: var(--text-sm);
    }

    .wl-input,
    .wl-search,
    .wl-modal-card input[type="text"] {
        font-size: 16px;
        min-height: 44px;
    }

    .wl-item-name {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

/* ════════════════════════════════════════════════════════════
   8. SETTINGS
   ════════════════════════════════════════════════════════════ */

@media (max-width: 768px) {

    /* Description text was being squeezed into a ~140px column beside
       the switch. Stack them: label + copy, then the switch on its own
       row aligned right. */
    .setting-item {
        flex-direction: column;
        align-items: stretch;
        gap: var(--space-sm);
        padding: var(--space-md) 0;
    }

    .setting-info {
        max-width: none;
        padding-right: 0;
    }

    .setting-description {
        font-size: var(--text-sm);
        line-height: 1.55;
    }

    .toggle-switch {
        align-self: flex-start;
    }

    .settings-section {
        padding: var(--space-md);
    }
}

/* ════════════════════════════════════════════════════════════
   9. MY ANALYSES
   ════════════════════════════════════════════════════════════ */

@media (max-width: 768px) {

    .my-analyses-filters .filter-group {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: var(--space-sm);
    }

    .filter-search {
        grid-column: 1 / -1;
        width: 100%;
    }

    .filter-select,
    .filter-search {
        min-height: 44px;
        font-size: 16px;
    }

    .my-analyses-container {
        padding: 0;
    }

    .load-more-container .btn-secondary,
    .my-analyses-empty .btn-primary,
    .my-analyses-auth-gate .btn-primary {
        min-height: 44px;
        width: 100%;
    }

    /* Three side-by-side groups with 140px/100px minimums collide well
       before 393px — the name and the provider literally overlap. Two
       grid rows instead: identity + timing on top, badges underneath.
       (flex-wrap alone cannot do this: `.analysis-item-center` has
       `flex: 1`, so its `flex-basis: 0` beats any width we set.) */
    .analysis-list-item {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        align-items: center;
        gap: 6px var(--space-sm);
        padding: var(--space-sm) var(--space-md);
        min-height: 64px;
    }

    .analysis-item-left {
        grid-column: 1;
        grid-row: 1;
        min-width: 0;
        gap: var(--space-sm);
    }

    .analysis-item-right {
        grid-column: 2;
        grid-row: 1;
        min-width: 0;
        justify-content: flex-end;
    }

    .analysis-item-center {
        grid-column: 1 / -1;
        grid-row: 2;
        flex: none;
        flex-wrap: wrap;
        justify-content: flex-start;
    }

    .analysis-item-name {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

/* ════════════════════════════════════════════════════════════
   10. PREDICTIONS
   ════════════════════════════════════════════════════════════ */

@media (max-width: 768px) {

    .prediction-content-row {
        grid-template-columns: 1fr;
        gap: var(--space-md);
    }

    .prediction-odds-panel {
        order: -1;
    }

    .prediction-header-row {
        flex-direction: column;
        align-items: flex-start;
        gap: var(--space-sm);
    }

    .prediction-meta-info {
        flex-wrap: wrap;
        gap: var(--space-sm) var(--space-md);
    }

    .prediction-title {
        font-size: var(--text-md);
        line-height: 1.35;
    }

    .timeframe-btn {
        min-height: 40px;
    }
}

/* ════════════════════════════════════════════════════════════
   11. SMALL PHONES (≤380px)
   ════════════════════════════════════════════════════════════ */

@media (max-width: 380px) {

    .main-content .container {
        padding-left: calc(var(--space-sm) + var(--safe-left));
        padding-right: calc(var(--space-sm) + var(--safe-right));
    }

    .market-pulse,
    .asset-tabs,
    .tchart-frame,
    .stocks-filter-badges {
        margin-left: calc(-1 * var(--space-sm));
        margin-right: calc(-1 * var(--space-sm));
    }

    .asset-tabs,
    .stocks-filter-badges {
        padding-left: var(--space-sm);
        padding-right: var(--space-sm);
    }

    .mobile-tab-label {
        font-size: 0.62rem;
    }

}

/* ════════════════════════════════════════════════════════════
   12. SHORT VIEWPORTS (landscape phone)
   A 390px-tall viewport cannot afford a 52px bar, a 56px bar and a
   300px chart. Everything chrome-like gets shorter and the chart takes
   a percentage of what is actually left.
   ════════════════════════════════════════════════════════════ */

@media (max-width: 1024px) and (max-height: 500px) {

    :root {
        --topbar-height: 44px;
        --tabbar-height: 44px;
    }

    .mobile-tab {
        flex-direction: row;
        gap: 6px;
    }

    .mobile-tab-icon {
        font-size: 0.9rem;
    }

    .mobile-tab-label {
        font-size: 0.62rem;
    }

    .tchart-canvas {
        height: 64vh;
        min-height: 170px;
    }

    /* A landscape phone is short but wide — put the chip lanes back on
       shared rows instead of stacking one per line. */
    .tchart-bar {
        flex-direction: row;
        flex-wrap: wrap;
        align-items: center;
        gap: 4px var(--space-md);
        padding: 4px var(--space-sm);
    }

    .tchart-bar .tchart-chips {
        padding: 0;
    }

    /* Timeframes keep a full row (most-used, and 11 chips wide);
       indicators and tools share the next one and scroll if they must.
       Three side-by-side scrollers would be worse than the row saved. */
    .tchart-bar .tchart-timeframes {
        flex: 1 1 100%;
    }

    .tchart-bar .tchart-indicators {
        flex: 0 1 auto;
        min-width: 130px;
    }

    /* flex-basis:0 is load-bearing — line breaking uses each item's
       hypothetical (max-content) size, and shrinking only happens after
       items are assigned to a line. With `auto` the tools lane's
       486px max-content forces its own row even though it can shrink. */
    .tchart-bar .tchart-tools {
        flex: 1 1 0;
        min-width: 160px;
    }

    /* The tab bar already reaches Markets; 40px of back link is a tenth
       of the viewport height here. */
    .back-navigation {
        display: none;
    }

    .tchart-chip {
        min-height: 30px;
        padding: 4px 10px;
    }

    .chat-drawer-header {
        min-height: 44px;
    }

    .chat-input {
        max-height: 22vh;
    }
}

/* ════════════════════════════════════════════════════════════
   13. REDUCED MOTION
   ════════════════════════════════════════════════════════════ */

@media (prefers-reduced-motion: reduce) {
    .sidebar,
    .chat-drawer,
    .sidebar-overlay,
    .mobile-tab {
        transition: none;
    }
}
