/* ==========================================================================
   BloBrowser Pro Terminal — High-Performance Crypto Trading Design System
   ========================================================================== */

:root {
    /* 3.242.0 (owner, 2026-10-17): the platform's text font, 'BloHunter Sans' (Selawik, self-hosted, fonts.css), then
       the system's own for the moment before it loads; monospace only for code-like text (a raw key field) */
    --font-text: 'BloHunter Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    --font-code: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    --bg-base: #080c14;
    /* 3.242.0 (owner): every card solid over the starfield (the colour the 82% card made over the old page) */
    --bg-card: #0f1624;
    /* 3.246.0 (owner, 2026-10-19, BloHunter's shell.css exactly): every card's surface, the panels' blue-black and the
       sidebar's purple blended, drifting slowly across the page; fixed to the window on desktop, so the cards read as
       windows onto one surface (--drift moves it: the one clock below); per card on phones and tablets */
    --drift-surface: linear-gradient(120deg, #121722 0%, #1a1a2e 25%, #121722 50%, #1a1a2e 75%, #121722 100%);
    --drift-attach: fixed;
    
    --border-subtle: rgba(255, 255, 255, 0.07);
    --border-card: rgba(255, 255, 255, 0.11);
    
    --text-primary: #f8fafc;
    --text-secondary: #94a3b8;
    --text-muted: #64748b;
    --text-dim: #475569;
    
    --accent: #38bdf8;
    --accent-glow: rgba(56, 189, 248, 0.25);
    
    --bull: #10b981;
    --bull-border: rgba(16, 185, 129, 0.35);
    
    --bear: #f43f5e;
    --bear-bg: rgba(244, 63, 94, 0.12);
    --bear-border: rgba(244, 63, 94, 0.35);
    
    --warn: #f59e0b;
    --warn-border: rgba(245, 158, 11, 0.35);
    
    --radius-sm: 8px;
    --radius-md: 12px;
    --radius-lg: 16px;
    
    --shadow-glass: 0 8px 32px 0 rgba(0, 0, 0, 0.45);
}

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

/* 3.242.0 (owner, 2026-10-17; the platform's page background, as blohunter.com): solid black behind everything (the
   old blue glows are gone), the starfield on it (starfield.js), the cards solid on top */
html { background: #000000; }
body {
    font-family: var(--font-text);
    color: var(--text-primary);
    background: #000000;
    min-height: 100vh;
    overflow-x: hidden;
    overscroll-behavior-x: none;
    /* Async card loads were re-anchoring the mobile viewport ~30% down the
       page on open (browser scroll anchoring) — pin anchoring off. */
    overflow-anchor: none;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
}

/* 3.242.0 (owner, 2026-10-17): the class keeps its name (hundreds of uses) but no longer means a monospaced font: the
   text font with tabular figures, so number columns stay straight in Selawik (docs/STYLE_GUIDE.md § Text font) */
.mono {
    font-family: var(--font-text);
    font-variant-numeric: tabular-nums;
}

/* 3.246.0 (BloHunter's): the cards' drifting surface, one clock for the whole page so every card stays in step. Two
   steps a second over two minutes, there and back: a gentle shift, the cards repainted twice a second, never every
   frame; still for anyone who asks for less motion. Phones and tablets (iOS Safari above all) paint a background fixed
   to the window in a scrolling column only after the scroll settles, so the cards showed black while scrolling: there
   each card carries the same blend itself, standing still */
@property --drift {
    syntax: '<percentage>';
    inherits: true;
    initial-value: 0%;
}
:root { animation: bb-surface-drift 120s steps(240) infinite alternate; }
@keyframes bb-surface-drift { to { --drift: 100%; } }
@media (prefers-reduced-motion: reduce) { :root { animation: none; } }
@media (max-width: 820px), (hover: none) { :root { --drift-attach: scroll; animation: none; } }

/* Glass Panels */
.glass-panel {
    background: var(--drift-surface) var(--drift) 50% / 200% 100% var(--drift-attach);
    background-color: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-glass);
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.glass-panel:hover {
    border-color: rgba(255, 255, 255, 0.16);
}

.glass-panel-subtle {
    background: var(--drift-surface) var(--drift) 50% / 200% 100% var(--drift-attach); /* 3.246.0: BloHunter's surface */
    background-color: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
}

/* Header & Top Bar */
.top-header {
    background: #080c14; /* 3.242.0: a solid strip over the starfield */
    border-bottom: 1px solid var(--border-card);
    position: sticky;
    top: 0;
    z-index: 100;
}

.brand-badge {
    background: linear-gradient(135deg, #0ea5e9 0%, #6366f1 100%);
    color: #ffffff;
    font-weight: 800;
    letter-spacing: -0.02em;
    /* 2026-10-01 (operator): the same height as the delivery pill beside it (4 px + a 1 px edge, as .status-pill) */
    padding: 4px 10px;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    box-shadow: 0 0 15px rgba(14, 165, 233, 0.5);
    display: inline-flex;
    align-items: center;
}

/* 3.143.0: the brand badge is the home link */
a.brand-home {
    cursor: pointer;
    text-decoration: none;
    transition: box-shadow 0.15s ease, filter 0.15s ease;
}

a.brand-home:hover,
a.brand-home:focus-visible {
    filter: brightness(1.12);
    box-shadow: 0 0 20px rgba(14, 165, 233, 0.75);
    outline: none;
}

.badge-chip {
    /* 3.237.0 (operator, 2026-10-17): every pill in the style guide's one format, BloHunter's chip: squared (5px
       corners), 10px bold, padding 2px 6px; no pill sets its own size (docs/STYLE_GUIDE.md § Layout) */
    padding: 2px 6px;
    border-radius: 5px;
    font-size: 10px;
    line-height: 1.3;
    font-weight: 700;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    display: inline-flex;
    align-items: center;
    gap: 5px;
}

/* Compact position labels share the contract's left edge and stay together.
   The same compact badge dimensions apply to recent-exit headers. */
.position-pills {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: flex-start;
    gap: 3px;
    margin-top: 2px;
}

/* 3.235.0 (operator, 2026-10-16): BloHunter's squared chip (its .chip: 2px 6px, radius 5px, 10px bold), so more fit;
   the colours stay BloBrowser's */
.position-pills .badge-chip,
.exit-card-header .badge-chip {
    flex-shrink: 0;
    padding: 2px 6px;
    border-radius: 5px;
    font-size: 10px;
    line-height: 1.3;
    letter-spacing: 0.02em;
}

/* Keep exit cards short without letting a long historical name move the time. */
.exit-card-header {
    display: flex;
    align-items: center;
    gap: 3px;
    min-width: 0;
}
.exit-instrument { min-width: 0; }
.exit-action, .exit-time { flex-shrink: 0; white-space: nowrap; }
.exit-time { margin-left: auto; font-size: 9px; }
/* 3.94.0: close reason (TTP / S / M ...) right of the time, in the footer's tiny text */
.exit-reason { flex-shrink: 0; white-space: nowrap; margin-left: 4px; }
.exit-card-header .exit-strategy {
    display: block;
    min-width: 0;
    max-width: 36%;
    flex-shrink: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* 3.84.1: the footer reads Margin / Size / Exit Px. A readout never wraps inside
   itself (that made card height depend on the digits). In the three-column
   tablet range the ~211 px footer cannot hold all three on one line, so the
   margin takes its own line above the 3.80.3 Size / Exit Px row on every card. */
.exit-card-footer > span { white-space: nowrap; }
@media (min-width: 768px) and (max-width: 1023.98px) {
    .exit-card-footer { flex-wrap: wrap; }
    .exit-card-footer .exit-margin { flex-basis: 100%; }
}
/* 2026-10-06 (the Mac browser job): a narrow card's footer reads one pixel smaller (enlarged text at 1050 px, or the
   Mac's own monospace font before the webfont loaded, ran its one line past the card; 3.242.0: the text font). Every card of a grid has the
   same width, so every card switches together and the heights stay equal. */
.edge-card:has(> .exit-card-footer) { container-type: inline-size; }
@container (max-width: 299.98px) {
    .exit-card-footer { font-size: 9px; }
}
/* ... and in the narrowest cards (the 768 px tablet columns) the strategy name may give way too, shortened with an
   ellipsis (its full name the title), so the header never runs past the card; wider cards keep it whole */
@container (max-width: 239.98px) {
    .exit-card-header .exit-strategy { flex-shrink: 1; }
}

/* Status Indicators & Radar Pulses */
.status-pill {
    background: rgba(15, 23, 42, 0.8);
    border: 1px solid var(--border-card);
    border-radius: 9999px;
    padding: 4px 12px;
    font-size: 0.8rem;
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

.pulse-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    position: relative;
}

.pulse-dot.running {
    background-color: var(--bull);
    box-shadow: 0 0 8px var(--bull);
}

.pulse-dot.running::after {
    content: '';
    position: absolute;
    top: -4px;
    left: -4px;
    right: -4px;
    bottom: -4px;
    border-radius: 50%;
    border: 2px solid var(--bull);
    animation: radarRipple 1.8s cubic-bezier(0.24, 0, 0.38, 1) infinite;
}

.pulse-dot.stopped {
    background-color: var(--text-dim);
}

/* 2026-09-30 (operator's choice): the header pill names the delivery. ADVANCED DELIVERY ACTIVE while the page streams
   BloFin's marks, in the Gemini model name's blue (--accent) with a soft glow and a blue dot; STANDARD DELIVERY in plain
   grey on the page's REST reads alone. The dot keeps the REST health (red when the last answer is old). */
#feedPill .feed-label { color: var(--text-muted); }
#feedPill .feed-label-short { display: none; }
#feedPill[data-delivery="advanced"] {
    border-color: var(--accent);
    box-shadow: 0 0 10px var(--accent-glow), inset 0 0 6px rgba(56, 189, 248, 0.12);
}
#feedPill[data-delivery="advanced"] .feed-label { color: var(--accent); }
#feedPill[data-delivery="advanced"] .pulse-dot.running { background-color: var(--accent); box-shadow: 0 0 8px var(--accent); }
#feedPill[data-delivery="advanced"] .pulse-dot.running::after { border-color: var(--accent); }
@media (max-width: 480px) {
    /* 3.206.2 (operator): the agent line on a phone is "Live AI Agent" and the model's name; no bar, no tagline */
    #userAgentLine .agent-blurb { display: none; }
}

/* 3.208.0 (operator): the MANUAL pill crossfades to CAPTURE for 5 s of every 20 on the wall clock (terminal.js
   takeProfitFace sets body.tp-locked); reduced motion swaps without the fade */
.manual-pill .tp-face { white-space: nowrap; transition: opacity 0.45s ease; }
.manual-pill .tp-face-locked { opacity: 0; }
body.tp-locked .manual-pill .tp-face-word { opacity: 0; }
body.tp-locked .manual-pill .tp-face-locked { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .manual-pill .tp-face { transition: none; } }

/* 3.115.0: the feed pill's dot when the page's last server answer is older than 15 s */
.pulse-dot.stale {
    background-color: var(--bear);
    box-shadow: 0 0 8px var(--bear);
}

@keyframes radarRipple {
    0% { transform: scale(0.6); opacity: 1; }
    100% { transform: scale(2.2); opacity: 0; }
}

/* Navigation Tabs */
.nav-tabs {
    display: flex;
    gap: 4px;
    background: rgba(15, 23, 42, 0.6);
    padding: 4px;
    border-radius: var(--radius-md);
    border: 1px solid var(--border-subtle);
}

.nav-tab-btn {
    background: transparent;
    border: none;
    color: var(--text-secondary);
    padding: 6px 14px;
    border-radius: var(--radius-sm);
    font-size: 0.825rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.18s ease;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.nav-tab-btn:hover {
    color: var(--text-primary);
    background: rgba(255, 255, 255, 0.05);
}

.nav-tab-btn.active {
    color: #ffffff;
    background: rgba(56, 189, 248, 0.18);
    border: 1px solid rgba(56, 189, 248, 0.35);
    box-shadow: 0 0 10px rgba(56, 189, 248, 0.2);
}

/* Dynamic Number Animations */
.slide-num {
    display: inline-block;
    transition: transform 260ms cubic-bezier(.2, .8, .2, 1), color 260ms ease, opacity 260ms ease;
    will-change: transform;
}

.slide-num.bump {
    transform: translateY(-5px);
    opacity: 0.6;
}

.slide-num.bump-down {
    transform: translateY(5px);
    opacity: 0.6;
}

/* Bull / Bear Text Classes */
.text-bull { color: var(--bull) !important; }
.text-bear { color: var(--bear) !important; }
.text-warn { color: var(--warn) !important; }
.text-accent { color: var(--accent) !important; }
.text-muted { color: var(--text-muted) !important; }

/* Buttons */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    font-weight: 600;
    font-size: 0.85rem;
    padding: 8px 16px;
    border-radius: var(--radius-md);
    border: 1px solid transparent;
    cursor: pointer;
    transition: all 0.18s cubic-bezier(0.4, 0, 0.2, 1);
}

/* A button with the hidden attribute stays hidden: .btn's display would beat the browser's [hidden] rule (2026-09-25:
   "Trade on this device" showed on the device that holds the seat). */
.btn[hidden] { display: none; }

.btn:active {
    transform: scale(0.97);
}

.btn-primary {
    background: linear-gradient(135deg, #0284c7 0%, #0369a1 100%);
    color: #ffffff;
    border-color: rgba(56, 189, 248, 0.4);
    box-shadow: 0 2px 10px rgba(2, 132, 199, 0.35);
}

.btn-primary:hover {
    background: linear-gradient(135deg, #0369a1 0%, #0284c7 100%);
    box-shadow: 0 4px 16px rgba(56, 189, 248, 0.45);
    border-color: rgba(56, 189, 248, 0.6);
}

.btn-subtle {
    background: rgba(255, 255, 255, 0.06);
    color: var(--text-secondary);
    border-color: var(--border-subtle);
}

.btn-subtle:hover {
    background: rgba(255, 255, 255, 0.12);
    color: var(--text-primary);
    border-color: var(--border-card);
}

.btn-sm {
    padding: 4px 10px;
    font-size: 0.75rem;
    border-radius: var(--radius-sm);
}

/* MTM Meters */
.progress-bar-bg {
    background: rgba(255, 255, 255, 0.08);
    border-radius: 9999px;
    height: 6px;
    overflow: hidden;
    position: relative;
}

.progress-bar-fill {
    height: 100%;
    border-radius: 9999px;
    transition: width 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* Chart Canvas Container (3.92.0: taller on desktop — the height comes out of the
   KPI strip above it, not the panels below) */
.chart-container {
    position: relative;
    width: 100%;
    height: 330px;
    border-radius: var(--radius-md);
    overflow: hidden;
}
@media (min-width: 1024px) {
    .chart-container { height: 470px; }
}

/* 3.92.0 OHLC legend — top-left overlay, pointer-transparent */
.chart-legend {
    position: absolute;
    top: 8px;
    left: 10px;
    z-index: 5;
    pointer-events: none;
    font-size: 11px;
    line-height: 1.5;
    color: var(--text-muted);
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
    max-width: calc(100% - 80px);
}
.chart-legend[hidden] { display: none; }
.chart-legend .legend-row { display: flex; flex-wrap: wrap; gap: 0 6px; align-items: baseline; }
.chart-legend .legend-sym { color: var(--text-primary); font-weight: 700; }
.chart-legend .legend-tf { color: var(--text-dim); margin-right: 4px; }
.chart-legend .legend-k { color: var(--text-dim); }
.chart-legend .legend-up { color: #26a69a; }
.chart-legend .legend-down { color: #ef5350; }
.chart-legend .legend-vol { color: var(--text-secondary); }
.chart-legend .legend-fills { margin-top: 1px; font-size: 10px; }
.chart-legend .legend-sep { color: var(--text-dim); margin: 0 5px; }

/* 3.116.1 symbol watermark — the coin in big letters, BloBrowser under it, centred and faint,
   above the canvases but pointer-transparent (the library's multi-line primitive misstacks). */
.chart-watermark {
    position: absolute;
    inset: 0;
    z-index: 4;
    pointer-events: none;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    user-select: none;
    color: rgba(148, 163, 184, 0.10);
    font-weight: 800;
    letter-spacing: 0.02em;
    line-height: 1;
}
.chart-watermark span:first-child { font-size: 92px; }
.chart-watermark span:last-child { font-size: 26px; margin-top: 6px; letter-spacing: 0.08em; }
@media (max-width: 640px) {
    .chart-watermark span:first-child { font-size: 56px; }
    .chart-watermark span:last-child { font-size: 18px; }
}

/* 3.116.3 marker key — in the axes' corner: the price-scale column below the last label, down
   through the time axis. A right-aligned column (Long / Short / Avg), pointer-transparent.
   chart.js sets the width to the price scale and the height to the time axis plus a little of the
   column above it. */
.chart-keys {
    position: absolute;
    right: 0;
    bottom: 0;
    z-index: 5;
    pointer-events: none;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: flex-end;
    gap: 4px;
    font-size: 10px;
    line-height: 1;
    color: var(--text-muted);
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
    padding: 0 14px 12px 3px;
    overflow: hidden;
    white-space: nowrap;
    /* 3.126.7: the key's box is painted in the chart's own background (a short fade at its top edge) so a
       price label that lands in the axes' corner disappears behind the key instead of printing through it.
       The library draws its price labels all the way down through the volume margin, and which label lands
       in the corner depends on the day's price range, so without this the overlap came and went with the
       market (seen 2026-09-22 on BTC-USDT 5m). */
    background: linear-gradient(to bottom, rgba(11, 15, 25, 0) 0, #0b0f19 10px);
}
.chart-keys[hidden] { display: none; }
.chart-keys > span { display: inline-flex; align-items: center; pointer-events: auto; }
.chart-keys .key-swatch { display: inline-block; width: 8px; height: 8px; margin-right: 3px; }
.chart-keys .key-long { background: #10b981; border-radius: 2px; }
.chart-keys .key-short { background: #f43f5e; border-radius: 2px; }
/* 2026-10-05 (operator): Long and Short are tiny arrows, the chart's own markers (up green, down red), on every page */
.chart-keys .key-long,
.chart-keys .key-short { width: 8px; height: 10px; border-radius: 0; }
.chart-keys .key-long { clip-path: polygon(50% 0, 100% 50%, 68% 50%, 68% 100%, 32% 100%, 32% 50%, 0 50%); }
.chart-keys .key-short { clip-path: polygon(32% 0, 68% 0, 68% 50%, 100% 50%, 50% 100%, 0 50%, 32% 50%); }
.chart-keys .key-avg { height: 0; border-top: 2px dashed #22d3ee; }

/* 2026-10-05 (operator): the position lines' labels on the chart's LEFT edge (chart.js _drawLabels), each blinking
   while the last price is within 0.3% of its line */
.pos-labels { position: absolute; inset: 0; pointer-events: none; z-index: 3; overflow: hidden; }
/* 2026-10-06 (operator): the chart rotation's switch fades out and in (chart.js showSymbol, FADE_MS), never a jump;
   a person holding the chart sees a faint bar on its top edge run down for the minute (terminal.js
   pauseChartRotation). 2026-10-07 (operator: smooth in Firefox, a jagged flash in Chrome): the chart itself is never
   faded (Chrome moved its canvases to a layer of their own as an opacity fade began and ended, and flashed); a cover in
   the chart's own background colour (chart.js layout #0b0f19) fades in over it, the coin changes under it, and it fades
   away again. The cover keeps its own layer for good (will-change), so nothing is promoted mid-fade */
.chart-veil {
    position: absolute; inset: 0; z-index: 5; pointer-events: none;
    background: #0b0f19; opacity: 0; transition: opacity 0.45s ease-in-out; will-change: opacity;
}
.chart-veil.on { opacity: 1; }
/* 2026-10-07 (operator: Chrome still flashed): no reduced-motion exception for the cover. A crossfade is not motion,
   and with Windows' "animation effects" off (a wall-display PC, often) the exception turned every switch into the
   hard flash the fade exists to prevent; the spinner below keeps its exception */
/* 2026-10-07 (operator): the open position whose coin the chart shows lights as a hovered row does */
#positionsTableBody > tr.chart-shown { background-color: rgba(255, 255, 255, 0.03); }
.chart-rotate-pause {
    position: absolute; top: 0; left: 0; right: 0; height: 2px; z-index: 4; pointer-events: none;
    background: rgba(148, 163, 184, 0.28); transform-origin: left center; transform: scaleX(0);
}
.chart-rotate-pause.running { animation-name: chart-rotate-pause; animation-timing-function: linear; animation-fill-mode: forwards; }
@keyframes chart-rotate-pause { from { transform: scaleX(1); } to { transform: scaleX(0); } }
.pos-labels[hidden] { display: none; }
.pos-label {
    position: absolute;
    left: 6px;
    top: 0;
    height: 15px;
    padding: 0 5px;
    border-radius: 3px;
    color: #0b0f14;
    font: 600 10px/15px var(--font-text);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.pos-label.near { animation: pos-label-blink 0.9s ease-in-out infinite; } /* the delay (chart.js) locks the phase */
@keyframes pos-label-blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.2; } }

/* 3.92.0 fullscreen chart: the panel covers the viewport; Esc or the button exits */
body.chart-fs { overflow: hidden; }
body.chart-fs #chartPanel {
    position: fixed;
    inset: 0;
    z-index: 80;
    border-radius: 0;
    max-width: none;
    margin: 0;
}
body.chart-fs #chartPanel .chart-container { height: calc(100vh - 92px); flex: 1 1 auto; min-height: 0; }

/* Mobile / tablet (< 1024px): keep the expand / un-expand button (#chartFullscreen) at the top-right corner
   of the chart box (row 1) when the timeframe toolbar wraps onto row 2. */
@media (max-width: 1023.98px) {
    .chart-header { position: relative; }
    .chart-symbol-group { width: 100%; padding-right: 2.25rem; }
    .chart-toolbar { width: 100%; }
    #chartFullscreen {
        position: absolute;
        top: 0;
        right: 0;
    }
}

/* Mobile touch-hold lock overlay: light wash in the middle of the chart with large semi-translucent
   3, 2, 1 countdown numbers; unlocks chart pan/zoom only when the finger stays on the chart past 1. */
.chart-touch-lock {
    position: absolute;
    inset: 0;
    z-index: 6;
    pointer-events: none;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(248, 250, 252, 0.08);
    user-select: none;
    -webkit-user-select: none;
}
.chart-touch-lock[hidden] { display: none; }
.chart-touch-count {
    font-size: 88px;
    font-weight: 800;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    color: rgba(248, 250, 252, 0.45);
    text-shadow: 0 2px 16px rgba(0, 0, 0, 0.45);
}

.timeframe-pill {
    background: transparent;
    border: none;
    color: var(--text-muted);
    font-size: 0.725rem;
    font-weight: 700;
    padding: 2px 8px;
    border-radius: 4px;
    cursor: pointer;
    transition: all 0.15s ease;
}

.timeframe-pill:hover {
    color: var(--text-primary);
    background: rgba(255, 255, 255, 0.08);
}

.timeframe-pill.active {
    color: var(--accent);
    background: rgba(56, 189, 248, 0.15);
}

/* 3.120.2 (operator): the performance panel's By-day list shows four rows, the rest scroll behind the
   same thin scrollbar as the radar, so the three breakdown blocks sit at one height. Four rows =
   4 x the 10px line (15px) + 3 x the 4px row gap. */
.perf-days { max-height: 72px; }

/* 3.126.3 (operator): the KPI stat cards keep their type sizes but lose the spare vertical space: 5px
   padding top and bottom (Tailwind's p-2 gives 8), the 18px amount on a 1.2 line instead of the utility's
   1.75rem, the 10px label and caption on 1.3 instead of 1.5, and the margin meter box sized to the new
   caption line so it stays level with the neighbours' captions. 76px -> 60px per tile. */
.kpi-tile { padding-top: 5px; padding-bottom: 5px; }
.kpi-tile .text-lg { line-height: 1.2; }
.kpi-tile .text-\[10px\] { line-height: 1.3; }
.kpi-tile .h-\[15px\] { height: 13px; }
/* 2026-10-06 (operator): the two realized tiles' names (Daily Realized PnL (D), Monthly Realized PnL (M)) read at the
   other tiles' size and stay on one line on a desktop: only a tile too narrow for them sets them a little smaller (down
   to 9 px, by the tile's own width). On a tablet or a phone they wrap to a second line, never cut short */
@media (min-width: 1024px) {
    .kpi-tile:has(> .kpi-label-long) { container-type: inline-size; }
    .kpi-tile .kpi-label-long { white-space: nowrap; font-size: clamp(9px, calc(100cqw / 16), 10px); }
}

/* 3.126.4 (operator): the vertical space between the page's stacked blocks halved. Header to status bar
   (the page container's padding) 8px -> 4px; status bar -> KPI strip -> panel grid (space-y-2.5) 10px -> 5px;
   the rows of the panel grid (chart, agent, radar, positions, exits, performance, intelligence) 10px -> 5px.
   3.126.5 (operator): the side-to-side gaps match: the KPI tiles, the seam between the chart and the side
   panels, and the card grids (recent exits, performance, intelligence) are 5px too. The KPI strip sits on the
   same twelve-column grid as the panels below it at desktop width (each tile spans two columns), so the fourth
   tile ends exactly where the chart ends and the last two tiles line up with the side panels. */
main.page-main { padding-top: 4px; padding-bottom: 4px; }
/* 3.126.6 (operator): the header bar's padding above and below its row halved, 10px -> 5px (the py-2.5 utility). */
header.top-header { padding-top: 5px; padding-bottom: 5px; }
.page-stack > :not([hidden]) ~ :not([hidden]) { margin-top: 5px; }
.page-grid, .card-grid, .kpi-strip { gap: 5px; }
@media (min-width: 1024px) {
    .kpi-strip { grid-template-columns: repeat(12, minmax(0, 1fr)); }
    .kpi-strip > .kpi-tile { grid-column: span 2 / span 2; }
}

/* 3.134.0 (operator request): the Journal tab as cards in the Recent Exits style, in their own scroller. Entries are
   cyan-edged, exits green or red by their result, rejected orders dimmed. The AI rationale sits small at the bottom,
   three lines until the card is clicked. */
.journal-scroll { max-height: calc(100vh - 230px); min-height: 240px; overflow-y: auto; overflow-x: hidden; padding-right: 4px; }
.journal-card { cursor: pointer; min-width: 0; }
/* 3.134.2 (operator: fix it as designed): the thick coloured left edge of the Recent Exits and Journal cards. The panel's
   own 1px border (.glass-panel-subtle) loads after the utilities and had been cancelling border-l-4 and its colour, so
   exits never showed green or red. Two classes outrank it. */
.glass-panel-subtle.edge-card { border-left-width: 4px; border-left-style: solid; }
.glass-panel-subtle.edge-card.edge-win, .glass-panel-subtle.edge-card.edge-exit-win { border-left-color: #10b981; }
.glass-panel-subtle.edge-card.edge-loss, .glass-panel-subtle.edge-card.edge-exit-loss { border-left-color: #f43f5e; }
.journal-rationale { font-size: 9px; line-height: 1.35; color: var(--text-muted); border-top: 1px solid var(--border-subtle);
    padding-top: 4px; white-space: normal; overflow-wrap: anywhere; display: -webkit-box; -webkit-box-orient: vertical;
    -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; }
.journal-card.expanded .journal-rationale { display: block; -webkit-line-clamp: unset; line-clamp: unset; }

/* 3.137.0: the Account tab's person icon, feature buttons and the inline Exchange (Connect) panel */
.tab-person { display: inline-block; vertical-align: -3px; }   /* 3.138.0: 16px (was 12) */
.account-rule { display: flex; flex-direction: column; gap: 2px; }
.account-rule b { color: var(--text-primary); font-size: 13px; }
.account-rule small { color: var(--text-muted); font-size: 10px; line-height: 1.35; }
/* 3.139.0: the buttons are a column on the left, the view on the right (stacked on a phone) */
.account-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; align-items: start; }
.account-tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; }
@media (min-width: 768px) {
    .account-layout { grid-template-columns: 220px minmax(0, 1fr); }
    .account-tiles { grid-template-columns: minmax(0, 1fr); }
}
.account-tile { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; text-align: left; padding: 10px 12px;
    border-radius: var(--radius-md); background: rgba(14, 22, 38, 0.55); border: 1px solid var(--border-subtle);
    color: var(--text-primary); font-size: 12px; font-weight: 700; cursor: pointer; transition: border-color .15s, background .15s; }
.account-tile small { font-size: 10px; font-weight: 400; color: var(--text-muted); line-height: 1.3; }
.account-tile:hover { border-color: rgba(56, 189, 248, 0.35); }
.account-tile.active { border-color: var(--accent); background: rgba(56, 189, 248, 0.08); }
/* 3.184.0: Account > Credits (the balance, the packs, the Buy panel with the server's Solana Pay QR, the ledger) */
.credits-balance { display: flex; align-items: baseline; gap: 8px; }
.credits-number { font-size: 34px; font-weight: 800; color: var(--text-primary); line-height: 1; letter-spacing: -0.5px; }
.credits-number.is-negative { color: var(--bear); }
.credits-unit { font-size: 12px; color: var(--text-muted); }
.credits-warn { font-size: 11px; color: var(--warn); margin: 0; }
.credits-out { font-size: 11px; color: var(--bear); margin: 0; font-weight: 700; }
.credits-packs { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
@media (max-width: 480px) { .credits-packs { grid-template-columns: minmax(0, 1fr); } }
.credits-pack { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; padding: 8px 10px; text-align: left;
    border-radius: var(--radius-md); background: rgba(14, 22, 38, 0.55); border: 1px solid var(--border-subtle);
    color: var(--text-primary); font-size: 12px; font-weight: 700; cursor: pointer; transition: border-color .15s, background .15s; }
.credits-pack small { font-size: 10px; font-weight: 400; color: var(--text-muted); }
.credits-pack:hover { border-color: rgba(56, 189, 248, 0.35); }
.credits-pack:disabled { opacity: .6; cursor: default; }
.credits-buy { border: 1px solid var(--border-subtle); border-radius: var(--radius-md); padding: 10px; display: flex; flex-direction: column; gap: 8px; background: rgba(14, 22, 38, 0.35); }
.credits-buy-head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.credits-timer { font-size: 11px; color: var(--text-muted); }
.credits-buy-body { display: flex; gap: 12px; flex-wrap: wrap; align-items: flex-start; }
.credits-qr { width: 168px; height: 168px; flex: none; background: #fff; border-radius: 6px; padding: 6px; box-sizing: border-box; }
.credits-qr svg { width: 100%; height: 100%; display: block; }
/* 2026-10-03 (operator): no white frame before a code is in it; the code (a viewBox SVG) fills and centres the frame */
.credits-qr:empty { display: none; }
.credits-qr { display: flex; align-items: center; justify-content: center; }
.credits-buy-text { flex: 1 1 180px; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.credits-buy-text p { margin: 0; }
.credits-actions { display: flex; gap: 6px; flex-wrap: wrap; }
.credits-copy-note { overflow-wrap: anywhere; }
.credits-status { color: var(--text-secondary); }
.credits-status[data-state="paid"] { color: var(--bull); font-weight: 700; }
.credits-status[data-state="expired"], .credits-status[data-state="failed"] { color: var(--warn); font-weight: 700; }
.credits-list { list-style: none; margin: 4px 0 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.credits-list li { display: flex; justify-content: space-between; gap: 8px; font-size: 11px; color: var(--text-secondary); border-bottom: 1px solid var(--border-subtle); padding: 3px 0; }
.credits-list li span { min-width: 0; overflow-wrap: anywhere; }
.credits-list li time { color: var(--text-dim); flex: none; font-size: 10px; }
.connect-panel.connect-inline { position: static; width: auto; max-width: 560px; box-shadow: none; z-index: auto;
    background: #0e1625; backdrop-filter: none; -webkit-backdrop-filter: none; }

/* Custom Scrollbars */
::-webkit-scrollbar {
    width: 6px;
    height: 6px;
}

::-webkit-scrollbar-track {
    background: transparent;
}

::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.15);
    border-radius: 9999px;
}

::-webkit-scrollbar-thumb:hover {
    background: rgba(255, 255, 255, 0.28);
}

/* Filter Chips & Inputs */
.search-input {
    background: rgba(14, 22, 38, 0.8);
    border: 1px solid var(--border-card);
    color: var(--text-primary);
    padding: 6px 12px;
    border-radius: var(--radius-sm);
    font-size: 0.8rem;
    outline: none;
    transition: all 0.2s ease;
}

.search-input:focus {
    border-color: var(--accent);
    box-shadow: 0 0 8px var(--accent-glow);
}

/* 2026-09-29 (operator request): a small clear x inside the Trades search box's right side, shown while it holds text */
.search-wrap {
    position: relative;
    display: inline-flex;
    align-items: center;
}
.search-input.search-input-clearable {
    padding-right: 26px;
}
.search-clear {
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
    width: 16px;
    height: 16px;
    padding: 0;
    border: 0;
    border-radius: 9999px;
    background: transparent;
    color: var(--text-muted);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: color 0.15s ease, background 0.15s ease;
}
.search-clear:hover,
.search-clear:focus-visible {
    color: var(--text-primary);
    background: rgba(255, 255, 255, 0.1);
    outline: none;
}
.search-clear[hidden] {
    display: none;
}

/* 3.84.3: a <select> sizes itself from its LONGEST option, and the chart symbol
   list is rebuilt from the radar universe + open positions every poll, so one
   long instrument name (or an open-position suffix) widened it from ~95 px to
   ~240 px and pushed the timeframe / Candles toolbar under the price at the
   1050 px screenshot width. Fixed width: the selected label clips, the
   pop-up list still shows every name in full. The header itself is nowrap
   from lg up (index.html): the toolbar is shrink-0, the symbol group is
   min-w-0, so a visible RECONNECTING badge drops under the price instead of
   pushing the toolbar down. Phone widths use 16 px input text (iOS zoom
   rule below) and wrap the toolbar anyway, so they get more. */
#symbolSelector {
    width: 8.5rem;
    /* 3.206.3 (operator, Firefox: the page below the chart moved 1 px and the scrollbar lit on every rotation to a
       coin without a position): Firefox sizes a select from the SELECTED option's glyphs, and a held coin's option
       starts with the lightning (terminal.js updateSymbolSelector), a fallback-font glyph 2 px taller than the
       mono text (27 px against 25); Chromium sizes it from the font. One height, so no coin moves the page */
    height: 1.75rem;
    flex-shrink: 0;
    text-overflow: ellipsis;
}
@media (max-width: 767px) {
    /* 3.207.7 (operator, iPhone): the desktop's 28 px clipped the coin's name at its foot: a phone's select text is
       16 px (the iOS zoom rule below). 3.207.9 (operator, iPhone): sized by the browser, the select grew again with
       the lightning glyph of a held coin's option and the page below the chart moved on every rotation, as Firefox
       did before 3.206.3; so one fixed height here too, tall enough for the 16 px text and the glyph */
    #symbolSelector { width: 9.5rem; height: 2.5rem; }
}

/* AI stream cards: truncate the rationale at WHOLE-line boundaries.
   The cards previously leaned on Tailwind's line-clamp-2 utility, which was
   not taking effect in the served bundle — rationales overflowed their box
   and the third line rendered sliced in half horizontally (reported
   2026-08-29). Defined here so clamping never depends on the CDN bundle. */
.stream-clamp-3 {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
/* 2026-10-02 (operator): an AI card (MARKET EVAL, STATUS UPDATE) opens to its whole text when clicked, in place, and
   closes again on the next click */
.ai-expandable { cursor: pointer; }
.ai-card.ai-expanded .stream-clamp-3 {
    display: block;
    -webkit-line-clamp: unset;
    line-clamp: unset;
    overflow: visible;
}


/* Mobile hardening (3.23.2, iPhone 375-430px / Samsung 360-412px):
   iOS Safari ignores overflow-x on body alone — the page pans sideways under
   a thumb if ANY element overflows. Clamp at the html level too, and kill
   horizontal overscroll so vertical scrolling can't drift left. */
html {
    /* iOS Safari inflates text it deems primary ("text size adjustment"),
       rendering rows ~1.2-1.3x wider than every desktop emulation — the
       reason the layout fit in Chromium at 390px while a real iPhone panned
       sideways. Pin text to authored size; the layout below is additionally
       wrap-tolerant so OS-level accessibility text scaling still lays out. */
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}

html,
body {
    /* clip > hidden: hidden still creates a scroll box iOS can rubber-band a
       few px into ("goes off the right and left a bit"); clip forbids any
       horizontal scrolling of the page itself. Inner .overflow-x-auto
       wrappers (positions table) keep their own scrolling. hidden first as
       fallback for pre-2022 browsers. */
    overflow-x: hidden;
    overflow-x: clip;
    overscroll-behavior-x: none;
    max-width: 100%;
}

body {
    /* viewport-fit=cover extends the page under notches/curved edges —
       without safe-area padding, content sits a hair off both screen edges
       on iPhones and curved Samsungs. */
    padding-left: env(safe-area-inset-left, 0px);
    padding-right: env(safe-area-inset-right, 0px);
}

img, svg, video {
    max-width: 100%;
}


/* Structural containment: even if a future child renders wide, it clips at
   its section instead of widening the page. Inner .overflow-x-auto wrappers
   keep their own scrolling. */
main, header, footer, section {
    overflow-x: clip;
    max-width: 100%;
}
.chart-container {
    max-width: 100%;
}


/* iOS input auto-zoom kill (3.23.x mobile saga, the ACTUAL culprit):
   focusing any input/select under 16px makes iOS Safari zoom the page
   ~1.2-1.5x and the zoom PERSISTS for the whole session — the old (since
   retired) sign-in field was 14px WITH autofocus, so every phone visit began
   zoomed-in and horizontally pannable regardless of layout. 16px inputs
   on touch-size screens (Connect's key fields still need it); desktop
   typography untouched. */
@media (max-width: 767px) {
    input, select, textarea {
        font-size: 16px !important;
    }
}

/* ── Key panel (3.52.0 styles): the user page's own key form (#userKeyPanel) ───────── */
.connect-panel {
    position: absolute; right: 0; top: calc(100% + 8px); z-index: 60;
    width: 380px; max-width: calc(100vw - 24px);
    background: var(--drift-surface) var(--drift) 50% / 200% 100% var(--drift-attach); background-color: var(--bg-card); border: 1px solid var(--border-card); /* 3.246.0: BloHunter's surface */
    border-radius: var(--radius-lg); box-shadow: 0 24px 60px rgba(0, 0, 0, 0.55);
    padding: 14px; font-size: 12px; color: var(--text-secondary);
}
.connect-panel h4 { margin: 0; font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-muted); font-family: var(--font-text); }
.connect-panel .cx-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
.connect-panel label { display: block; font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--text-muted); margin: 8px 0 3px; font-family: var(--font-text); }
.connect-panel input[type="text"], .connect-panel input[type="password"], .connect-panel input[type="number"], .connect-panel select {
    width: 100%; background: rgba(14, 22, 38, 0.9); border: 1px solid var(--border-card); color: var(--text-primary);
    padding: 7px 10px; border-radius: var(--radius-sm); font-size: 16px; font-family: var(--font-code); outline: none; /* a raw key: code-like text */
}
@media (min-width: 640px) { .connect-panel input[type="text"], .connect-panel input[type="password"], .connect-panel input[type="number"], .connect-panel select { font-size: 12px; } }
.connect-panel input:focus, .connect-panel select:focus { border-color: var(--accent); box-shadow: 0 0 8px var(--accent-glow); }
.connect-panel .cx-check { display: flex; align-items: center; gap: 8px; margin: 8px 0 0; font-size: 12px; color: var(--text-secondary); text-transform: none; letter-spacing: 0; font-family: inherit; }
.connect-panel .cx-check input { width: auto; }
.connect-panel .cx-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.connect-panel .cx-row { display: flex; justify-content: space-between; gap: 10px; padding: 5px 0; border-bottom: 1px dashed var(--border-subtle); }
.connect-panel .cx-row:last-child { border-bottom: 0; }
.connect-panel .cx-v { font-family: var(--font-text); font-variant-numeric: tabular-nums; color: var(--text-primary); }
.connect-panel .cx-warn { color: var(--warn); }
.connect-panel .cx-note { color: var(--text-muted); font-size: 11px; line-height: 1.45; margin-top: 8px; }
.connect-panel .cx-note.cx-warn { color: var(--warn); }
.connect-panel .cx-error { background: var(--bear-bg); border: 1px solid var(--bear-border); color: #fda4af; padding: 6px 9px; border-radius: var(--radius-sm); margin-top: 8px; font-size: 11px; }
.connect-panel .cx-log { max-height: 180px; overflow-y: auto; margin-top: 8px; border-top: 1px solid var(--border-subtle); padding-top: 6px; }
.connect-panel .cx-logline { font-family: var(--font-text); font-variant-numeric: tabular-nums; font-size: 10.5px; line-height: 1.4; padding: 2px 0; color: var(--text-secondary); word-break: break-word; }
.connect-panel .cx-time { color: var(--text-dim); margin-right: 4px; }

/* 3.64.0 auto-refresh on deploy (version_watch.js): full-page "Updating" overlay
   shown from stamp mismatch until the confirmed reload. */
.bb-update-overlay { position: fixed; inset: 0; z-index: 999998; background: rgba(0, 0, 0, 0.72); backdrop-filter: blur(4px); display: flex; align-items: center; justify-content: center; }
.bb-update-card { background: rgba(20, 22, 28, 0.96); border: 1px solid var(--border-subtle, #2a2f3a); border-radius: 14px; padding: 32px 44px; text-align: center; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5); }
.bb-update-spinner { font-size: 44px; line-height: 1; color: #60a5fa; animation: bb-spin 1s linear infinite; margin-bottom: 12px; }
.bb-update-title { color: #fff; font-size: 17px; font-weight: 600; margin-bottom: 6px; }
.bb-update-msg { color: #9aa3b2; font-size: 13px; }
@keyframes bb-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .bb-update-spinner { animation: none; } }
/* The marks beside the contract (2026-09-25, operator order): strategy adds are unlimited, so their count is a small
   yellow number to the left of the dots (hidden at 0), never dots; the red dots are the DCAs, the light-blue ones the DLs. */
.signal-add-dots {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    flex-shrink: 0;
}
.strategy-add-count {
    color: #fbbf24;
    font-size: 9px;
    font-weight: 700;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0;
}
.signal-add-dot {
    width: 4px;
    height: 4px;
    border-radius: 50%;
}
.signal-add-dot.dca { background: #f43f5e; }   /* 3.129.0: the emergency DCA, one per fill */
/* 3.129.1: arming (within the warning band above the DCA level, not filled yet): blinks; filled: solid */
@keyframes dca-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0.15; } }
.signal-add-dot.dca.arming { animation: dca-blink 0.9s steps(1, end) infinite; }
/* 3.201.0 (operator order 2026-10-07, docs/DL_AND_DCA.md): a de-leverage (DL1, DL2: the position doubles and the
   leverage steps down) is a LIGHT-BLUE dot, solid once done, blinking inside its warning band; the chart's DL line is
   the same colour (chart.js POSITION_LINE_COLORS.dl) */
.signal-add-dot.dl { background: #7dd3fc; }
.signal-add-dot.dl.arming { animation: dca-blink 0.9s steps(1, end) infinite; }
/* 3.129.2: the Trailing Take Profit is within the band of the ROE: green, blinking, never solid */
.signal-add-dot.ttp { background: #10b981; }
.signal-add-dot.ttp.arming { animation: dca-blink 0.9s steps(1, end) infinite; }

/* 3.146.0 (operator request): Trades and Account start as close under the header as the Browser page. The page's
   section spacing (main's space-y) gave every tab after the first a 10px top margin the Browser tab never had. */
main.page-main > .tab-content-panel { margin-top: 0 !important; }

/* 3.146.0 style guide (docs/STYLE_GUIDE.md): PnL and uPnL keep their casing inside uppercase labels. */
.term-pnl { text-transform: none; }

/* 3.146.0: the sortable PnL column header of the open positions table */
.pos-sort-btn {
    background: none;
    border: 0;
    padding: 0;
    color: inherit;
    font: inherit;
    letter-spacing: inherit;
    text-transform: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 4px;
}
.pos-sort-btn:hover { color: var(--text-primary, #e2e8f0); }
.pos-sort-btn:focus-visible { outline: 1px solid rgba(56, 189, 248, 0.8); outline-offset: 2px; border-radius: 2px; }
.pos-sort-arrow { font-size: 8px; }

/* Phase 4 (user mode): the visitor preview (<html data-mode="preview">). Every panel shows sample figures from
   /api/preview (apps/dashboard/preview.py), blurred and inert; the banner and the Sign In tab stay sharp. */
html[data-mode='preview'] .kpi-strip,
html[data-mode='preview'] .page-grid,
html[data-mode='preview'] #tab-journal .glass-panel {
    filter: blur(6px);
    pointer-events: none;
    user-select: none;
    -webkit-user-select: none;
}
.preview-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 5px;
    padding: 8px 12px;
    border: 1px solid rgba(56, 189, 248, 0.35);
    border-radius: var(--radius-md);
    background: var(--drift-surface) var(--drift) 50% / 200% 100% var(--drift-attach); /* 3.246.0: BloHunter's surface */
    background-color: var(--bg-card);
    font-size: 12px;
    color: var(--text-secondary);
}
.preview-banner-text { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; min-width: 0; }
.preview-banner b { color: var(--text-primary); font-weight: 600; }
.preview-chip {
    font-family: var(--font-text);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.06em;
    color: var(--accent);
    border: 1px solid rgba(56, 189, 248, 0.45);
    border-radius: 9999px;
    padding: 1px 8px;
}
.preview-signin { white-space: nowrap; text-decoration: none; }
.sign-in-tab { color: var(--text-primary); text-decoration: none; border: 1px solid rgba(56, 189, 248, 0.35); }

/* Phase 3: the sign-in window over the page (signin.js); the frame comes from the sign-in origin */
.bb-signin-overlay {
  position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center;
  padding: 16px; background: rgba(3, 6, 12, 0.72); backdrop-filter: blur(3px);
}
/* the box is only a frame holder: Clerk's own card draws the window. The frame is 440 px (Clerk's 400 px card plus
   its 20 px margins, which Clerk takes from the frame's width) and its page is transparent, so only the card shows. */
.bb-signin-box {
  position: relative; width: min(440px, 100%); max-height: calc(100vh - 32px); display: flex; flex-direction: column;
}
.bb-signin-frame { width: 100%; height: 420px; border: 0; display: block; background: transparent; color-scheme: dark; }
.bb-signin-close {
  position: absolute; top: -15px; right: 5px; z-index: 1; width: 30px; height: 30px; border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.16); background: #0f141c; color: var(--text-secondary, #9aa7b4);
  font-size: 18px; line-height: 1; cursor: pointer; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
}
.bb-signin-close:hover, .bb-signin-close:focus-visible { color: var(--text-primary, #e6edf3); border-color: rgba(34, 211, 238, 0.6); }
.bb-signin-note { margin: 0; padding: 0 16px 12px; font-size: 12px; color: var(--text-secondary, #9aa7b4); text-align: center; }
.bb-signin-note:empty { display: none; }
/* 3.158.0: the same window as a full page (a browser that will not use a passkey inside a frame) */
.bb-signin-full {
  align-self: center; margin-top: 6px; padding: 4px 8px; font-size: 12px; color: var(--text-secondary, #9aa7b4);
  text-align: center; text-decoration: underline; text-underline-offset: 2px;
}
.bb-signin-full:hover, .bb-signin-full:focus-visible { color: var(--text-primary, #e6edf3); }
/* 2026-09-24: a "Sign out everywhere" the sign-in service did not confirm keeps saying so on the page it lands on,
   until the person dismisses it (signin.js; the Security view's own line is gone with the navigation) */
.bb-signout-notice {
  position: fixed; top: 16px; left: 50%; transform: translateX(-50%); z-index: 1001;
  width: min(560px, calc(100vw - 32px)); display: flex; align-items: flex-start; gap: 12px;
  padding: 12px 14px; border-radius: 10px; border: 1px solid rgba(251, 191, 36, 0.55);
  background: #151a22; color: var(--text-primary, #e6edf3); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.55);
  font-size: 13px; line-height: 1.45;
}
.bb-signout-notice-text { margin: 0; flex: 1; min-width: 0; overflow-wrap: anywhere; }
.bb-signout-notice-close {
  flex: none; padding: 4px 12px; border-radius: 6px; border: 1px solid rgba(255, 255, 255, 0.16);
  background: #0f141c; color: var(--text-primary, #e6edf3); font-size: 12px; cursor: pointer;
}
.bb-signout-notice-close:hover, .bb-signout-notice-close:focus-visible { border-color: rgba(34, 211, 238, 0.6); }

/* Phase 5 (user mode): a signed-in user's own page (<html data-mode="user">, user.js). The key banner reuses the preview
   banner's look; it and the one-line read error hide with the hidden attribute (the banner's display: flex would win). */
.user-banner[hidden], .user-read-error[hidden] { display: none; }
.user-read-error {
    margin-bottom: 5px; padding: 6px 12px; border: 1px solid var(--bear-border); border-radius: var(--radius-md);
    /* 3.242.0: the red tint laid over a solid card, never see-through over the starfield */
    /* 3.246.0: over the cards' drifting surface */
    background: linear-gradient(var(--bear-bg), var(--bear-bg)),
        var(--drift-surface) var(--drift) 50% / 200% 100% var(--drift-attach);
    background-color: var(--bg-card);
    color: #fda4af; font-size: 12px; overflow-wrap: anywhere;
}
/* 3.163.0: amber while one read fails or BloFin limits the key (the rest of the page is current); red only when balance
   and positions have had no good read for 60 s (user.js statusNow) */
.user-read-error[data-tone="warn"] {
    border-color: var(--warn-border);
    background: linear-gradient(rgba(245, 158, 11, 0.08), rgba(245, 158, 11, 0.08)),
        var(--drift-surface) var(--drift) 50% / 200% 100% var(--drift-attach);
    background-color: var(--bg-card);
    color: var(--warn);
}
/* 2026-10-03 (operator): the credit lines in the notification area share the status line's look; the warning's x */
.user-credits-line { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.user-credits-line[hidden] { display: none; }
.user-dismiss { background: none; border: 0; color: inherit; font-size: 16px; line-height: 1; cursor: pointer; padding: 0 4px; }
/* a position or close on a coin BloBrowser does not trade (operator decision 2026-09-24): shown, marked, not clickable */
/* 3.212.0 (operator): a hand position's white MANUAL pill among the row's pills (3.211.0's note line before); on the
   trading device it crossfades to CAPTURE with the TAKE PROFIT pill and opens the capture dialog on a click; the
   question is a small dialog over the page, Cancel focused (user.js confirmDialog) */
.manual-pill { background: rgba(248, 250, 252, 0.08); color: #f8fafc; border: 1px solid rgba(248, 250, 252, 0.6); }
/* 3.212.1 (operator, 2026-10-10: the MANUAL pill looked taller than SWING and "6 OPEN" than the pills; all measured
   17 px, the difference was the border): every pill and badge wears a 1 px border in its own text colour at 60%, as
   MANUAL does, so the edges read alike. The strategy pills by their title (terminal.js), the header badges and the
   AI's MARKET READ chip by .pill-ring; the colour follows the text, so a badge that changes colour (AI: LIVE AGENT,
   AI: OFFLINE) keeps a matching ring. A browser without color-mix keeps the text colour itself */
.position-pills .badge-chip[title^="STRATEGY:"], .pill-ring {
    border-width: 1px !important;
    border-style: solid !important;
    border-color: currentColor !important;
    border-color: color-mix(in srgb, currentColor 60%, transparent) !important;
}
/* ...and the header badges at the position pills' own size (they measured 23 to 25 px against the pills' 17: the
   operator's "6 OPEN is definitely thicker"): the compact pill's 10 px text, 1.3 line height and (3.237.0, the
   squared chip format of 3.235.0) 2 px padding */
.pill-ring {
    border-radius: 5px !important; /* 3.237.0: squared, a status pill wearing the ring too (AUTONOMOUS) */
    padding: 2px 6px !important;
    font-size: 10px !important;
    line-height: 1.3 !important;
    letter-spacing: 0.02em;
}
.manual-capture { cursor: pointer; }
/* 3.212.2 (operator: MANUAL had extra room either side, 7.8 px against the others' 5): the shared cell was sized for
   CAPTURE, the wider word. The word sizes the pill now, and CAPTURE lays over it, centred, reaching a little into the
   padding for its 5 s; the pill never changes width */
.manual-pill .tp-faces { position: relative; display: inline-block; }
.manual-pill .tp-face-locked { position: absolute; top: 0; left: 50%; transform: translateX(-50%); }
.manual-capture:hover, .manual-capture:focus-visible { background: rgba(248, 250, 252, 0.18); outline: none; }
.bb-dialog-wrap { position: fixed; inset: 0; z-index: 120; display: flex; align-items: center; justify-content: center; background: rgba(0, 0, 0, 0.55); padding: 16px; }
.bb-dialog { width: min(100%, 380px); padding: 16px; display: grid; gap: 12px; }
.bb-dialog-actions { display: flex; justify-content: flex-end; gap: 8px; }
.bb-dialog-no:focus-visible, .bb-dialog-yes:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.perf-coverage { font-size: 10px; color: var(--text-dim); margin: 0; }
.rules-caption { font-size: 11px; color: var(--text-muted); margin: 0 0 8px; }
.connect-panel .ux-log-head { margin: 8px 0 0; }
/* 3.165.0: the link in Account > Exchange's help while Blofin refuses this address (Tailwind's base takes links' underline) */
#userKeyPanel .cx-note a { color: inherit; text-decoration: underline; }
/* Phase 6 (3.161.0): where the person's bot trades and whether it may open (user.js renderBot): TRADING ON THIS
   DEVICE (on), PAUSED ON THIS DEVICE (paused), OFF and STOPPED (off), REMOTE (remote) */
.user-bot-bar[hidden] { display: none; }
.user-bot-bar {
    display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin-bottom: 5px; padding: 6px 12px;
    border: 1px solid var(--border-card); border-radius: var(--radius-md);
    background: var(--drift-surface) var(--drift) 50% / 200% 100% var(--drift-attach); /* 3.246.0: BloHunter's surface */
    background-color: var(--bg-card);
    font-size: 12px; color: var(--text-secondary); overflow-wrap: anywhere;
}
.user-bot-pill {
    font-size: 10px; font-weight: 700; letter-spacing: 0.06em; padding: 2px 6px; border-radius: 5px; line-height: 1.3;
    /* 3.237.0: the squared chip format and height of every other pill */
    border: 1px solid var(--border-card); color: var(--text-muted);
}
.user-bot-bar[data-tone="on"] .user-bot-pill { color: var(--bull); border-color: var(--bull-border); }
.user-bot-bar[data-tone="remote"] .user-bot-pill { color: var(--accent); border-color: var(--accent); }
/* the switch keeps new trades out (entries_off or Stop bot): PAUSED ON THIS DEVICE, never the green of trading */
.user-bot-bar[data-tone="paused"] { border-color: var(--warn-border); }
.user-bot-bar[data-tone="paused"] .user-bot-pill { color: var(--warn); border-color: var(--warn-border); }
.user-bot-bar[data-tone="off"] { border-color: var(--bear-border); background: var(--bear-bg); }
.user-bot-bar[data-tone="off"] .user-bot-pill { color: #fda4af; border-color: var(--bear-border); }
/* 2026-09-26 (operator order: one engine, one page): the account status the Trading Rules badge carried is the pill's
   (user.js barStatus): IP BLOCKED and READ ERROR (error, red), READ-ONLY (read, the accent), KEY NEEDED (key, amber) */
.user-bot-bar[data-tone="error"] { border-color: var(--bear-border); background: var(--bear-bg); }
.user-bot-bar[data-tone="error"] .user-bot-pill { color: #fda4af; border-color: var(--bear-border); }
.user-bot-bar[data-tone="read"] .user-bot-pill { color: var(--accent); border-color: var(--accent); }
.user-bot-bar[data-tone="key"] .user-bot-pill { color: var(--warn); border-color: var(--warn-border); }
.user-bot-text { flex: 1 1 200px; min-width: 0; }
/* the Strategy Architecture footer's strategy list wraps under the tick line on a narrow panel, right-aligned */
.user-ai-foot { flex-wrap: wrap; }
/* 2026-10-01 (operator): the strategy names stand on one line of their own, centred, never wrapped (an ellipsis only
   if a screen is narrower than the names at 10 px) */
.strategy-mix {
    flex-basis: 100%;
    width: 100%;
    min-width: 0;
    margin-top: 2px;
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    /* 2026-10-02 (operator, a narrower desktop window): the names are never cut with an ellipsis; the line sizes its
       text to the foot's width (a size container, .strategy-foot) from the number of characters the writer records
       in --mix-chars (0.62 em per character of the mono face with its tracking), 10 px at most, 7 px at least */
    font-size: clamp(7px, calc(100cqw / (var(--mix-chars, 50) * 0.62)), 10px);
    letter-spacing: -0.02em;
}
.strategy-foot { container-type: inline-size; }
/* 2026-10-02 (operator, a slim desktop window): the Strategy Architecture tiles' values stay on one line; from 768 px
   (where the utility makes them 14 px) the font follows the tile's width (a size container) down to 10 px, so
   "Variable Isolated" (17 characters of the mono face) fits a 150 px tile; a phone keeps its 12 px */
.arch-tile { container-type: inline-size; }
.glass-panel-subtle .arch-value { white-space: nowrap; }
@media (min-width: 768px) {
    .glass-panel-subtle .arch-value { font-size: clamp(10px, calc(100cqw / 11), 14px); }
}
/* 2026-10-01 (operator): the thesis card's head: the title and MARKET READ on one line, the tick chip under them on a
   phone and beside them from 640 px */
.ai-thesis-badge { margin-left: auto; order: 1; }
.ai-thesis-chip { flex-basis: 100%; order: 9; }
/* 2026-10-06: on a phone the title takes the room the badge leaves (wrapping inside itself when it must), so MARKET
   READ never drops to a line of its own: at 390 px the two filled 273 of 302 px, and Firefox on Linux, with wider
   fonts, used up the rest */
@media (max-width: 639.98px) {
    .ai-thesis-left { flex: 1 1 0; min-width: 0; }
}
@media (min-width: 640px) {
    .ai-thesis-chip { flex-basis: auto; order: 1; }
    .ai-thesis-badge { order: 2; }
}
/* the line with the person's own figures above the AI cards (computed in the browser, never the AI's) */
.user-ai-account { margin: 0; font-size: 11px; color: var(--text-secondary); overflow-wrap: anywhere; }
#userAiFeed .ai-text, #userAiFeed .ai-thesis-chip { overflow-wrap: anywhere; }

/* 2026-10-08 (operator): the header's ticker tape — the tradeable coins' daily change scrolling right to left for 50 s
   of every minute (user.js renderHeaderBar). One strip drawn twice and moved by half its width: a seamless loop whose
   speed is set from the item count (--tape-s). The edges fade, a hover pauses it, reduced motion shows it still. */
/* 3.203.1: the bar's two faces in one grid cell, crossfading over 450 ms; the face that is off takes no clicks */
.bar-stack { display: grid; }
.bar-stack > .bar-face {
    grid-area: 1 / 1;
    min-width: 0;
    transition: opacity 0.45s ease, visibility 0s linear 0s;
}
.bar-stack > .bar-face[data-face="off"] {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 0.45s ease, visibility 0s linear 0.45s;
}
.tape {
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
}
.tape-track {
    display: inline-flex;
    align-items: center;
    gap: 1.25rem;
    padding-right: 1.25rem;
    will-change: transform;
    animation: tapeScroll var(--tape-s, 40s) linear infinite;
}
.tape:hover .tape-track { animation-play-state: paused; }
.tape-item { display: inline-flex; align-items: baseline; gap: 0.35rem; }
.tape-sym { color: var(--text-primary); font-weight: 600; letter-spacing: 0.02em; }
.tape-held .tape-sym::before { content: '●'; color: var(--accent); font-size: 0.6em; margin-right: 0.3rem; vertical-align: middle; }
.tape-flat { color: var(--text-muted); }
/* 3.235.0 (operator, 2026-10-16: BloFin's tape): the price after the change, grey, to the coin's tick size */
.tape-px { color: var(--text-secondary); font-variant-numeric: tabular-nums; }
@keyframes tapeScroll {
    from { transform: translateX(0); }
    to { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
    .tape-track { animation: none; }
}

/* 3.233.0 (owner, 2026-10-16): the Open Positions buttons, BloHunter's look (app/styles/desktop/dashboard.css,
   the .position-* rules): M white, ✕ red, + green, A gold; 22 px squares 6 px apart, dim until hovered or focused */
td.position-actions-cell { width: 1%; padding: 0.5rem 0.875rem 0.5rem 6px; text-align: right; white-space: nowrap; }
.position-actions { display: inline-flex; align-items: center; gap: 6px; }
.position-close-btn, .position-add-btn, .position-manual-btn, .position-auto-btn {
  display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; padding: 0;
  border-radius: 5px; font-size: 12px; line-height: 1; cursor: pointer; opacity: 0.55;
  transition: opacity 0.15s ease, background 0.15s ease, border-color 0.15s ease;
}
.position-close-btn { border: 1px solid rgba(248, 113, 113, 0.28); background: rgba(248, 113, 113, 0.08); color: #ef4444; }
.position-close-btn:hover { opacity: 1; background: rgba(248, 113, 113, 0.18); border-color: rgba(248, 113, 113, 0.55); }
.position-close-btn:focus-visible { opacity: 1; outline: 1px solid #ef4444; outline-offset: 1px; }
.position-add-btn { border: 1px solid rgba(74, 222, 128, 0.28); background: rgba(74, 222, 128, 0.08); color: #22c55e; font-size: 15px; font-weight: 600; }
.position-add-btn:hover { opacity: 1; background: rgba(74, 222, 128, 0.18); border-color: rgba(74, 222, 128, 0.55); }
.position-add-btn:focus-visible { opacity: 1; outline: 1px solid #22c55e; outline-offset: 1px; }
.position-manual-btn { border: 1px solid rgba(255, 255, 255, 0.28); background: rgba(255, 255, 255, 0.08); color: #ffffff; font-weight: 600; }
.position-manual-btn:hover { opacity: 1; background: rgba(255, 255, 255, 0.18); border-color: rgba(255, 255, 255, 0.55); }
.position-manual-btn:focus-visible { opacity: 1; outline: 1px solid #ffffff; outline-offset: 1px; }
.position-manual-btn.is-active { opacity: 1; background: rgba(255, 255, 255, 0.24); border-color: rgba(255, 255, 255, 0.7); }
.position-close-btn:disabled, .position-add-btn:disabled, .position-manual-btn:disabled { cursor: default; opacity: 0.3; }
.position-auto-btn { border: 1px solid rgba(240, 185, 11, 0.35); background: rgba(240, 185, 11, 0.08); color: #f0b90b; font-weight: 600; }
.position-auto-btn:hover { opacity: 1; background: rgba(240, 185, 11, 0.18); border-color: rgba(240, 185, 11, 0.6); }
.position-auto-btn:focus-visible { opacity: 1; outline: 1px solid #f0b90b; outline-offset: 1px; }
.position-auto-btn.is-active { opacity: 1; background: rgba(240, 185, 11, 0.24); border-color: rgba(240, 185, 11, 0.75); color: #ffd24a; }
.position-auto-btn:disabled { cursor: default; opacity: 0.3; }
/* the bot's own rows are auto by definition: A is not a toggle but still reads ON */
.position-auto-btn.is-active:disabled { opacity: 1; background: rgba(240, 185, 11, 0.24); border-color: rgba(240, 185, 11, 0.75); }
.bb-dialog-line { margin-top: 0.35rem; }
.bb-dialog-select { background: rgba(10, 15, 28, 0.8); border: 1px solid var(--border-subtle, #334155); border-radius: 5px; padding: 2px 6px; font-size: 16px; color: inherit; }
.bb-dialog-yes.bb-dialog-danger { color: #fecaca; border-color: rgba(248, 113, 113, 0.55); background: rgba(127, 29, 29, 0.55); }
/* 3.234.0 (owner, 2026-10-16): the Open Positions liquidation price in BloHunter's amber, bold (its .liq-value); the
   red text-bear state stays while the forced-close price sits close to the stop; the chart's LIQ line and the global
   --warn token are unchanged */
.liq-value { color: #d97706; font-weight: 700; }
/* 3.234.0 (operator, 2026-10-16, after BloFin's positions table): a small bar at each Open Positions row's far left;
   since 3.240.0 it is profit (green) / loss (red), not the side */
td.pos-pnl-bar { position: relative; }
/* 3.242.0 (owner, 2026-10-17): the bar runs the full height of the row, top to bottom, touching the bars of the rows
   above and below (BloHunter's); the Age caption never shortens it. 1px past the cell at both ends: over the rows'
   1px borders (the collapsed border sits outside the cell's own box), so there is no gap at any seam */
td.pos-pnl-bar::before { content: ''; position: absolute; left: 0; top: -1px; bottom: -1px; width: 3px; border-radius: 0; z-index: 1; }
/* 3.240.0 (owner, with BloHunter): the bar is in profit (green, PnL >= 0) / in loss (red); the side is the label's colour.
   3.243.0 (owner): BloHunter's row glow: its bar (inset 3px at 85%, at the cell's left edge; drawn here as the
   full-height bar, which also covers the rows' borders) and a soft wash of its colour fading out across the first
   quarter of the cell */
td.pos-pnl-up::before { background: rgba(34, 197, 94, 0.85); }
td.pos-pnl-down::before { background: rgba(239, 68, 68, 0.85); }
td.pos-pnl-up { background: linear-gradient(90deg, rgba(34, 197, 94, 0.10) 0%, rgba(34, 197, 94, 0) 25%); }
td.pos-pnl-down { background: linear-gradient(90deg, rgba(239, 68, 68, 0.10) 0%, rgba(239, 68, 68, 0) 25%); }
/* 3.243.0 (owner, BloHunter's PositionTables.jsx / dashboard.css): the sparkline behind the contract name, not a
   column: from just right of the coin across the top line, the name, the side label and the pills read over it */
.pos-spark {
    /* BloHunter's .contract-sparkline exactly: behind the name, absolute in the contract cell */
    position: absolute; left: 32px; top: 2px; width: 92px; height: 24px; opacity: 0.55; pointer-events: none;
    z-index: 0; overflow: visible;
}
.pos-cell-body { position: relative; z-index: 1; }
/* 3.243.2 (owner, 2026-10-18, BloHunter's Contracts cell): the cell takes the width the Avg. Price column gave up and
   holds TWO pills in a row on the side label's line, [strategy] [TAKE PROFIT] (the second drawn only when it is lit,
   the first at the same left edge on every row), and the coin logo in a slot of its own: the cell's right padding
   (48px: the logo's 30px, its 10px from the edge and 8px clear of the pills) is never content, so no pill and no
   sparkline ever reaches the logo, and the logo sits at the same x on every row (one column, one width). 300px at least:
   the side label, HARMONICS and a lit TAKE PROFIT fit with the slot at the narrowest desktop width (1080px); a wider
   rendering widens the column, never overlaps */
/* 3.246.0 (owner, 2026-10-19, BloHunter's proportions): the column as wide as its widest row and no wider (it was a
   fixed 300px, which left a dead gap between the pills and the logo); the logo's slot stays the cell's own padding, so
   the logo sits 8px after the widest row's pills and no pill ever reaches it */
.positions-table tbody td.pos-pnl-bar { width: 1%; white-space: nowrap; }
/* the logo's slot: BloHunter's watermark sits in its .contract-cell (right 10px), so the slot is that box's own right
   padding (the logo's 30px, its 10px from the edge, 8px clear of the pills): no pill or sparkline ever reaches it */
.positions-table .contract-cell { padding-right: 48px; }
/* 3.243.0 (owner): the coin's logo faint behind the right of the contract cell (BloHunter's), never a broken box */
.pos-coin-logo {
    position: absolute; right: 10px; top: 50%; transform: translateY(-50%); width: 30px; height: 30px; opacity: 0.16;
    filter: saturate(0.85); pointer-events: none; z-index: 0;
}

/* 3.236.0 (owner's order 2026-10-17): the position-mode banner, red, two lines and the Recheck now button on one row;
   on a phone the words wrap and the button goes under them */
.pos-mode-banner { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
    color: #fecdd3; }
.pos-mode-banner[hidden] { display: none; }
.pos-mode-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 240px; }
.pos-mode-text strong { color: #fda4af; font-weight: 700; }
/* 3.241.0: the banners' buttons side by side (they wrap under the words on a phone), and a switch's failure (BloFin's
   refusal, or a switch that never reached BloFin) on its own line under them */
.pos-mode-actions { display: flex; flex-wrap: wrap; gap: 6px; }
.pos-mode-msg { flex-basis: 100%; margin: 0; color: #fecdd3; font-size: 12px; }
.pos-mode-msg[hidden] { display: none; }

/* 3.236.0 (owner, 2026-10-17): the delivery pill in BloHunter's exact form (its .delivery-pill, shell.css): a squared
   26 px pill with 6 px corners, the blue glow while live, a grey dot while waiting or on STANDARD DELIVERY, red with
   SEE WARNING BELOW while the reads are stale (the dot's red: feed_health.js data-state); the short words on a phone */
#feedPill.delivery-pill {
    height: 26px; display: inline-flex; align-items: center; gap: 8px; padding: 0 12px;
    border: 1px solid var(--border-card); border-radius: 6px; background: #0f1624; box-shadow: none;
    font-family: var(--font-text);
    font-size: 11px; font-weight: 700; letter-spacing: 0.06em; white-space: nowrap; text-transform: uppercase;
}
#feedPill.delivery-pill .feed-label { color: var(--text-muted); }
#feedPill.delivery-pill .pulse-dot { width: 7px; height: 7px; flex: 0 0 auto; }
#feedPill.delivery-pill .pulse-dot.stopped { background-color: #64748b; box-shadow: none; }
#feedPill.delivery-pill .feed-label-short,
#feedPill.delivery-pill .feed-label-fail-long,
#feedPill.delivery-pill .feed-label-fail-short { display: none; }
#feedPill.delivery-pill[data-delivery="advanced"] {
    border-color: rgba(56, 189, 248, 0.55);
    box-shadow: 0 0 8px rgba(56, 189, 248, 0.25), inset 0 0 6px rgba(56, 189, 248, 0.1);
}
#feedPill.delivery-pill[data-delivery="advanced"] .feed-label { color: #38bdf8; }
#feedPill.delivery-pill[data-delivery="advanced"] .pulse-dot.running { background-color: #38bdf8; box-shadow: 0 0 8px #38bdf8; }
#feedPill.delivery-pill[data-delivery="advanced"] .pulse-dot.running::after { border-color: #38bdf8; }
#feedPill.delivery-pill[data-state="red"] {
    border-color: rgba(244, 63, 94, 0.6); box-shadow: 0 0 8px rgba(244, 63, 94, 0.25);
}
#feedPill.delivery-pill[data-state="red"] .feed-label { color: #fda4af; }
#feedPill.delivery-pill[data-state="red"] .pulse-dot { background-color: #f43f5e; box-shadow: 0 0 8px #f43f5e; }
#feedPill.delivery-pill[data-state="red"] .feed-label-long,
#feedPill.delivery-pill[data-state="red"] .feed-label-short { display: none; }
#feedPill.delivery-pill[data-state="red"] .feed-label-fail-long { display: inline; }
@media (max-width: 560px) {
    #feedPill.delivery-pill .feed-label-long { display: none; }
    #feedPill.delivery-pill .feed-label-short { display: inline; }
    #feedPill.delivery-pill[data-state="red"] .feed-label-short,
    #feedPill.delivery-pill[data-state="red"] .feed-label-fail-long { display: none; }
    #feedPill.delivery-pill[data-state="red"] .feed-label-fail-short { display: inline; }
}
@media (prefers-reduced-motion: reduce) {
    #feedPill.delivery-pill .pulse-dot.running::after { animation: none; }
}

/* 3.238.0 (owner, 2026-10-17: on a narrower monitor AVG. / PRICE, BREAK EVEN / PRICE, EST. LIQ. / PRICE and MARK / PRICE
   wrapped onto two lines): every Open Positions header stays on one line; below 1440 px the cells' side padding tightens
   from 14 to 8 px first, and what still does not fit scrolls sideways inside the panel (its overflow-x-auto wrapper),
   never wrapped or squeezed */
.positions-table th, .positions-table th * { white-space: nowrap; } /* the PnL header's sort button too */
@media (max-width: 1439px) {
    .positions-table th, .positions-table td { padding-left: 8px; padding-right: 8px; }
}

/* 3.239.0 (owner, 2026-10-17): the Open Positions summary boxes (uPnL, MARGIN), BloHunter's boxed metric pills (its
   .panel-metric-pill): summary boxes, not the small squared chip (docs/STYLE_GUIDE.md names the exception); two
   decimals; the group right-aligned, 8 px apart, wrapping, left-aligned at phone width */
.panel-metric-group { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px; }
.panel-metric-pill {
    display: inline-flex; align-items: center; gap: 5px; min-height: 26px; padding: 5px 8px;
    border: 1px solid #1e2633; border-radius: 8px; background: #121824; color: #d8def0;
    font-size: 11px; font-weight: 700; white-space: nowrap;
}
.panel-metric-label { color: #9aa4b2; font-size: 10px; font-weight: 800; text-transform: uppercase; }
.panel-metric-value { color: #d8def0; font-size: 11px; font-weight: 700; }
.panel-metric-value.is-gain { color: #22c55e; }
.panel-metric-value.is-loss { color: #ef4444; }
@media (max-width: 560px) {
    .panel-metric-group { justify-content: flex-start; }
}

/* 3.240.0 (owner, 2026-10-17, with BloHunter): the side is BloHunter's side label, coloured text, no pill: "Long 5x" /
   "Short 20x", 13px semibold, green or red by side, the platform's text font (3.242.0; it was the monospaced font);
   as wide as the widest label on the screen plus 2px (3.243.0, owner: half the distance to the pill: 5 to 6px where
   the 9ch column left 11.3px; measured by the page, so no platform's wider text breaks the column), so the pill after
   it starts in
   one straight column. Under it BloHunter's age caption ("Age: 4h", 8px, weight 500, muted),
   laid out of the flow (absolute) so the row's height never changes (BloHunter's .side-block / .position-age) */
.side-block { position: relative; display: inline-flex; flex-shrink: 0; }
/* the pill line keeps a chip's 19px with no pill on it (a row without a strategy: every row one height) */
.position-pills { min-height: 19px; }
/* 3.243.0: the column's width from the widest label on this screen (terminal.js fitSideLabels sets --side-w on the
   table); 8.25ch until it is measured */
.pos-side-text { display: inline-block; min-width: var(--side-w, 8.25ch); font-size: 13px; font-weight: 600; line-height: 1.3; white-space: nowrap; }
.pos-side-text.is-long { color: #22c55e; }
.pos-side-text.is-short { color: #ef4444; }
.position-age {
    position: absolute; left: 0; top: 100%; font-size: 8px; font-weight: 500; line-height: 1; letter-spacing: 0.02em;
    color: #9aa4b2; white-space: nowrap; pointer-events: none;
}
/* ...and column pills have a uniform min-width per type, in ch, text centred: the strategy pill (SWING ... HARMONICS;
   a hand position's MANUAL / CAPTURED in the same place) sized to the widest public names in the text font (3.242.0,
   measured in Chromium, WebKit and Firefox, 10px bold with the 0.02em spacing: MOMENTUM 64.2px, HARMONICS 63.1px):
   11.5ch for the letters (66.1px), 14px for the 6px padding and the 1px border either side. min-width, never width: a
   longer text (a joined strategy name) still fits, never cut */
.badge-chip.pill-strategy {
    box-sizing: border-box;
    min-width: calc(11.5ch + 14px);
    justify-content: center;
    text-align: center;
}

/* 3.242.0 (owner, 2026-10-17; the platform's page background, BloHunter's VideoBackground.tsx and globals.css
   "Starfield Background"): a fixed layer of 90 white stars (starfield.js) drifting up and to the right behind the
   page; the page is black and every card, banner and bar on it solid, so the stars show only in the gaps. The header
   and the footer are solid strips. Stars are round: dots, icons and stars are the squarish standard's exceptions */
.stars { position: fixed; top: 0; left: 0; width: 100%; height: 200vh; pointer-events: none; z-index: 0;
    overflow: hidden; /* 3.243.2: a star drifting past the right edge never widens the page (iOS) */ }
.star {
    position: absolute; width: 2px; height: 2px; background: white; border-radius: 50%; opacity: 0.6;
    animation: starMove linear infinite;
}
.star:nth-child(odd) { opacity: 0.4; }
.star:nth-child(3n) { width: 1px; height: 1px; opacity: 0.3; }
.star:nth-child(5n) { width: 3px; height: 3px; opacity: 0.7; box-shadow: 0 0 4px rgba(255, 255, 255, 0.4); }
.star:nth-child(7n) { width: 4px; height: 4px; opacity: 0.8; box-shadow: 0 0 6px rgba(255, 255, 255, 0.5); }
@keyframes starMove {
    0% { transform: translate(0, 0); }
    100% { transform: translate(50vw, -100vh); }
}
@media (prefers-reduced-motion: reduce) {
    .star { animation: none; }
}
/* the page above the stars: the header (sticky, z 100) and these */
.page-main { position: relative; z-index: 1; }
body > footer { position: relative; z-index: 1; background: #080c14; }
/* 3.243.2 (operator, 2026-10-18, BloHunter's panel): the Open Positions title and its count line; the headers in
   sentence case (Contracts, PnL (PnL%), Est. Liq. Price); the PnL on one line ($0.02 +3.75%) */
/* 3.243.2 (operator, 2026-10-18: "match blohunter on open positions exactly"): BloHunter's panel and table, its
   dashboard.css values (`.panel-header h2`, `.panel-caption`, `.data-table`, `.contract-*`, `.chip.*`, `.pnl-stack`,
   `.value-stack`, `.primary-value`, `.secondary-value`, `.liq-value`, `.mark-value`, the live flash) */
.pos-panel-head { display: flex; flex-direction: column; }
.pos-panel-title { margin: 0; font-size: 17px; font-weight: 700; color: #e7ebf0; }
.pos-panel-sub { margin: 0; font-size: 12px; color: #9aa4b2; }
/* 3.246.0 (owner, 2026-10-19, BloHunter's .table-wrap): Open Positions grows with its rows. The table scrolls sideways
   only (a narrow screen); an axis set to auto turned the other to auto too, and a table one fractional pixel taller than
   its box (354 in 353) drew a scrollbar line down the card's right side on Windows. 2px under the rows so nothing of
   the last one (its age caption) is ever clipped; BloHunter's thin bar for the sideways scroll */
.positions-scroll {
    overflow-x: auto; overflow-y: hidden; padding-bottom: 2px;
    scrollbar-color: rgba(255, 255, 255, 0.18) transparent; scrollbar-width: thin;
}
.positions-scroll::-webkit-scrollbar { width: 4px; height: 4px; }
.positions-scroll::-webkit-scrollbar-track { background: transparent; }
.positions-scroll::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.14); border-radius: 999px; }
.positions-table thead th {
    padding: 12px 8px 11px; border-top: 1px solid rgba(255, 255, 255, 0.06); border-bottom: 1px solid rgba(255, 255, 255, 0.06);
    background: rgba(0, 0, 0, 0.22); color: #8d95aa; font-size: 11px; font-weight: 500; text-align: left;
    letter-spacing: 0.03em; text-transform: none; white-space: nowrap;
}
.positions-table tbody td {
    padding: 8px; border-bottom: 1px solid rgba(255, 255, 255, 0.06); vertical-align: middle; font-size: 13px;
    color: #e7ebf0;
}
.positions-table tbody tr:last-child td { border-bottom: none; }
.contract-cell { display: flex; flex-direction: column; gap: 4px; position: relative; }
.contract-cell > *:not(.pos-coin-logo):not(.pos-spark) { position: relative; z-index: 1; }
.contract-name { display: flex; align-items: center; gap: 6px; font-size: 15px; font-weight: 700; color: #f4f7fb; }
.contract-subline { display: flex; align-items: center; gap: 4px; flex-wrap: nowrap; margin-top: 0; }
.chip-strategy { border: 1px solid rgba(125, 211, 252, 0.38); background: rgba(10, 32, 48, 0.92); color: #bfe9ff; }
.chip.ttp {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    padding: 2px 6px;
    border-radius: 5px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.02em;
    border: 1px solid rgba(249, 168, 212, 0.6);
    background: rgba(48, 18, 36, 0.9);
    color: #fce7f3;
    transition:
        border-color 0.45s ease,
        background 0.45s ease,
        color 0.45s ease,
        box-shadow 0.45s ease;
}
.chip.ttp::after {
    content: '✓';
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #bbf7d0;
    font-size: 10px;
    font-weight: 800;
    opacity: 0;
    transform: scale(0.88);
    pointer-events: none;
    transition:
        opacity 0.45s ease,
        transform 0.45s ease;
}
.chip-ttp.ttp-outer { border: 1px solid rgba(249, 168, 212, 0.6); background: rgba(48, 18, 36, 0.9); color: #fce7f3; }
.chip-ttp.ttp-inner { border: 1px solid rgba(196, 181, 253, 0.6); background: rgba(30, 20, 52, 0.9); color: #ede9fe; }
.tp-locked .chip.ttp,
.chip.ttp.is-locked {
    border-color: rgba(34, 197, 94, 0.56);
    background: rgba(8, 32, 19, 0.94);
    color: transparent;
    box-shadow: 0 0 8px rgba(34, 197, 94, 0.2);
}
.tp-locked .chip.ttp::after,
.chip.ttp.is-locked::after {
    opacity: 1;
    transform: scale(1);
}
@media (prefers-reduced-motion: reduce) {
    .chip.ttp,
    .chip.ttp::after {
        transition: none;
    }
}
.pnl-stack { display: flex; flex-direction: row; align-items: center; justify-content: flex-start; gap: 8px; }
.value-stack { display: flex; flex-direction: column; gap: 4px; }
.pnl-stack .primary-value, .pnl-stack .secondary-value { white-space: nowrap; }
.pnl-stack .secondary-value { font-size: inherit; font-weight: 700; text-align: left; }
.positions-table .primary-value { font-weight: 700; }
.positions-table .secondary-value { color: #9aa4b2; font-size: 12px; }
.positions-table .primary-value.positive, .positions-table .secondary-value.positive { color: #22c55e; }
.positions-table .primary-value.negative, .positions-table .secondary-value.negative { color: #ef4444; }
.positions-table .pnl-stack .secondary-value { font-size: 13px; }
.positions-table .liq-value { color: #d97706; font-weight: 700; }
.mark-value { color: #38bdf8; white-space: nowrap; }
.positions-table .is-live-up, .positions-table .is-live-up-b { animation: open-position-number-up 1.05s ease-out; }
.positions-table .is-live-up-b { animation-name: open-position-number-up-b; }
.positions-table .is-live-down, .positions-table .is-live-down-b { animation: open-position-number-down 1.05s ease-out; }
.positions-table .is-live-down-b { animation-name: open-position-number-down-b; }
@keyframes open-position-number-up {
    0% { color: #00d67a; filter: brightness(1.35) saturate(1.2); text-shadow: 0 0 10px rgba(0, 214, 122, 0.28), 0 0 18px rgba(0, 214, 122, 0.16); }
    100% { filter: brightness(1) saturate(1); text-shadow: none; }
}
@keyframes open-position-number-up-b {
    0% { color: #00d67a; filter: brightness(1.35) saturate(1.2); text-shadow: 0 0 10px rgba(0, 214, 122, 0.28), 0 0 18px rgba(0, 214, 122, 0.16); }
    100% { filter: brightness(1) saturate(1); text-shadow: none; }
}
@keyframes open-position-number-down {
    0% { color: #ff6b6b; filter: brightness(1.25) saturate(1.18); text-shadow: 0 0 10px rgba(255, 107, 107, 0.28), 0 0 18px rgba(255, 107, 107, 0.14); }
    100% { filter: brightness(1) saturate(1); text-shadow: none; }
}
@keyframes open-position-number-down-b {
    0% { color: #ff6b6b; filter: brightness(1.25) saturate(1.18); text-shadow: 0 0 10px rgba(255, 107, 107, 0.28), 0 0 18px rgba(255, 107, 107, 0.14); }
    100% { filter: brightness(1) saturate(1); text-shadow: none; }
}
@media (prefers-reduced-motion: reduce) {
    .positions-table .is-live-up, .positions-table .is-live-up-b, .positions-table .is-live-down,
    .positions-table .is-live-down-b { animation: none; }
}

