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

    /* First-frame defense: the OS/browser paints html's background before any
       stylesheet rule applies to body. Setting it black on html itself means
       the very first pixel ever drawn — Mac dock launch, iOS PWA cold start,
       Chrome extension popup open — is already pure black, killing the white
       flash that used to bracket the splash. */
    html { background: #000; }

    :root {
      --bg:        #000000;
      --surface:   #0d0d0d;
      --surface2:  #141414;
      --border:    #222222;
      --text:      #b8b8b8;
      --muted:     #666666;
      --accent:    #7ab8f5;
      --green:     #7ab8f5;
      --neg:       #8f8f8f;
      --red:       #8f8f8f;
      --yellow:    #a8c8f0;
    }

    body {
      background: var(--bg);
      color: var(--text);
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
      font-size: 13px;
      line-height: 1.5;
      min-height: 100vh;
      overflow-x: hidden;
    }

    /* zoom trims the whole dashboard a touch smaller for a tighter, minimal feel */
    /* Journal stays invisible until the splash hands off — kills the
       flash-of-everything when the window first opens. */
    /* Safe-area-inset padding so iOS status bar / Dynamic Island / home
       indicator never overlap content. env() resolves to 0 on desktop, so
       the same rule works everywhere — desktop hits the literal min values,
       iPhone/iPad add the inset on top of the min. */
    .app {
      max-width: 1680px; margin: 0 auto; position: relative; z-index: 1; zoom: 0.96; opacity: 0;
      padding-top:    max(28px, calc(env(safe-area-inset-top)    + 14px));
      padding-right:  max(36px, env(safe-area-inset-right));
      padding-bottom: max(28px, calc(env(safe-area-inset-bottom) + 6px));
      padding-left:   max(36px, env(safe-area-inset-left));
    }

    /* ── Hero header ── */
    .hero {
      margin-bottom: 24px;
      padding-bottom: 20px;
      border-bottom: 1px solid var(--border);
      position: relative;
    }
    .hero-greeting {
      position: absolute;
      top: 0;
      right: 0;
      height: 32px;
      display: flex;
      align-items: center;
      font-size: 15.5px;
      font-weight: 500;
      color: var(--accent);
      letter-spacing: 0.015em;
      /* Multi-layer soft bloom instead of a tight 18px α0.55 — same total
         luminance, but spread across three layers so there is no defined
         rectangular edge around the line. Same recipe as the splash
         greeting fix: prevents the bounding-box halo artifact. */
      text-shadow:
        0 0 16px rgba(122,184,245,0.18),
        0 0 38px rgba(122,184,245,0.10),
        0 0 70px rgba(122,184,245,0.05);
    }
    /* Centred cluster — the live clock plus a discrete weather readout. */
    .hero-center {
      position: absolute;
      top: 0;
      left: 50%;
      transform: translateX(-50%);
      height: 32px;
      display: flex;
      align-items: center;
      gap: 16px;
    }
    .hero-clock {
      display: flex;
      align-items: center;
      font-size: 17px;
      font-weight: 500;
      color: #8a96a3;
      letter-spacing: 0.22em;
      font-variant-numeric: tabular-nums;
    }
    .hero-weather {
      display: flex;
      align-items: center;
      gap: 5px;
      font-size: 12.5px;
      font-weight: 600;
      color: #8a96a3;
      font-variant-numeric: tabular-nums;
      opacity: 0;
      transition: opacity 0.6s ease;
    }
    .hero-weather.ready { opacity: 1; }
    .hero-weather svg { display: block; color: var(--accent); }
    .hero-title {
      font-size: 32px;
      font-weight: 800;
      color: var(--accent);
      letter-spacing: 4px;
      line-height: 1;
      text-shadow: 0 0 40px rgba(122,184,245,0.25);
    }
    /* Per-letter spans — driven by the AI "decode" entrance in journal.js. */
    .ht-l {
      display: inline-block;
      will-change: transform, filter, opacity;
    }
    .ht-l.scrambling { color: #5d7fa6; }
    .hero-sub {
      font-size: 10px;
      letter-spacing: 0.18em;
      text-transform: uppercase;
      color: var(--muted);
      margin-top: 5px;
    }
    .hero-sub span {
      color: var(--accent);
      opacity: 0.6;
    }
    .hero-accent-line {
      position: absolute;
      bottom: -1px;
      left: 0;
      width: 60px;
      height: 1px;
      background: var(--accent);
      box-shadow: 0 0 8px rgba(122,184,245,0.6);
    }

    /* ── Opening splash ── */
    .splash {
      position: fixed;
      inset: 0;
      z-index: 200;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .splash-bg { position: absolute; inset: 0; background: #000000; }
    /* Once we're in the chamber the splash is only a fading backdrop — never let
       it intercept clicks meant for the chamber. The text bar must be usable
       while the orb is still forming (Jake), and the splash (z-index 200) was
       sitting on top of it until it finished hiding. */
    body.in-atrium #splash { pointer-events: none !important; }
    /* Faint architectural grid — fades out toward the edges. */
    .splash-grid {
      position: absolute;
      inset: 0;
      background-image:
        linear-gradient(rgba(116,160,210,0.022) 1px, transparent 1px),
        linear-gradient(90deg, rgba(116,160,210,0.022) 1px, transparent 1px);
      background-size: 54px 54px;
      -webkit-mask-image: radial-gradient(ellipse 52% 52% at 50% 46%, #000 4%, transparent 62%);
      mask-image: radial-gradient(ellipse 52% 52% at 50% 46%, #000 4%, transparent 62%);
      /* Pre-hide the grid in CSS — the JS fade-in animation has fill:both so
         it sets opacity:0 on first frame, but during the gap between CSS
         parse and JS exec the grid would otherwise render fully visible and
         cause a brief perceptible flash of faint architectural lines. */
      opacity: 0;
    }
    /* On Enter the grid energises — bright lines, no edge mask — so it can be
       seen as it comes flying straight at the viewer. */
    .splash-grid.warp {
      background-image:
        linear-gradient(rgba(150,196,255,0.55) 1.4px, transparent 1.4px),
        linear-gradient(90deg, rgba(150,196,255,0.55) 1.4px, transparent 1.4px);
      -webkit-mask-image: none;
      mask-image: none;
    }
    /* The luminance — a soft blue light alive inside the void behind the
       greeting. It breathes on a long slow pulse, the system quietly alive in
       the dark. Restrained — a faint diffuse glow, not a lamp. */
    .splash-glow {
      position: absolute;
      top: 50%; left: 50%;
      /* Sized in viewport units so the breathing light fills any screen
         proportionally — on a phone it dominates, on a large desktop it still
         reads as a real luminous halo rather than a small spot lost in black. */
      width:  min(1600px, 110vw);
      height: min(820px,  72vh);
      transform: translate(-50%, -50%);
      /* Many small steps with a quadratic falloff (alpha ∝ (1-r)²) — every
         consecutive pair has nearly identical alpha, so no individual stop
         is perceptible as a contour. The previous five hard stops created
         faint concentric rings that the eye tracked as the glow scaled in
         and out — pure optical illusion from gradient banding. */
      background: radial-gradient(ellipse at center,
        rgba(126,176,236,0.260) 0%,
        rgba(124,174,234,0.225) 8%,
        rgba(120,170,230,0.190) 16%,
        rgba(116,166,226,0.155) 24%,
        rgba(112,160,220,0.122) 32%,
        rgba(108,156,216,0.092) 40%,
        rgba(104,150,210,0.066) 48%,
        rgba(100,144,204,0.044) 56%,
        rgba( 96,138,198,0.026) 64%,
        rgba( 92,132,192,0.012) 72%,
        rgba( 88,128,186,0.004) 80%,
        transparent 88%);
      pointer-events: none;
      /* Heavier blur dissolves the gradient's mathematical edge into an
         organic atmospheric falloff — the light reads as emanating from the
         dark, not as a shape laid on top of it. Bumped from 14px so any
         residual banding from 8-bit alpha quantization is smeared past the
         eye's ability to track it. */
      filter: blur(34px) brightness(0.18);
      animation: splashBreathe 8.4s ease-in-out infinite;
    }
    @keyframes splashBreathe {
      /* Very narrow scale band — was 0.96↔1.06 (10% swing) which made the
         gradient's faint stop boundaries visibly travel outward and inward
         like contour rings. At ~3% swing the boundaries can't be tracked. */
      0%, 100% { opacity: 0.84; transform: translate(-50%, -50%) scale(0.992); }
      50%      { opacity: 1;    transform: translate(-50%, -50%) scale(1.022); }
    }
    /* Greeting + Enter — the whole content of the splash: a single quiet,
       centred cluster. Released from the void by the shroud, never animated
       on its own. */
    .splash-inner {
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 26px;
    }
    /* The veil — a permanent, atmospheric darkness: clear over the centre so
       it never dims the greeting, deepening to a soft vignette at the edges.
       It drifts perpetually on a long, slow cycle — the dark is never static. */
    .splash-veil {
      position: absolute;
      inset: 0;
      pointer-events: none;
      background: radial-gradient(ellipse 52% 56% at 50% 50%,
        rgba(0,0,0,0) 0%, rgba(2,3,6,0.16) 42%, rgba(1,2,4,0.48) 73%, rgba(0,0,1,0.78) 100%);
      will-change: transform;
      animation: veilDrift 26s ease-in-out infinite;
    }
    @keyframes veilDrift {
      0%, 100% { transform: scale(1)     translate3d(0, 0, 0); }
      50%      { transform: scale(1.045) translate3d(-0.6%, -0.7%, 0); }
    }
    /* The stage is a plain wrapper holding the splash scene — glow, grid,
       greeting, Enter. The void-release is done per text line, by the masks
       on .splash-greeting and .splash-enter — not on the stage. */
    .splash-stage {
      position: absolute;
      inset: 0;
    }
    /* Greeting — the focal line. It is STATIC: it never moves, scales, fades
       or masks. It sits already in its final place, rendered at brightness(0)
       — the same black as the void, so it is genuinely buried: present, but
       imperceptible. It is filled with a faint centre-weighted gradient, so as
       JS lifts its luminance over many seconds the brighter centre of the
       line crosses into perception first while the ends linger in the dark.
       There is no reveal shape — only the light slowly coming up. */
    .splash-greeting {
      font-size: clamp(20px, 2.5vw, 30px);
      font-weight: 500;
      letter-spacing: 0.015em;
      white-space: nowrap;
      text-align: center;
      line-height: 1.1;
      background: radial-gradient(ellipse 60% 200% at 50% 50%,
        #aaccef 0%, #7ba8d4 52%, #52749a 100%);
      -webkit-background-clip: text;
      background-clip: text;
      -webkit-text-fill-color: transparent;
      color: transparent;
      /* Wider, lower-alpha bloom — a diffuse haze around the line, not a
         tight rect-shaped halo. A tight halo around a single line of text
         reads as a rounded rectangle when the surrounding atmospheric glow
         recedes during the breath, which created a perceptible "box around
         the greeting" defect. Three layered shadows step out gently so the
         bloom has no defined edge. */
      text-shadow:
        0 0 18px rgba(122,184,245,0.10),
        0 0 48px rgba(122,184,245,0.07),
        0 0 96px rgba(122,184,245,0.04);
      filter: brightness(0);
      will-change: transform, filter;
      /* High-DPI antialiasing — the gradient-clipped text reads softer/cheaper
         without these on Retina + iPhone. Forces the highest-fidelity grayscale
         smoothing the browser supports, so each letter edge is razor-clean. */
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-rendering: optimizeLegibility;
    }
    /* After the lume reveal lands at brightness(1), the greeting takes on a
       slow, cinematic breathing cycle of its own — text-shadow expands and
       contracts, brightness gently modulates. Long, unhurried, never crossing
       below full visibility. Same 8.4s cadence as the glow so the two pulses
       stay in phase and the whole scene breathes as one organism. The
       animation is added programmatically once the lume settles (see
       initSplash) so it cannot fight the brightness(0) → brightness(1) reveal. */
    @keyframes splashGreetingBreathe {
      /* Brightness does almost all of the breathing work; the text-shadow
         barely moves so the bloom can never pulse out into a visible box
         around the greeting. Previous keyframes pushed the shadow to a
         heavy 0 0 72px / alpha 0.46 at the peak, which formed a tight
         halo rectangle around the line every breath. */
      0%, 100% {
        filter: brightness(0.94);
        text-shadow:
          0 0 18px rgba(122,184,245,0.09),
          0 0 48px rgba(122,184,245,0.06),
          0 0 96px rgba(122,184,245,0.035);
      }
      50% {
        filter: brightness(1.08);
        text-shadow:
          0 0 22px rgba(140,196,250,0.13),
          0 0 58px rgba(122,184,245,0.085),
          0 0 110px rgba(122,184,245,0.045);
      }
    }
    .splash-greeting.breathing {
      animation: splashGreetingBreathe 8.4s ease-in-out infinite;
    }
    /* Enter — like the greeting: static, buried at brightness(0), brought into
       perception only by its luminance slowly lifting. No mask, no movement. */
    .splash-enter {
      display: inline-flex;
      align-items: center;
      gap: 13px;
      background: transparent;
      border: none;
      color: var(--accent);
      padding: 6px;
      font-size: 12px;
      font-weight: 600;
      font-family: inherit;
      letter-spacing: 0.3em;
      text-transform: uppercase;
      cursor: pointer;
      transition: color 0.22s, text-shadow 0.22s;
      filter: brightness(0);
      will-change: filter;
      /* Soft 3-layer diffuse bloom — without this the button sat as a
         small bright text cluster on its own GPU compositing layer
         (will-change: filter), and the eye filled in a rectangular
         block of light around the text, identical to the greeting
         bounding-box halo we just killed. Spreading the bloom across
         three layers with widely-varying blur radii dissolves any
         perceptible edge — same recipe as .hero-greeting and the
         splash greeting fix. */
      text-shadow:
        0 0 14px rgba(122,184,245,0.10),
        0 0 32px rgba(122,184,245,0.06),
        0 0 60px rgba(122,184,245,0.03);
    }
    .splash-enter:hover {
      color: #b3d4fa;
      /* Hover brightens the bloom but stays in the soft multi-layer
         shape — the original tight 0 0 18px α0.55 would have flashed
         the same rectangular halo back on hover. */
      text-shadow:
        0 0 16px rgba(122,184,245,0.20),
        0 0 38px rgba(122,184,245,0.13),
        0 0 72px rgba(122,184,245,0.06);
    }
    .splash-enter-arrow {
      font-size: 14px;
      transition: transform 0.22s;
    }
    .splash-enter:hover .splash-enter-arrow { transform: translateX(6px); }

    /* ── Controls bar ── */
    /* Right side of the tab row — refresh + session countdown. */
    .tab-nav-right {
      margin-left: auto;
      display: flex;
      align-items: center;
      gap: 16px;
    }
    .refresh-btn {
      background: none;
      border: none;
      color: var(--muted);
      font-size: 14px;
      line-height: 1;
      padding: 4px;
      cursor: pointer;
      opacity: 0.32;
      transition: color 0.18s, opacity 0.18s;
    }
    .refresh-btn:hover { color: var(--accent); opacity: 1; }

    /* ── Session countdown clock ── */
    .session-clock {
      display: flex;
      align-items: center;
      gap: 8px;
      white-space: nowrap;
    }
    .sc-dot {
      width: 6px; height: 6px;
      border-radius: 50%;
      background: var(--muted);
      flex-shrink: 0;
    }
    .sc-dot.live {
      background: var(--accent);
      box-shadow: 0 0 7px rgba(122,184,245,0.9);
      animation: scPulse 2.4s ease-in-out infinite;
    }
    @keyframes scPulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

    /* ── Economic news strip ── */
    .news-strip { display: none; margin-bottom: 16px; }
    .news-strip.show { display: block; }
    .news-strip-inner {
      display: flex;
      align-items: center;
      gap: 16px;
      border-radius: 10px;
      padding: 13px 18px;
    }
    /* Active high-impact day — accent blue, with a live pulsing dot. */
    .news-strip-inner.alert {
      background: linear-gradient(90deg, rgba(122,184,245,0.13), rgba(122,184,245,0.04) 55%, transparent);
      border: 1px solid rgba(122,184,245,0.30);
      border-left: 3px solid #7ab8f5;
    }
    /* Muted — a past news day, or a day with no news. The light is "out". */
    .news-strip-inner.muted {
      background: var(--surface);
      border: 1px solid var(--border);
      border-left: 3px solid #4a5a68;
    }
    .news-tag {
      display: flex; align-items: center; gap: 8px;
      font-size: 10px; font-weight: 800; letter-spacing: 0.15em;
      text-transform: uppercase; white-space: nowrap;
    }
    .news-strip-inner.alert .news-tag { color: #7ab8f5; }
    .news-strip-inner.muted .news-tag { color: #8b97a3; }
    .news-tag-dot { width: 8px; height: 8px; border-radius: 50%; }
    .news-strip-inner.alert .news-tag-dot {
      background: #7ab8f5;
      box-shadow: 0 0 8px rgba(122,184,245,0.85);
      animation: newsDotPulse 2.6s ease-in-out infinite;
    }
    .news-strip-inner.muted .news-tag-dot { background: #5a7184; }
    @keyframes newsDotPulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
    .news-events { display: flex; flex-wrap: wrap; gap: 6px 20px; flex: 1; min-width: 0; }
    .news-event { font-size: 12.5px; white-space: nowrap; }
    .news-event-name { font-weight: 600; }
    .news-strip-inner.alert .news-event-name { color: var(--text); }
    .news-strip-inner.muted .news-event-name { color: #8b97a3; }
    .news-event-time { font-weight: 700; margin-left: 5px; }
    .news-strip-inner.alert .news-event-time { color: #7ab8f5; }
    .news-strip-inner.muted .news-event-time { color: #6f7d8a; }
    .news-clear-msg { font-size: 12.5px; color: #9aa5b0; }

    /* Calendar high-impact news label — sits above the day number so it
       never collides with the day's P&L. */
    .cal-news-label {
      font-size: 8px;
      font-weight: 800;
      letter-spacing: 0.05em;
      text-transform: uppercase;
      color: #7ab8f5;
      transition: color 0.2s;
      max-width: 100%;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    /* When the day also has P&L, the news tag drops to a footnote strip along
       the bottom edge — separated from the centred P&L, so they never crowd. */
    .cal-news-label.foot {
      position: absolute;
      left: 4px;
      right: 4px;
      bottom: 4px;
      text-align: center;
    }
    /* Past news day — the light is out, label fades to grey. */
    .cal-news-label.past { color: #6b7a88; }
    /* On a solid win/loss cell, the tag flips to a contrasting tone so it
       never disappears into the coloured background. */
    .cal-cell.cal-win:not(.cal-provisional)  .cal-news-label { color: rgba(0,0,0,0.62); }
    .cal-cell.cal-loss:not(.cal-provisional) .cal-news-label { color: rgba(255,255,255,0.58); }
    .sc-label {
      font-size: 9px;
      text-transform: uppercase;
      letter-spacing: 0.09em;
      color: var(--muted);
    }
    .sc-time {
      font-size: 12px;
      font-weight: 700;
      color: var(--accent);
      font-variant-numeric: tabular-nums;
      letter-spacing: 0.02em;
    }

    /* ── Tab nav ── */
    .tab-nav {
      display: flex;
      gap: 0;
      margin-bottom: 20px;
      border-bottom: 1px solid var(--border);
      position: relative;
    }
    .tab-indicator {
      position: absolute;
      bottom: -1px;
      left: 0;
      width: 0;
      height: 2px;
      background: var(--accent);
      border-radius: 2px;
      pointer-events: none;
      transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1),
                  width     0.4s cubic-bezier(0.22, 1, 0.36, 1);
    }
    .tab-btn {
      background: none;
      border: none;
      border-bottom: 2px solid transparent;
      border-radius: 6px 6px 0 0;
      color: var(--muted);
      padding: 8px 18px;
      font-size: 13px;
      font-family: inherit;
      cursor: pointer;
      margin-bottom: -1px;
      transition: color 0.18s, border-color 0.18s, background 0.18s;
    }
    .tab-btn.active { color: var(--accent); }
    .tab-btn:hover:not(.active) { color: var(--text); background: rgba(255,255,255,0.03); }

    /* ── Page views (tab-switch animation handled in JS via WAAPI) ── */
    .view { display: none; }
    .view.active { display: block; }
    /* The Journal page carries the most — trim it a touch smaller than the rest. */
    #journalView { zoom: 0.98; }

    /* (removed ~40 lines of orphaned .bulk-bar / .field-* / .apply-all-btn
       styles — no markup references them anywhere in the app.) */

    /* ── Main layout: calendar left, right panel ── */
    .main-layout {
      display: grid;
      grid-template-columns: 500px 1fr;
      gap: 16px;
      margin-bottom: 16px;
      align-items: stretch;
    }
    /* Right column stretches to match the (taller) calendar height */
    .main-right {
      display: flex;
      flex-direction: column;
      min-height: 0;
    }

    /* Lower row — trades table beside the notes panel */
    .lower-layout {
      display: grid;
      grid-template-columns: 1fr 540px;
      gap: 16px;
      margin-bottom: 16px;
      align-items: start;
      scroll-margin-top: 24px;
    }
    .trades-col { min-width: 0; }
    .lower-layout .note-wrap { margin-top: 0; }
    @media (max-width: 1000px) {
      .main-layout, .lower-layout { grid-template-columns: 1fr; }
    }

    /* ── Selected-date label + streak ── */
    .date-head {
      display: flex;
      align-items: baseline;
      justify-content: space-between;
      gap: 10px;
      margin-bottom: 10px;
      flex-wrap: wrap;
    }
    .date-label {
      font-size: 16px;
      font-weight: 700;
      color: var(--text);
      letter-spacing: -0.3px;
    }
    .date-pnl {
      font-weight: 700;
      letter-spacing: -0.2px;
    }
    .date-pnl.pos { color: var(--green); }
    .date-pnl.neg { color: var(--neg); }
    .date-pnl.flat { color: var(--muted); }
    .date-sep { color: var(--muted); font-weight: 400; margin: 0 4px; }
    .date-inprogress { font-size: 11px; color: var(--muted); font-weight: 400; }
    .streak {
      font-size: 11px;
      color: var(--muted);
      white-space: nowrap;
    }
    .streak.green { color: var(--green); }
    .streak.red   { color: var(--neg); }

    /* ── Stats cards ── */
    .summary {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: 10px;
      margin-bottom: 12px;
    }
    .card {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 10px;
      padding: 12px 14px;
    }
    .card-label { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; }
    .card-value { font-size: 20px; font-weight: 700; margin-top: 3px; }
    .card-value.pos { color: var(--green); }
    .card-value.neg { color: var(--neg); }
    .card-value.neutral { color: var(--text); }

    /* ── Equity curve ── */
    .equity-wrap {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 10px;
      padding: 12px 14px;
      margin-bottom: 0;
      position: relative;
      display: flex;
      flex-direction: column;
      flex: 1;
      min-height: 0;
    }
    .equity-title {
      font-size: 10px;
      text-transform: uppercase;
      letter-spacing: 0.08em;
      color: var(--muted);
      margin-bottom: 8px;
    }
    #equityCurve { width: 100%; display: block; cursor: crosshair; flex: 1; min-height: 0; }
    .eq-tip {
      position: absolute;
      display: none;
      background: #1a1a1a;
      border: 1px solid var(--border);
      border-radius: 6px;
      padding: 5px 9px;
      font-size: 11px;
      line-height: 1.35;
      pointer-events: none;
      white-space: nowrap;
      z-index: 5;
      box-shadow: 0 6px 16px rgba(0,0,0,0.55);
    }

    /* ── Calendar ── */
    .calendar-wrap {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 10px;
      padding: 14px;
      overflow: hidden;
    }
    .cal-header {
      display: flex;
      align-items: center;
      justify-content: space-between;
      margin-bottom: 10px;
    }
    .cal-title { font-size: 13px; font-weight: 600; color: var(--text); }
    .cal-nav {
      background: none;
      border: none;
      color: var(--muted);
      font-size: 18px;
      cursor: pointer;
      padding: 0 4px;
      line-height: 1;
      transition: color 0.15s;
    }
    .cal-nav:hover { color: var(--accent); }
    .cal-grid {
      display: grid;
      grid-template-columns: repeat(7, 1fr);
      gap: 4px;
      grid-auto-rows: 62px;  /* match .cal-cell height (was 58 → cells bled 4px past their track) */
    }
    .cal-dow {
      text-align: center;
      font-size: 9px;
      text-transform: uppercase;
      letter-spacing: 0.05em;
      color: var(--muted);
      padding-bottom: 4px;
    }
    .cal-cell {
      height: 62px;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      border-radius: 6px;
      cursor: pointer;
      transition: opacity 0.1s;
      min-width: 0;
      overflow: hidden;
      border: 1px solid transparent;
      position: relative;
    }
    .cal-cell:not(.empty):not(.cal-win):not(.cal-loss):hover { background: var(--surface2); }
    .cal-cell.empty { cursor: default; pointer-events: none; background: transparent; }
    /* Today — permanent prominent highlight (the in-progress trade session) */
    .cal-cell.cal-today { border: 2px solid #fff !important; }
    /* Real calendar today (used on weekends when the trade session is
       still pointing at Friday). Dashed accent border so it reads as
       "here, but not a trading day" — distinct from the solid trade-day
       highlight. */
    .cal-cell.cal-realtoday {
      border: 2px dashed rgba(122, 184, 245, 0.85) !important;
    }
    /* Selected — secondary marker shown while browsing other days */
    .cal-cell.cal-selected:not(.cal-today):not(.cal-realtoday) { border: 2px solid #6f6f6f !important; }

    /* Trade day backgrounds */
    .cal-cell.cal-win  { background: #7ab8f5; }
    .cal-cell.cal-win:hover  { opacity: 0.85; }
    .cal-cell.cal-loss { background: #3a3a3a; }
    .cal-cell.cal-loss:hover { opacity: 0.85; }

    /* Provisional days — session not closed (past 5 PM ET) yet. */
    .cal-cell.cal-provisional { border: 1px dashed var(--border); }
    .cal-cell.cal-provisional.cal-win {
      background: rgba(122,184,245,0.18);
      border-color: rgba(122,184,245,0.7);
    }
    .cal-cell.cal-provisional.cal-loss {
      background: rgba(143,143,143,0.16);
      border-color: rgba(143,143,143,0.5);
    }
    .cal-cell.cal-provisional.cal-win  .cal-day-pnl { color: #7ab8f5; }
    .cal-cell.cal-provisional.cal-loss .cal-day-pnl { color: #8f8f8f; }

    /* Day number — pinned to the top-left corner so the cell body stays free
       for the news tag + P&L, no matter how many a day carries. */
    .cal-day-num {
      position: absolute;
      top: 5px;
      left: 7px;
      font-size: 10px;
      font-weight: 600;
      line-height: 1;
      color: #8c8c8c;
    }
    .cal-cell.cal-today .cal-day-num,
    .cal-cell.cal-selected .cal-day-num,
    .cal-cell.cal-provisional .cal-day-num { color: var(--text); }
    .cal-cell.cal-win:not(.cal-provisional)  .cal-day-num { color: rgba(0,0,0,0.55); }
    .cal-cell.cal-loss:not(.cal-provisional) .cal-day-num { color: rgba(255,255,255,0.45); }

    /* P&L — the centred hero of the cell. */
    .cal-day-pnl {
      font-size: 11.5px;
      font-weight: 700;
      line-height: 1.15;
    }
    .cal-cell.cal-win  .cal-day-pnl { color: #000; }
    .cal-cell.cal-loss .cal-day-pnl { color: #d4d4d4; }

    /* ── Section title ── */
    .section-title {
      font-size: 10px;
      text-transform: uppercase;
      letter-spacing: 0.08em;
      color: var(--muted);
      margin-bottom: 8px;
    }

    /* ── Trades table ── */
    .trades-wrap {
      /* Same glass material as .note-wrap beside it (constitution principle 4:
         surfaces emerge from void with soft shadow + glass edge, not a flat box
         with a hard border). Was --surface + 1px border, which read as a
         different design system next to the notes panel. overflow:hidden stays
         so the rows table keeps its rounded clip. */
      background: linear-gradient(180deg,
        rgba(255,255,255,0.018) 0%,
        rgba(255,255,255,0.008) 100%);
      border: none;
      border-radius: 12px;
      overflow: hidden;
      box-shadow: var(--vv-shadow-soft), var(--vv-edge-glass);
    }
    table { width: 100%; border-collapse: collapse; }
    thead th {
      background: var(--surface2);
      padding: 9px 12px;
      text-align: left;
      font-size: 10px;
      text-transform: uppercase;
      letter-spacing: 0.06em;
      color: var(--muted);
      font-weight: 600;
      white-space: nowrap;
    }
    tbody tr { border-top: 1px solid var(--border); transition: background 0.1s; }
    tbody tr:hover { background: var(--surface2); }
    td { padding: 9px 12px; vertical-align: middle; white-space: nowrap; }

    .side-buy  { color: var(--green); font-weight: 600; }
    .side-sell { color: var(--neg);   font-weight: 600; }
    .pnl-pos   { color: var(--green); font-weight: 600; }
    .pnl-neg   { color: var(--neg);   font-weight: 600; }
    .pnl-open  { color: var(--yellow); font-style: italic; }

    /* ── Expandable trade rows ── */
    .trade-row { cursor: pointer; }
    .trade-row:hover { background: var(--surface2); }
    .trade-caret {
      display: inline-block;
      width: 12px;
      color: var(--muted);
      font-size: 9px;
      transition: color 0.15s, transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
    }
    .trade-row:hover .trade-caret { color: var(--accent); }
    .trade-row.open .trade-caret { color: var(--accent); transform: rotate(90deg); }

    /* Detail row — expands smoothly via a 0fr→1fr grid wrapper */
    .trade-detail { border-top: none; }
    .trade-detail:hover { background: transparent; }
    .trade-detail > td { padding: 0; }
    .trade-detail-inner {
      display: grid;
      grid-template-rows: 0fr;
      transition: grid-template-rows 0.5s cubic-bezier(0.22, 1, 0.36, 1);
    }
    .trade-detail.open .trade-detail-inner { grid-template-rows: 1fr; }
    /* The clipping layer carries no padding/border of its own, so it can
       collapse to a true zero height — nothing peeks through when closed. */
    .trade-detail-pad {
      overflow: hidden;
      min-height: 0;
    }
    .trade-detail-body { padding: 2px 12px 10px 12px; }

    /* ── Show-more toggle for the trades table ── */
    table.trades-collapsed .tr-extra { display: none; }
    .trades-more {
      display: flex;
      justify-content: center;
      margin-top: 11px;
    }
    .trades-more-btn {
      display: inline-flex;
      align-items: center;
      gap: 8px;
      background: none;
      border: none;
      color: var(--muted);
      font-family: inherit;
      font-size: 10px;
      font-weight: 600;
      letter-spacing: 0.2em;
      text-transform: uppercase;
      padding: 6px 4px;
      cursor: pointer;
      transition: color 0.2s, text-shadow 0.2s;
    }
    .trades-more-btn:hover {
      color: var(--accent);
      text-shadow: 0 0 14px rgba(122,184,245,0.45);
    }
    .tm-caret {
      font-size: 9px;
      transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
    }
    .trades-more-btn.open .tm-caret { transform: rotate(180deg); }

    .acct-subtable {
      width: 100%;
      border-collapse: collapse;
      background: rgba(255,255,255,0.014);
      border: none;
      border-left: 1px solid rgba(255,255,255,0.05);
      border-radius: 8px;
      overflow: hidden;
      margin-left: var(--vv-space-roomy);
      width: calc(100% - var(--vv-space-roomy));
    }
    .acct-subtable thead th {
      background: transparent;
      padding: var(--vv-space-tight) var(--vv-space-snug);
      font-family: var(--vv-font-label);
      font-size: 9.5px;
      font-weight: 600;
      color: var(--vv-text-muted);
      letter-spacing: 0.14em;
      text-transform: uppercase;
    }
    .acct-subtable tbody tr { border-top: 1px solid rgba(255,255,255,0.03); }
    .acct-subtable tbody tr:hover { background: transparent; }
    .acct-subtable td {
      padding: var(--vv-space-tight) var(--vv-space-snug);
      font-family: var(--vv-font-display);
      font-size: 11.5px;
      color: var(--vv-text-secondary);
      font-variant-numeric: tabular-nums;
    }
    .acct-subtable td.acct-name { color: var(--vv-text-primary); }

    /* Empty / loading / no-data placeholder used across every list
       and panel in the journal. Constitution principles 1 + 4 + 5 +
       8 all live here:
         1 — sits over its container; container should be void-treated
             not flat
         4 — has soft ambient glow at edges to feel like a "waiting"
             surface, not a dead box
         5 — single line of considered prose, no icon clutter
         8 — when used as a loading state, the ambient breath pulse
             communicates "Loryn is working" instead of dead silence */
    .empty {
      position: relative;
      text-align: center;
      padding: var(--vv-space-vast) var(--vv-space-roomy);
      font-family: var(--vv-font-display);
      font-size: var(--vv-body-size);
      font-weight: 350;
      letter-spacing: 0.005em;
      line-height: var(--vv-body-leading);
      color: var(--vv-text-secondary);
      background: radial-gradient(ellipse 60% 50% at 50% 50%,
        rgba(122,184,245,0.022) 0%,
        rgba(122,184,245,0.010) 30%,
        transparent 70%);
    }
    .empty .icon {
      display: none;  /* constitution 7 — no SaaS icon decoration on empty state */
    }
    .empty .msg {
      font-family: var(--vv-font-display);
      font-size: var(--vv-body-size);
      color: var(--vv-text-secondary);
    }
    /* Loading variant — when an .empty container is showing "Loading…"
       text, the .loading class adds the ambient breath pulse so it
       reads as "Loryn is fetching" not "dead UI". */
    .empty.loading,
    .empty[data-loading="true"] {
      animation: vvAmbientPulse 2.8s var(--vv-ease-breath) infinite;
    }

    /* ── Daily notes — timestamped log ── */
    .note-wrap {
      /* Composed from visual-vocab material treatment. Surfaces
         emerge from void with soft shadow + glass edge — not "flat
         box with hard border." Constitution principle 4. */
      background: linear-gradient(180deg,
        rgba(255,255,255,0.018) 0%,
        rgba(255,255,255,0.008) 100%);
      border: none;
      border-radius: 12px;
      padding: var(--vv-space-comfort);
      margin-top: var(--vv-space-comfort);
      box-sizing: border-box;
      display: flex;
      flex-direction: column;
      box-shadow: var(--vv-shadow-soft), var(--vv-edge-glass);
    }
    .note-wrap .section-title { margin-bottom: 8px; flex-shrink: 0; }
    /* The note log grows with its contents — the panel extends down the page
       as more (or longer) notes are added, instead of scrolling internally.
       syncNotesHeight() gives it a min-height matching the trades column so
       it stays balanced when there are few notes; once content exceeds that,
       it pushes the page down naturally. */
    .note-log {
      display: flex;
      flex-direction: column;
      gap: 8px;
    }
    .note-empty { color: var(--muted); font-size: 11px; font-style: italic; padding: 6px 2px; }
    .note-entry {
      background: rgba(255,255,255,0.016);
      border: none;
      border-left: 1px solid rgba(255,255,255,0.04);
      border-radius: 8px;
      padding: var(--vv-space-snug) var(--vv-space-comfort);
      animation: vvCinematicEnter var(--vv-dur-considered) var(--vv-ease-emerge) both;
      transition: opacity var(--vv-dur-considered) var(--vv-ease-cinematic),
                  transform var(--vv-dur-considered) var(--vv-ease-cinematic),
                  max-height var(--vv-dur-considered) var(--vv-ease-cinematic);
      overflow: hidden;
    }
    /* Cinematic exit when removing a note — collapse + fade. JS removes
       the element after the transition fires. */
    .note-entry.removing {
      opacity: 0;
      transform: translateX(-12px);
      max-height: 0;
      padding-top: 0;
      padding-bottom: 0;
      margin-top: 0;
    }
    .note-entry-head {
      display: flex;
      align-items: center;
      justify-content: space-between;
      margin-bottom: var(--vv-space-tight);
    }
    .note-entry-time {
      font-family: var(--vv-font-label);
      font-size: 9.5px;
      text-transform: uppercase;
      letter-spacing: 0.18em;
      color: var(--vv-cyan-accent);
      font-weight: 600;
    }
    .note-entry-del {
      background: none; border: none;
      color: rgba(255,255,255,0.18);
      cursor: pointer;
      font-size: 14px; font-weight: 300; line-height: 1; padding: 0 var(--vv-space-hair);
      opacity: 0;
      transition: opacity var(--vv-dur-quick) var(--vv-ease-cinematic),
                  color var(--vv-dur-quick) var(--vv-ease-cinematic);
    }
    .note-entry:hover .note-entry-del { opacity: 1; }
    .note-entry-del:hover { color: var(--vv-alarm-red); }
    .note-entry-text {
      width: 100%;
      background: transparent;
      color: var(--text);
      border: none;
      outline: none;
      resize: none;
      overflow: hidden;
      display: block;
      padding: 0;
      font-family: inherit;
      font-size: 12px;
      line-height: 1.5;
    }
    .note-entry-text::placeholder { color: var(--muted); }
    .note-add {
      margin-top: 8px;
      flex-shrink: 0;
      width: 100%;
      background: var(--surface2);
      color: var(--accent);
      border: 1px dashed var(--border);
      border-radius: 7px;
      padding: 7px 12px;
      font-size: 11px;
      font-weight: 600;
      font-family: inherit;
      cursor: pointer;
      transition: border-color 0.15s, background 0.15s;
    }
    .note-add:hover { border-color: var(--accent); background: #161616; }

    /* Add-note + dictate row. The mic lets a user SPEAK a note instead of typing
       it (Web Speech dictation straight into a fresh note). Sleek + discrete. */
    .note-foot { margin-top: 8px; display: flex; align-items: stretch; gap: 7px; }
    .note-foot .note-add { margin-top: 0; width: auto; flex: 1 1 auto; }
    .note-mic {
      flex: 0 0 auto; width: 38px;
      display: inline-flex; align-items: center; justify-content: center;
      background: var(--surface2);
      color: rgba(232, 240, 255, 0.45);
      border: 1px dashed var(--border);
      border-radius: 7px;
      cursor: pointer;
      transition: border-color 0.15s, background 0.15s, color 0.15s;
    }
    .note-mic svg { width: 16px; height: 16px; }
    .note-mic:hover { border-color: var(--accent); color: var(--accent); background: #161616; }
    /* Listening — solid accent + soft pulse so it's unmistakably recording. */
    .note-mic.is-listening {
      color: var(--accent); border-style: solid; border-color: var(--accent);
      background: rgba(122, 184, 245, 0.08);
      animation: noteMicPulse 1.5s ease-in-out infinite;
    }
    @keyframes noteMicPulse {
      0%, 100% { box-shadow: 0 0 0 0 rgba(122, 184, 245, 0); }
      50%      { box-shadow: 0 0 0 3px rgba(122, 184, 245, 0.14); }
    }

    /* ── Goals tab — planner ── */
    /* ── Goals overview panel ── */
    .goals-overview {
      display: flex;
      align-items: center;
      gap: 32px;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 12px;
      padding: 20px 26px;
      margin-bottom: 16px;
    }
    .go-ring { width: 138px; flex-shrink: 0; }
    .go-ring svg { width: 100%; display: block; }
    .go-body { flex: 1; min-width: 0; }
    .go-headline { font-size: 22px; font-weight: 800; color: var(--text); letter-spacing: -0.5px; }
    .go-headline span { color: var(--accent); }
    .go-sub { font-size: 12px; color: var(--muted); margin-top: 5px; }
    .go-chips { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 16px; }
    .go-chip {
      flex: 1;
      min-width: 110px;
      background: var(--surface2);
      border: 1px solid var(--border);
      border-radius: 9px;
      padding: 9px 12px;
    }
    .go-chip-name { font-size: 9px; text-transform: uppercase; letter-spacing: 0.07em; color: var(--muted); }
    .go-chip-val { font-size: 14px; font-weight: 700; color: var(--text); margin-top: 3px; }
    .go-chip-bar { background: #1a1a1a; height: 3px; border-radius: 2px; margin-top: 7px; overflow: hidden; }
    .go-chip-bar div { height: 100%; border-radius: 2px; background: var(--accent); transition: width 0.3s; }
    @media (max-width: 620px) {
      .goals-overview { flex-direction: column; text-align: center; gap: 18px; }
    }

    .planner-grid {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 14px;
      align-items: start;
    }
    /* Plan section — composed from vocab material treatment. Surfaces
       emerge from void via gradient + soft shadow + glass edge. */
    .plan-section {
      background: linear-gradient(180deg,
        rgba(255,255,255,0.020) 0%,
        rgba(255,255,255,0.010) 100%);
      border: none;
      border-left: 3px solid var(--vv-cyan-accent);
      border-radius: 12px;
      padding: var(--vv-space-comfort);
      display: flex;
      flex-direction: column;
      min-height: 168px;
      box-shadow: var(--vv-shadow-soft), var(--vv-edge-glass);
      transition: box-shadow var(--vv-dur-quick) var(--vv-ease-cinematic);
    }
    .plan-section.complete {
      box-shadow: var(--vv-shadow-soft), var(--vv-glow-soft), var(--vv-edge-glass);
    }
    .plan-head {
      display: flex;
      align-items: baseline;
      justify-content: space-between;
      gap: var(--vv-space-snug);
      margin-bottom: var(--vv-space-snug);
    }
    .plan-title {
      font-family: var(--vv-font-label);
      font-size: 11px;
      font-weight: 700;
      color: var(--vv-text-secondary);
      letter-spacing: 0.18em;
      text-transform: uppercase;
    }
    .plan-count {
      font-family: var(--vv-font-numerals);
      font-size: 11px;
      color: var(--vv-text-muted);
      font-weight: 500;
      font-variant-numeric: tabular-nums;
      letter-spacing: 0.04em;
    }
    .plan-count.complete { color: var(--vv-cyan-accent); }
    .plan-progress-track {
      background: rgba(255,255,255,0.04);
      border-radius: 2px;
      height: 3px;
      overflow: hidden;
      margin-bottom: var(--vv-space-comfort);
    }
    .plan-progress-fill {
      height: 100%;
      border-radius: 2px;
      background: var(--vv-cyan-accent);
      box-shadow: 0 0 6px rgba(122,184,245,0.4);
      transition: width var(--vv-dur-considered) var(--vv-ease-cinematic);
      min-width: 0;
    }
    .plan-add {
      display: flex;
      align-items: center;
      gap: var(--vv-space-snug);
      margin-top: var(--vv-space-snug);
      padding-top: var(--vv-space-comfort);
      border-top: 1px solid rgba(255,255,255,0.04);
    }
    .plan-add-mark {
      flex-shrink: 0; width: 16px; text-align: center;
      color: var(--vv-text-muted); font-size: 14px; font-weight: 300; cursor: pointer;
      transition: color var(--vv-dur-quick) var(--vv-ease-cinematic);
    }
    .plan-add-mark:hover { color: var(--vv-cyan-accent); }
    .plan-input {
      flex: 1; min-width: 0;
      background: transparent; border: none; outline: none;
      color: var(--vv-text-primary);
      font-family: var(--vv-font-display);
      font-size: 13px;
      padding: 0;
    }
    .plan-input::placeholder { color: var(--vv-text-quiet); }
    .plan-list { display: flex; flex-direction: column; gap: var(--vv-space-tight); }
    .plan-row {
      display: flex;
      align-items: center;
      gap: var(--vv-space-snug);
      background: rgba(255,255,255,0.018);
      border: none;
      border-left: 1px solid rgba(255,255,255,0.04);
      border-radius: 6px;
      padding: var(--vv-space-snug) var(--vv-space-comfort);
      transition: background var(--vv-dur-quick) var(--vv-ease-cinematic),
                  border-left-color var(--vv-dur-quick) var(--vv-ease-cinematic),
                  opacity var(--vv-dur-considered) var(--vv-ease-cinematic),
                  transform var(--vv-dur-considered) var(--vv-ease-cinematic),
                  max-height var(--vv-dur-considered) var(--vv-ease-cinematic);
      animation: vvCinematicEnter var(--vv-dur-considered) var(--vv-ease-emerge) both;
      overflow: hidden;
    }
    /* Cinematic exit when removing an objective (mirrors .note-entry.removing).
       JS adds .removing, waits the transition, then commits the state mutation. */
    .plan-row.removing {
      opacity: 0;
      transform: translateX(-12px);
      max-height: 0;
      padding-top: 0;
      padding-bottom: 0;
      margin-top: 0;
      pointer-events: none;
    }
    /* Brief cyan-wash pulse when Loryn amends a row in place. */
    .plan-row.just-edited,
    .rule-row.just-edited,
    .note-entry.just-edited {
      animation: vvRowEditPulse 900ms cubic-bezier(0.32, 0.5, 0.32, 1) both;
    }
    @keyframes vvRowEditPulse {
      0%, 100% { background-color: rgba(122, 184, 245, 0); }
      30%      { background-color: rgba(122, 184, 245, 0.10); }
    }
    .plan-row:hover { background: rgba(255,255,255,0.030); }
    .plan-check {
      width: 16px; height: 16px;
      border: 1px solid rgba(255,255,255,0.18);
      border-radius: 4px;
      cursor: pointer;
      flex-shrink: 0;
      display: flex; align-items: center; justify-content: center;
      font-size: 10px; font-weight: 700;
      color: var(--vv-cyan-accent);
      transition: border-color var(--vv-dur-quick) var(--vv-ease-cinematic),
                  background var(--vv-dur-quick) var(--vv-ease-cinematic);
    }
    .plan-row.done .plan-check {
      border-color: var(--vv-cyan-accent);
      background: rgba(122,184,245,0.12);
    }
    .plan-row.done {
      border-left-color: rgba(122,184,245,0.25);
    }
    .plan-text {
      flex: 1;
      font-family: var(--vv-font-display);
      font-size: 13px;
      color: var(--vv-text-primary);
      word-break: break-word;
      line-height: 1.4;
    }
    .plan-row.done .plan-text { color: var(--vv-text-muted); text-decoration: line-through; }
    /* Constitution 7 — destructive × hidden until row hover, hairline weight. */
    .plan-del {
      background: none; border: none;
      color: rgba(255,255,255,0.18);
      cursor: pointer;
      font-size: 14px; font-weight: 300; line-height: 1;
      padding: 0 var(--vv-space-tight);
      flex-shrink: 0;
      opacity: 0;
      transition: opacity var(--vv-dur-quick) var(--vv-ease-cinematic),
                  color var(--vv-dur-quick) var(--vv-ease-cinematic);
    }
    .plan-row:hover .plan-del { opacity: 1; }
    .plan-del:hover { color: var(--vv-alarm-red); }
    .plan-empty {
      color: var(--vv-text-muted);
      font-family: var(--vv-font-display);
      font-size: 12px;
      font-weight: 300;
      padding: var(--vv-space-comfort) 0;
      text-align: center;
      font-style: italic;
      letter-spacing: 0.01em;
    }

    /* ── Objectives: section heads + planner spacing ── */
    .obj-section-head {
      display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
      margin-bottom: 10px;
    }
    .obj-section-head .section-title { margin-bottom: 0; }
    .obj-section-sub { font-size: 11px; color: var(--muted); }
    .obj-section-title { margin-top: 4px; }
    .obj-discipline-title { margin-top: 30px; }
    .plan-section { transition: box-shadow 0.25s; }
    .plan-row { transition: border-color 0.15s, background 0.15s; }
    .plan-row:hover { border-color: #2c2c2c; }
    .plan-check:hover { border-color: var(--accent); }
    @media (max-width: 760px) {
      .planner-grid { grid-template-columns: 1fr; }
    }

    /* ── Objectives: trading-rules board ── */
    .rules-board {
      background: linear-gradient(180deg,
        rgba(255,255,255,0.018) 0%,
        rgba(255,255,255,0.008) 100%);
      border: none;
      border-left: 3px solid var(--vv-cyan-accent);
      border-radius: 12px;
      padding: var(--vv-space-hair) var(--vv-space-roomy);
      box-shadow: var(--vv-shadow-soft), var(--vv-edge-glass);
    }
    .rules-board > :first-child { border-top: none; }
    .rule-row, .rules-add {
      display: flex; gap: var(--vv-space-comfort);
      padding: var(--vv-space-comfort) var(--vv-space-hair);
      border-top: 1px solid rgba(255,255,255,0.04);
    }
    .rule-row {
      align-items: flex-start;
      overflow: hidden;
      transition: opacity var(--vv-dur-considered) var(--vv-ease-cinematic),
                  transform var(--vv-dur-considered) var(--vv-ease-cinematic),
                  max-height var(--vv-dur-considered) var(--vv-ease-cinematic),
                  padding var(--vv-dur-considered) var(--vv-ease-cinematic);
    }
    .rule-row.removing {
      opacity: 0;
      transform: translateX(-12px);
      max-height: 0;
      padding-top: 0;
      padding-bottom: 0;
      border-top-color: transparent;
      pointer-events: none;
    }
    .rules-add { align-items: center; }
    .rule-num {
      flex-shrink: 0;
      font-family: var(--vv-font-numerals);
      font-size: 11px;
      font-weight: 600;
      color: var(--vv-cyan-accent);
      letter-spacing: 0.04em;
      line-height: 1.55;
      font-variant-numeric: tabular-nums;
    }
    .rule-text {
      flex: 1; min-width: 0;
      font-family: var(--vv-font-display);
      font-size: 13.5px;
      line-height: 1.55;
      color: var(--vv-text-primary);
      word-break: break-word;
    }
    /* Graded/stated tag — the SAME model as the command Strategy doctrine, so the
       two rule views speak one language: "tracked" = Loryn grades it from fills,
       "stated" = she can't see it (honest intention). Kills the "two sections,
       one grades one doesn't" confusion (Jake 2026-06-04). */
    .rule-tag {
      flex-shrink: 0;
      font-family: var(--vv-font-display);
      font-size: 8.5px;
      font-weight: 600;
      letter-spacing: 0.14em;
      text-transform: uppercase;
      line-height: 1.55;
      padding-top: 1px;
      white-space: nowrap;
    }
    .rule-tag.is-graded { color: var(--vv-cyan-accent); }
    .rule-tag.is-stated { color: rgba(255, 255, 255, 0.30); font-style: italic; text-transform: none; letter-spacing: 0.02em; }
    .rule-del {
      flex-shrink: 0; background: none; border: none;
      color: rgba(255,255,255,0.18);
      cursor: pointer;
      font-size: 14px; font-weight: 300; line-height: 1.2; padding: 0 var(--vv-space-hair);
      opacity: 0;
      transition: opacity var(--vv-dur-quick) var(--vv-ease-cinematic),
                  color var(--vv-dur-quick) var(--vv-ease-cinematic);
    }
    .rule-row:hover .rule-del { opacity: 1; }
    .rule-del:hover { color: var(--vv-alarm-red); }
    .rules-add-mark {
      flex-shrink: 0; width: 16px; text-align: center;
      color: var(--vv-text-muted); font-size: 15px; font-weight: 300; cursor: pointer;
      transition: color var(--vv-dur-quick) var(--vv-ease-cinematic);
    }
    .rules-add-mark:hover { color: var(--vv-cyan-accent); }
    .rules-add-input {
      flex: 1; min-width: 0; background: transparent; border: none; outline: none;
      color: var(--vv-text-primary);
      font-family: var(--vv-font-display);
      font-size: 13.5px; padding: 0;
    }
    .rules-add-input::placeholder { color: var(--vv-text-quiet); }
    .rules-empty {
      padding: var(--vv-space-roomy) var(--vv-space-hair);
      text-align: center;
      color: var(--vv-text-muted);
      font-family: var(--vv-font-display);
      font-size: 12px;
      font-style: italic;
      letter-spacing: 0.01em;
    }

    /* ── Accounts overview panel ── */
    .accounts-overview {
      display: flex;
      align-items: center;
      gap: 32px;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 12px;
      padding: 20px 26px;
      margin-bottom: 16px;
    }
    .ao-ring { width: 138px; flex-shrink: 0; }
    .ao-ring svg { width: 100%; display: block; }
    .ao-body { flex: 1; min-width: 0; }
    .ao-headline { font-size: 22px; font-weight: 800; color: var(--text); letter-spacing: -0.5px; }
    .ao-headline span { color: var(--accent); }
    .ao-sub { font-size: 12px; color: var(--muted); margin-top: 5px; }
    .ao-chips { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 16px; }
    .ao-chip {
      flex: 1;
      min-width: 110px;
      background: var(--surface2);
      border: 1px solid var(--border);
      border-radius: 9px;
      padding: 9px 12px;
    }
    .ao-chip-name { font-size: 9px; text-transform: uppercase; letter-spacing: 0.07em; color: var(--muted); }
    .ao-chip-val { font-size: 16px; font-weight: 700; color: var(--text); margin-top: 3px; }
    .ao-chip-val.pos { color: var(--accent); }
    .ao-chip-val.neg { color: #8f8f8f; }
    @media (max-width: 620px) {
      .accounts-overview { flex-direction: column; text-align: center; gap: 18px; }
    }
    #acctsSummary:empty { display: none; }

    .footer { margin-top: 20px; text-align: center; font-size: 11px; color: #2a2a2a; }

    /* ── Health panel ── */
    .health-section-title { font-size: 10px; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); margin: 16px 0 8px; }
    .health-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 10px; }
    .health-card {
      background: var(--surface); border: 1px solid var(--border);
      border-left: 3px solid var(--border); border-radius: 10px; padding: 12px 14px;
      cursor: pointer;
      transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
    }
    .health-card:hover {
      transform: translateY(-4px) scale(1.03);
      border-color: rgba(122,184,245,0.55);
      box-shadow: 0 8px 24px rgba(122,184,245,0.18), 0 0 0 1px rgba(122,184,245,0.25);
    }
    .health-card.blown:hover {
      border-color: rgba(143,143,143,0.55);
      box-shadow: 0 8px 24px rgba(143,143,143,0.16), 0 0 0 1px rgba(143,143,143,0.22);
    }
    .health-card.healthy { border-left-color: #7ab8f5; }
    .health-card.warning { border-left-color: #8f8f8f; }
    .health-card.danger  { border-left-color: #5e5e5e; }
    .health-card.unlinked { opacity: 0.45; }
    .hc-name { font-size: 12px; font-weight: 600; color: var(--text); margin-bottom: 6px; }
    .hc-balance { font-size: 20px; font-weight: 700; color: var(--accent); margin-bottom: 10px; }
    .hc-bar-row { margin-top: 6px; }
    .hc-bar-label { display: flex; justify-content: space-between; font-size: 9px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; margin-bottom: 3px; }
    .hc-bar-track { background: #1a1a1a; border-radius: 3px; height: 3px; overflow: hidden; }
    .hc-bar-fill { height: 100%; border-radius: 3px; transition: width .3s; min-width: 2px; }
    .hc-bar-fill.target { background: #7ab8f5; }
    .hc-bar-fill.dll    { background: #8f8f8f; }
    .hc-bar-fill.dd     { background: #8f8f8f; }
    .hc-unlinked { font-size: 11px; color: #333; margin-top: 8px; }
    .hc-foot { font-size: 10px; color: var(--muted); margin-top: 10px; letter-spacing: .03em; }
    .health-card.blown { border-left-color: #5e5e5e; opacity: 0.62; position: relative; }
    .hc-blown-tag { font-size: 10px; color: #8f8f8f; font-weight: 700; letter-spacing: .04em; margin: 4px 0 8px; }
    /* Fail-safe affordances — manual blown override.
       The active card gets a small "×" in the top-right corner; the
       blown card gets an "undo" pill when it was marked manually.
       Both kept compact + low-key so they don't compete with the
       primary balance/limit info on the card. */
    .health-card { position: relative; }
    .hc-mark-blown {
      position: absolute;
      top: 6px;
      right: 8px;
      width: 18px;
      height: 18px;
      line-height: 18px;
      text-align: center;
      font-size: 14px;
      font-weight: 400;
      color: #444;
      cursor: pointer;
      border-radius: 50%;
      transition: color 0.15s, background 0.15s;
    }
    .hc-mark-blown:hover {
      color: rgba(180, 188, 200, 0.92);
      background: rgba(180, 188, 200, 0.10);
    }
    /* Armed state — first click turns the × into a "blow?" confirm pill.
       A second click within 3.5s commits (account-status.js lorynArmBlown). */
    .hc-mark-blown.is-arming {
      width: auto;
      height: auto;
      line-height: 1;
      font-size: 9px;
      font-weight: 600;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      color: rgba(180, 188, 200, 0.96);
      background: rgba(180, 188, 200, 0.14);
      border-radius: 9px;
      padding: 4px 8px;
    }
    .hc-blown-undo {
      position: absolute;
      top: 8px;
      right: 8px;
      font-size: 9px;
      letter-spacing: 0.14em;
      text-transform: uppercase;
      color: #7ab8f5;
      cursor: pointer;
      padding: 2px 7px;
      border: 1px solid rgba(122,184,245,0.35);
      border-radius: 10px;
      transition: background 0.15s, color 0.15s;
    }
    .hc-blown-undo:hover {
      color: #b3d4fa;
      background: rgba(122,184,245,0.12);
    }

    /* Blown / inactive section — smoothly collapses via a 0fr→1fr grid wrapper */
    .blown-caret { display: inline-block; transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1); }
    .blown-toggle.open .blown-caret { transform: rotate(90deg); }
    .blown-wrap {
      display: grid;
      grid-template-rows: 0fr;
      transition: grid-template-rows 0.5s cubic-bezier(0.22, 1, 0.36, 1);
    }
    .blown-wrap.open { grid-template-rows: 1fr; }
    .blown-pad { overflow: hidden; min-height: 0; padding-top: 2px; }

    /* ── Account detail modal ── */
    .acct-modal {
      display: none;
      position: fixed;
      inset: 0;
      background: rgba(0,0,0,0.7);
      z-index: 100;
      align-items: center;
      justify-content: center;
      padding: 20px;
      zoom: 0.96;
    }
    .acct-modal.open { display: flex; animation: acctModalFade 0.28s ease-out; }
    /* Exit transition — when the .closing class is added before display:none
       removal, the card recedes back into the void instead of snapping out.
       Constitution principle 3 — every transition is considered. */
    .acct-modal.closing { animation: acctModalFadeOut 0.32s cubic-bezier(0.4, 0, 0.1, 1) forwards; }
    .acct-modal.closing .acct-modal-card { animation: acctModalCardOut 0.36s cubic-bezier(0.4, 0, 0.1, 1) forwards; }
    @keyframes acctModalFade { from { opacity: 0; } to { opacity: 1; } }
    @keyframes acctModalFadeOut { from { opacity: 1; } to { opacity: 0; } }
    .acct-modal.open .acct-modal-card { animation: acctModalCardIn 0.46s cubic-bezier(0.16, 1, 0.3, 1); }
    @keyframes acctModalCardIn {
      from { opacity: 0; transform: translateY(20px) scale(0.965); filter: blur(6px); }
      to   { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
    }
    @keyframes acctModalCardOut {
      from { opacity: 1; transform: translateY(0) scale(1); filter: blur(0); }
      to   { opacity: 0; transform: translateY(8px) scale(0.98); filter: blur(3px); }
    }
    .acct-modal-card {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 16px;
      padding: 22px 24px 20px;
      width: 440px;
      max-width: 100%;
      max-height: 90vh;
      overflow-y: auto;
      position: relative;
      box-shadow: 0 30px 72px rgba(0,0,0,0.74);
    }
    /* Premium accent edge along the top of the card. */
    .acct-modal-card::before {
      content: '';
      position: absolute;
      top: 0; left: 24px; right: 24px;
      height: 1px;
      background: linear-gradient(90deg, transparent, rgba(122,184,245,0.55), transparent);
    }
    .acct-modal-close {
      position: absolute;
      top: 12px;
      right: 14px;
      background: none;
      border: none;
      color: var(--muted);
      font-size: 22px;
      line-height: 1;
      cursor: pointer;
      padding: 2px 6px;
      transition: color 0.15s;
    }
    .acct-modal-close:hover { color: var(--neg); }
    /* Header — name + firm, close button sits absolute top-right. */
    .am-head { padding-right: 26px; }
    .am-name { font-size: 16px; font-weight: 700; color: var(--text); }
    .am-firm {
      font-size: 9.5px; text-transform: uppercase; letter-spacing: .12em;
      color: var(--muted); margin-top: 4px;
    }

    /* Hero — progress ring beside the balance figures. */
    .am-hero { display: flex; align-items: center; gap: 20px; margin: 18px 0 2px; }
    .am-ring { width: 104px; height: 104px; flex-shrink: 0; }
    .am-ring svg { width: 100%; height: 100%; display: block; }
    .am-figures { flex: 1; min-width: 0; }
    .am-status {
      display: inline-block;
      font-size: 8.5px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
      padding: 4px 9px; border-radius: 5px; margin-bottom: 8px;
    }
    .am-status.healthy,
    .am-status.passed  { color: #7ab8f5; background: rgba(122,184,245,0.12); border: 1px solid rgba(122,184,245,0.34); }
    .am-status.warning { color: #b0b0b0; background: rgba(143,143,143,0.12); border: 1px solid rgba(143,143,143,0.32); }
    .am-status.danger,
    .am-status.blown   { color: #8f8f8f; background: rgba(94,94,94,0.14); border: 1px solid rgba(94,94,94,0.34); }
    .am-bal-label { font-size: 9px; text-transform: uppercase; letter-spacing: .1em; color: var(--muted); }
    .am-balance { font-size: 29px; font-weight: 800; color: var(--accent); letter-spacing: -0.8px; margin: 2px 0 7px; }
    .am-today { font-size: 12px; font-weight: 600; }
    .am-today.pos { color: var(--green); }
    .am-today.neg { color: var(--neg); }
    .am-today.flat { color: var(--muted); }

    .am-divider { height: 1px; background: var(--border); margin: 16px 0 15px; }

    /* Limit bars. */
    .am-limit { margin-bottom: 14px; }
    .am-limit:last-child { margin-bottom: 2px; }
    .am-limit-top {
      display: flex; align-items: baseline; justify-content: space-between;
      gap: 10px; margin-bottom: 7px;
    }
    .am-limit-name {
      font-size: 10.5px; font-weight: 700; color: var(--text);
      text-transform: uppercase; letter-spacing: .05em;
    }
    .am-limit-detail { font-size: 10.5px; color: var(--muted); white-space: nowrap; }
    .am-limit-track { background: #161616; border-radius: 4px; height: 8px; overflow: hidden; }
    .am-limit-fill { height: 100%; border-radius: 4px; min-width: 2px; }
    .am-limit-fill.target { background: #7ab8f5; }
    .am-limit-fill.dll    { background: #8f8f8f; }
    .am-limit-fill.dd     { background: #8f8f8f; }

    /* ── Analytics tab ── */
    .visuals-grid { display: flex; flex-direction: column; gap: 12px; }

    /* KPI strip */
    .an-kpis {
      display: grid;
      grid-template-columns: repeat(4, 1fr);
      gap: 10px;
    }
    @media (max-width: 720px) {
      .an-kpis { grid-template-columns: repeat(2, 1fr); }
    }
    .an-kpi {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 10px;
      padding: 12px 14px;
    }
    .an-kpi-label {
      font-size: 10px;
      color: var(--muted);
      text-transform: uppercase;
      letter-spacing: 0.05em;
    }
    .an-kpi-value { font-size: 20px; font-weight: 700; margin-top: 3px; letter-spacing: -0.3px; }
    .an-kpi-value.pos     { color: var(--accent); }
    .an-kpi-value.neg     { color: #8f8f8f; }
    .an-kpi-value.neutral { color: var(--text); }
    .an-kpi-sub { font-size: 10px; color: var(--muted); font-weight: 400; margin-top: 3px; }

    /* Chart card */
    .chart-card {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 10px;
      padding: 14px 16px;
      position: relative;
    }
    .chart-title {
      font-size: 10px;
      text-transform: uppercase;
      letter-spacing: 0.08em;
      color: var(--muted);
      margin-bottom: 12px;
    }

    /* Cumulative P&L curve */
    .an-curve { width: 100%; height: 248px; cursor: crosshair; }
    .an-tip {
      position: absolute;
      display: none;
      background: #1a1a1a;
      border: 1px solid var(--border);
      border-radius: 6px;
      padding: 6px 10px;
      font-size: 11px;
      line-height: 1.4;
      pointer-events: none;
      white-space: nowrap;
      z-index: 5;
      box-shadow: 0 6px 16px rgba(0,0,0,0.55);
    }

    /* Win rate donut */
    .an-winrate {
      display: flex;
      align-items: center;
      gap: 40px;
      padding: 6px 4px;
    }
    .an-donut { width: 190px; flex-shrink: 0; }
    .an-donut svg { width: 100%; display: block; }
    .an-wr-stats { flex: 1; display: flex; flex-direction: column; }
    .an-wr-row {
      display: flex;
      align-items: center;
      gap: 12px;
      padding: 11px 4px;
      border-bottom: 1px solid var(--border);
    }
    .an-wr-row:last-child { border-bottom: none; }
    .an-wr-dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; }
    .an-wr-label { flex: 1; font-size: 13px; color: var(--text); }
    .an-wr-val { font-size: 15px; font-weight: 700; color: var(--text); }
    .an-wr-row.total .an-wr-label,
    .an-wr-row.total .an-wr-val { color: var(--muted); }
    @media (max-width: 560px) {
      .an-winrate { flex-direction: column; gap: 18px; }
      .an-wr-stats { width: 100%; }
    }

    /* Performance by symbol */
    .an-symbols { display: flex; flex-direction: column; }
    .an-sym-row {
      display: flex;
      align-items: center;
      gap: 16px;
      padding: 12px 6px;
      border-bottom: 1px solid var(--border);
      transition: background 0.12s;
    }
    .an-sym-row:last-child { border-bottom: none; }
    .an-sym-row:hover { background: #141414; }
    .an-sym-name {
      width: 96px;
      flex-shrink: 0;
      font-size: 13px;
      font-weight: 700;
      color: var(--text);
      letter-spacing: -0.2px;
    }
    .an-sym-mid { flex: 1; min-width: 0; }
    .an-sym-track {
      background: #1a1a1a;
      border-radius: 4px;
      height: 8px;
      overflow: hidden;
    }
    .an-sym-fill { height: 100%; border-radius: 4px; min-width: 2px; }
    .an-sym-meta {
      font-size: 10px;
      color: var(--muted);
      margin-top: 6px;
      letter-spacing: 0.02em;
    }
    .an-sym-pnl {
      width: 100px;
      text-align: right;
      font-size: 14px;
      font-weight: 700;
    }
    .an-sym-pnl.pos { color: var(--accent); }
    .an-sym-pnl.neg { color: #8f8f8f; }

    /* ── Mobile / iPhone responsive pass ─────────────────────────────────────
       Targets phones (≤ 600px) and very tight phones (≤ 380px). Keeps the
       same typography family, accent colour and dark aesthetic — just stacks
       containers vertically, tightens padding/typography, and pulls every
       row inside the viewport so nothing horizontally scrolls (except the
       trades table, which gets touch-friendly horizontal scroll). The
       desktop layout above ≥ 1000px is untouched. */

    @media (max-width: 600px) {
      /* Safe area padding so iOS status bar / Dynamic Island / home indicator
         never overlap content. env(safe-area-inset-top) returns the actual
         pixel height on iPhone (~47-59pt). zoom:1 drops the desktop zoom. */
      .app {
        padding-top:    max(20px, calc(env(safe-area-inset-top) + 12px));
        padding-right:  max(14px, env(safe-area-inset-right));
        padding-bottom: max(20px, calc(env(safe-area-inset-bottom) + 8px));
        padding-left:   max(14px, env(safe-area-inset-left));
        zoom: 1;
      }

      /* Splash — keep restrained on a small screen. */
      .splash-greeting { font-size: clamp(18px, 5.2vw, 24px); letter-spacing: 0.02em; }
      .splash-enter   { font-size: 11px; letter-spacing: 0.26em; }
      /* Glow is intimate on portrait — sized to halo the greeting, not blanket
         the screen. On desktop it fills the void luxuriously; on mobile that
         same coverage reads as "block of pulsing" instead of minimalist light
         behind one line of text. Tightened to roughly the width of the
         greeting + a generous bloom, vertically just enough to wrap the
         greeting + enter cluster. */
      .splash-glow {
        width:  min(360px, 88vw);
        height: min(220px, 32vh);
      }

      /* HERO — fully restructured for portrait phone. Everything centered,
         stacked vertically with breathing room: clock+weather row first,
         then LORYN, then the accent line. The persistent hero-greeting is
         hidden (the splash already greeted you, repeating it just clutters
         a tight viewport). */
      /* Bulletproof vertical hero: flex column centred, children stack
         predictably with proper spacing. Override the desktop's absolute
         positioning with !important so nothing positions outside the column. */
      .hero {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 8px;
        margin-bottom: 18px;
        padding-bottom: 14px;
        text-align: center;
      }
      .hero-greeting { display: none; }
      .hero-center {
        position: static !important;
        top: auto !important; left: auto !important; right: auto !important;
        transform: none !important;
        height: auto;
        width: auto;
        margin: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 18px;
        flex-wrap: wrap;
      }
      .hero-clock   { font-size: 14px; letter-spacing: 0.16em; color: var(--text); }
      .hero-weather { font-size: 12px; }
      .hero-weather svg { width: 13px; height: 13px; }
      .hero-title {
        font-size: 30px;
        letter-spacing: 2.5px;
        line-height: 1;
        text-align: center;
        text-shadow: 0 0 22px rgba(122,184,245,0.22);
      }
      .hero-accent-line {
        position: relative;
        left: auto !important; bottom: auto !important;
        transform: none !important;
        width: 50px;
        margin-top: 2px;
      }
      .hero-sub { font-size: 9px; letter-spacing: 0.14em; text-align: center; }

      /* Tab nav — tabs in a row evenly spaced, session info + refresh wrap
         to a second row below. Hidden tab-nav-right would lose the session
         clock; instead we let it flex-wrap onto its own line. */
      .tab-nav {
        flex-wrap: wrap;
        justify-content: stretch;
        margin-bottom: 14px;
      }
      .tab-btn {
        padding: 9px 4px;
        font-size: 12px;
        flex: 1 1 0;
        min-width: 0;
        text-align: center;
      }
      .tab-nav-right {
        order: 99;
        flex: 1 0 100%;
        justify-content: center;
        gap: 12px;
        margin: 8px 0 0;
        padding: 8px 0 0;
        border-top: 1px solid var(--border);
        font-size: 10.5px;
      }

      /* Date head — let it wrap, smaller. */
      .date-label    { font-size: 14px; }
      .date-pnl      { font-size: 14px; }
      .date-inprogress { font-size: 10px; }

      /* Stat cards — 2 columns instead of 3 on phone. Each card tighter. */
      .summary    { grid-template-columns: repeat(2, 1fr); gap: 8px; margin-bottom: 10px; }
      .card       { padding: 10px 12px; }
      .card-label { font-size: 9px; }
      .card-value { font-size: 17px; }

      /* Equity curve — compact height. */
      .equity-wrap { min-height: 200px; padding: 10px 12px; }
      #equityCurve { min-height: 170px; }

      /* Calendar — preserve the 7-day grid, shrink cells and typography. */
      .cal-grid       { gap: 3px; }
      .cal-cell       { min-height: 44px; padding: 3px 4px; border-radius: 5px; }
      .cal-day-num    { font-size: 9px; }
      .cal-day-pnl    { font-size: 10px; font-weight: 700; }
      .cal-news-label { font-size: 8px; }
      .cal-dow        { font-size: 9px; }
      .cal-title      { font-size: 12.5px; }
      .cal-nav        { padding: 4px 8px; font-size: 14px; }

      /* Trades — on mobile the table becomes a vertical CARD LIST that is
         collapsed by default. Each card shows the at-a-glance fields:
         Time + Symbol on the left, P&L large on the right. Tap a card to
         expand (toggles the existing .open class via the click handler) —
         then all fields (Side, Qty, Entry, Exit, Duration, Accounts) plus
         the per-account detail are revealed. */
      .trades-col { overflow-x: visible; }
      .trades-col table {
        display: block;
        min-width: 0;
        border-collapse: separate;
        border: none;
        background: transparent;
        box-shadow: none;
      }
      .trades-col thead { display: none; }
      .trades-col tbody { display: block; }

      /* COLLAPSED card — Time top-left, Symbol below it, P&L right side big */
      .trades-col tr.trade-row {
        display: grid;
        grid-template-columns: 1fr auto;
        align-items: center;
        gap: 4px 14px;
        background: var(--surface2);
        border: 1px solid var(--border);
        border-radius: 9px;
        padding: 11px 14px 11px 13px;
        margin-bottom: 8px;
        cursor: pointer;
        position: relative;
      }
      .trades-col tr.trade-row:hover { background: var(--surface2); }
      .trades-col tr.trade-row td {
        display: none;
        border: none !important;
        padding: 0;
        text-align: left;
        white-space: normal;
      }
      .trades-col tr.trade-row td::before { display: none; }
      .trades-col tr.trade-row td.trade-caret-cell { display: none !important; }
      .trades-col tr.trade-row td[data-label="Time"] {
        display: block;
        grid-column: 1; grid-row: 1;
        font-size: 11.5px;
        color: var(--muted);
      }
      .trades-col tr.trade-row td[data-label="Symbol"] {
        display: block;
        grid-column: 1; grid-row: 2;
        font-size: 13px;
        font-weight: 700;
        color: var(--text);
      }
      .trades-col tr.trade-row td[data-label="P&L"] {
        display: block;
        grid-column: 2; grid-row: 1 / span 2;
        font-size: 16px;
        font-weight: 700;
        text-align: right;
        align-self: center;
      }

      /* EXPANDED card (.open) — show all fields with labels in a 3-col grid */
      .trades-col tr.trade-row.open {
        grid-template-columns: repeat(3, 1fr);
        gap: 10px 10px;
        padding: 11px 13px 13px;
      }
      .trades-col tr.trade-row.open td {
        display: flex;
        flex-direction: column;
        font-size: 12px;
      }
      .trades-col tr.trade-row.open td::before {
        display: block;
        content: attr(data-label);
        font-size: 9px;
        text-transform: uppercase;
        letter-spacing: 0.08em;
        color: var(--muted);
        font-weight: 500;
        margin-bottom: 3px;
      }
      .trades-col tr.trade-row.open td[data-label="P&L"] {
        grid-column: 1 / -1;
        grid-row: auto;
        font-size: 16px;
        font-weight: 700;
        text-align: left;
        align-self: stretch;
        padding-bottom: 6px;
        border-bottom: 1px solid var(--border) !important;
      }
      .trades-col tr.trade-row.open td[data-label="Time"],
      .trades-col tr.trade-row.open td[data-label="Symbol"] {
        grid-column: auto;
        grid-row: auto;
        font-size: 12px;
        font-weight: 500;
        color: var(--text);
      }

      /* Detail (per-account breakdown) row — the OUTER td is the colspan=10
         wrapper, treat it as a block. The NESTED .acct-subtable inside it
         must keep proper table layout, so we scope with > to direct child
         only and explicitly re-assert table display on the subtable. */
      .trades-col tr.trade-detail {
        display: block;
        margin: -4px 0 8px;
      }
      .trades-col tr.trade-detail > td {
        display: block;
        border: none !important;
        padding: 0;
      }
      .trades-col tr.trade-detail > td::before { content: none; display: none; }
      /* The .trade-detail-body wrapper has 12px side padding on desktop; on
         a phone every pixel counts so the inner subtable can render its P&L
         column without being clipped. Strip the left/right padding here. */
      .trades-col tr.trade-detail .trade-detail-body {
        padding: 4px 0 8px 0;
      }
      .trades-col tr.trade-detail .acct-subtable {
        display: table;
        width: 100%;
        margin-left: 0;
        font-size: 10.5px;
        border-collapse: collapse;
        table-layout: fixed;
      }
      .trades-col tr.trade-detail .acct-subtable thead { display: table-header-group; }
      .trades-col tr.trade-detail .acct-subtable tbody { display: table-row-group; }
      .trades-col tr.trade-detail .acct-subtable tr {
        display: table-row;
        background: transparent;
        border: none;
        margin: 0;
        padding: 0;
      }
      .trades-col tr.trade-detail .acct-subtable th,
      .trades-col tr.trade-detail .acct-subtable td {
        display: table-cell !important;
        border: none !important;
        padding: 3px 4px !important;
        text-align: left;
        white-space: nowrap;
        font-size: 10.5px;
        overflow: hidden;
        text-overflow: ellipsis;
      }
      /* Column widths: account name takes more room; numeric columns are
         compact; P&L is the last column and gets a fixed width so it always
         fits inside the card rather than clipping off the right edge. */
      .trades-col tr.trade-detail .acct-subtable th:nth-child(1),
      .trades-col tr.trade-detail .acct-subtable td:nth-child(1) { width: 30%; }
      .trades-col tr.trade-detail .acct-subtable th:nth-child(2),
      .trades-col tr.trade-detail .acct-subtable td:nth-child(2) { width: 21%; }
      .trades-col tr.trade-detail .acct-subtable th:nth-child(3),
      .trades-col tr.trade-detail .acct-subtable td:nth-child(3) { width: 21%; }
      .trades-col tr.trade-detail .acct-subtable th:nth-child(4),
      .trades-col tr.trade-detail .acct-subtable td:nth-child(4) {
        width: 8%; text-align: center;
      }
      .trades-col tr.trade-detail .acct-subtable th:nth-child(5),
      .trades-col tr.trade-detail .acct-subtable td:nth-child(5) {
        width: 20%; text-align: right; padding-right: 2px !important;
      }
      .trades-col tr.trade-detail .acct-subtable th {
        font-size: 8.5px;
        text-transform: uppercase;
        letter-spacing: 0.06em;
        color: var(--muted);
        font-weight: 500;
        border-bottom: 1px solid var(--border) !important;
      }
      .trades-col tr.trade-detail .acct-subtable td::before { content: none; display: none; }

      /* Notes — full width, tighter padding; height matching the trades col
         is irrelevant on mobile since columns are already stacked. */
      .note-wrap       { padding: 10px 12px; margin-top: 10px; }
      .note-wrap .section-title { font-size: 11px; }
      .note-entry      { padding: 7px 10px 8px; }
      .note-entry-text { font-size: 12.5px; line-height: 1.45; }
      .note-add        { font-size: 11px; padding: 7px; }

      /* Vault — overview row stacks, account grid drops to one column. */
      .accounts-overview {
        flex-direction: column;
        align-items: stretch;
        gap: 14px;
      }
      .ao-ring  { align-self: center; }
      .ao-headline { font-size: 16px; text-align: center; }
      .ao-chips { justify-content: space-between; gap: 8px; }
      .ao-chip  { padding: 8px 10px; flex: 1 1 calc(50% - 4px); }
      .ao-chip-name { font-size: 9px; }
      .ao-chip-val  { font-size: 14px; }
      .health-grid  { grid-template-columns: 1fr; gap: 8px; }

      /* Objectives — planner stacks (already does at 760px, this stays). */
      .planner-grid { gap: 10px; }

      /* Analytics — KPI grid stays 2-col (declared above), tighten cards. */
      .an-kpi        { padding: 10px 12px; }
      .an-kpi-value  { font-size: 17px; }
      .an-kpi-label  { font-size: 9px; }
      .an-kpi-sub    { font-size: 9px; }
      .chart-card    { padding: 12px; }
      .chart-title   { font-size: 12px; margin-bottom: 8px; }
      .an-curve      { min-height: 180px; }
      .an-symbols    { gap: 6px; }
      .an-sym-row    { font-size: 11px; gap: 8px; }
      .an-sym-name   { font-size: 11px; min-width: 60px; }
      .an-sym-meta   { font-size: 10px; }
      .an-sym-pnl    { font-size: 12px; }

      /* Win-rate ring — column stack (already declared at 560px, keep). */
      .an-winrate { padding: 0; }
    }

    @media (max-width: 380px) {
      /* On very tight phones (older iPhones, SE) drop stats to single column. */
      .summary { grid-template-columns: 1fr; }
      .ao-chip { flex: 1 1 100%; }
    }

    /* ── Landscape lockout (phone-sized viewports only).
       Triggered when a device with the short side under ~520px is rotated
       sideways — phones, not tablets, not desktops with narrow windows.
       Catches iPhone landscape specifically. Covers the whole viewport in
       solid black with a quiet "rotate me" prompt. */
    .orient-lock {
      position: fixed;
      inset: 0;
      z-index: 300;
      background: #000;
      display: none;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: 16px;
      padding: 28px;
      text-align: center;
      color: var(--accent);
    }
    .orient-lock-glyph svg {
      width: 52px;
      height: 52px;
      color: var(--accent);
      opacity: 0.85;
      animation: orientRotateHint 3.6s ease-in-out infinite;
      transform-origin: 50% 50%;
    }
    @keyframes orientRotateHint {
      0%, 100% { transform: rotate(-90deg); opacity: 0.55; }
      45%, 60% { transform: rotate(0deg);   opacity: 1;    }
    }
    .orient-lock-title {
      font-size: 16px;
      font-weight: 500;
      letter-spacing: 0.04em;
      text-shadow: 0 0 14px rgba(122,184,245,0.35);
    }
    .orient-lock-sub {
      font-size: 12px;
      color: var(--muted);
      letter-spacing: 0.04em;
    }
    /* The lockout fires when both:
       - the device is phone-sized (short side ≤ 520px), and
       - the viewport is landscape (width > height).
       This means iPad / desktop landscape NEVER triggers it. */
    @media (orientation: landscape) and (max-height: 520px) {
      .orient-lock { display: flex; }
    }

    /* ─────────────────────────────────────────────────────────────────
       CHAMBER STATE SYSTEM
       The chamber is not a sequence of animations. It is a continuously
       interpolating space with three state variables. JS sets the target
       values when context changes; CSS smoothly interpolates between
       them on multiple offset timing curves. The user cannot identify
       a single "animation" because no single property change carries
       the meaning — the meaning is the SIMULTANEOUS shift of several
       variables at different rates.

         --chamber-tension   0..1   relaxed → focused
         --chamber-luminance 0..1   dim → clear
         --orb-attention     0..1   idle → focused

       @property declarations make these animatable (without them
       custom properties just snap on change). */
    @property --chamber-tension {
      syntax: '<number>';
      inherits: true;
      initial-value: 0.2;
    }
    @property --chamber-luminance {
      syntax: '<number>';
      inherits: true;
      initial-value: 0.55;
    }
    @property --orb-attention {
      syntax: '<number>';
      inherits: true;
      initial-value: 0.3;
    }
    .atrium-stage {
      --chamber-tension: 0.2;
      --chamber-luminance: 0.55;
      --orb-attention: 0.3;
      /* Offset durations so the variables NEVER all settle on the same
         beat. The user feels a shift but can't lock onto a single
         animation timing. */
      transition:
        --chamber-tension   1400ms cubic-bezier(0.32, 0.7, 0.32, 1),
        --chamber-luminance 1800ms cubic-bezier(0.32, 0.7, 0.32, 1),
        --orb-attention      900ms cubic-bezier(0.32, 0.7, 0.32, 1);
    }
    /* How the state variables MANIFEST in the room:
         - chamber breath opacity follows luminance + tension
         - orb scale/brightness follows orb-attention
         - manifestation veil opacity follows tension */
    body.in-atrium .atrium-stage::before {
      /* Override the prior atrium-breath fixed opacity — now driven
         by state. Still has its own slow drift via the keyframe but
         the BASE level reads the tension. */
      opacity: calc(0.4 + var(--chamber-tension) * 0.5);
    }
    .atrium-orb {
      /* The orb's brightness rides orb-attention — she visibly "leans
         in" when concentrating. Scale-via-attention was REMOVED: it
         was a transform: scale() on the same element that the
         atrium-active shrink resizes via width/height, so the two
         motions would fight when both fired at once (chamber state
         change + atrium-active toggle), producing a jittery spaz mid-
         transition. Brightness alone is enough for the "leaning in"
         signal; scale stays available to the size transition. */
      filter: brightness(calc(1 + var(--orb-attention) * 0.35));
    }

    /* ── Loryn voice mode — full-screen takeover ─────────────────────────
       Activated by the mic button in the drawer or by tapping the orb
       inside the Atrium. Full-screen black panel; her orb at center as
       the focal element; status text below ("listening" / "thinking" /
       "speaking"); transcript area for whatever Jake just said. Built
       to feel like Claude/ChatGPT voice mode but native to her aesthetic. */
    .loryn-voice {
      position: fixed;
      inset: 0;
      background: radial-gradient(ellipse at center,
        rgba(8,10,16,1) 0%,
        rgba(0,0,0,1) 70%);
      z-index: 100;
      display: none;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      padding: env(safe-area-inset-top) 24px env(safe-area-inset-bottom);
    }
    .loryn-voice.open {
      display: flex;
      animation: loryn-voice-fade 900ms cubic-bezier(0.22, 1, 0.36, 1);
    }
    /* The chamber dissolves THROUGH the orb, not just as a flat opacity
       fade. The whole panel arrives from a soft-blurred near-black
       contraction, settles to clean black. Layered with the orb's
       own scale-up below for a coordinated "pulling Jake in" feel. */
    @keyframes loryn-voice-fade {
      0%   { opacity: 0; backdrop-filter: blur(0px); filter: brightness(0.5); }
      35%  { opacity: 1; backdrop-filter: blur(14px); filter: brightness(0.9); }
      100% { opacity: 1; backdrop-filter: blur(0px); filter: brightness(1); }
    }
    /* The orb scales up from a point — sells the "she's RISING into
       view from the deep" entrance rather than a flat fade-in. The
       inner orb animations (mist breathe, ring trace) handle the
       living detail; this is the room-scale arrival. */
    .loryn-voice.open .loryn-voice-orb {
      animation: loryn-voice-orb-rise 1100ms cubic-bezier(0.22, 1, 0.36, 1);
    }
    @keyframes loryn-voice-orb-rise {
      0%   { opacity: 0; transform: scale(0.18); filter: blur(8px) brightness(1.6); }
      45%  { opacity: 1; transform: scale(1.05); filter: blur(0)    brightness(1.15); }
      100% { opacity: 1; transform: scale(1);    filter: blur(0)    brightness(1); }
    }
    /* Status, transcript, hint, close all lift in AFTER the orb has
       arrived, so Jake's eye lands on her FIRST. */
    .loryn-voice.open .loryn-voice-status,
    .loryn-voice.open .loryn-voice-transcript {
      animation: loryn-voice-text-rise 700ms cubic-bezier(0.22, 1, 0.36, 1) 520ms backwards;
    }
    .loryn-voice.open .loryn-voice-close,
    .loryn-voice.open .loryn-voice-hint {
      animation: loryn-voice-text-rise 600ms cubic-bezier(0.22, 1, 0.36, 1) 760ms backwards;
    }
    @keyframes loryn-voice-text-rise {
      from { opacity: 0; transform: translateY(6px); }
      to   { opacity: 1; transform: translateY(0); }
    }
    .loryn-voice-close {
      position: absolute;
      top: max(18px, calc(env(safe-area-inset-top) + 6px));
      right: 22px;
      width: 36px;
      height: 36px;
      background: transparent;
      border: none;
      color: var(--muted);
      font-size: 30px;
      line-height: 1;
      cursor: pointer;
      opacity: 0.55;
      transition: opacity 0.18s ease, color 0.18s ease;
      padding: 0;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .loryn-voice-close:hover { opacity: 1; color: var(--accent); }
    .loryn-voice-orb {
      width: 220px;
      height: 220px;
      position: relative;
      /* Reuses the same SVG orb but in a bigger, statelier scale. The
         orb's inner animations (mist breathing, ring spin) carry the
         "presence" — no extra pulse needed here. */
    }
    /* Desktop drop-shadow halo identical to atrium; iOS gets the
       box-shadow pseudo-element override below. */
    .loryn-voice-orb .loryn-orb-svg {
      filter: drop-shadow(0 0 60px rgba(122,184,245,0.35));
    }
    @media (hover: none) and (pointer: coarse) {
      .loryn-voice-orb { position: relative; }
      .loryn-voice-orb .loryn-orb-svg { filter: none; }
      .loryn-voice-orb::before {
        content: '';
        position: absolute;
        top: 50%; left: 50%;
        width: 52%; height: 52%;
        transform: translate(-50%, -50%);
        border-radius: 50%;
        box-shadow: 0 0 64px 6px rgba(122,184,245,0.35);
        pointer-events: none;
      }
    }
    .loryn-voice-status {
      margin-top: 38px;
      font-size: 11px;
      letter-spacing: 0.34em;
      text-transform: uppercase;
      color: var(--accent);
      opacity: 0.7;
      transition: opacity 0.4s ease;
    }
    /* Subtle breathing on the status text so the screen isn't dead. */
    .loryn-voice.open .loryn-voice-status {
      animation: loryn-voice-status-pulse 2.6s ease-in-out infinite;
    }
    @keyframes loryn-voice-status-pulse {
      0%, 100% { opacity: 0.45; }
      50%      { opacity: 0.9; }
    }
    .loryn-voice-transcript {
      margin-top: 22px;
      font-size: 14px;
      line-height: 1.55;
      color: var(--text);
      max-width: 640px;
      text-align: center;
      padding: 0 12px;
      min-height: 22px;
      opacity: 0.85;
    }
    .loryn-voice-hint {
      position: absolute;
      bottom: max(20px, calc(env(safe-area-inset-bottom) + 12px));
      font-size: 9.5px;
      letter-spacing: 0.28em;
      text-transform: uppercase;
      color: var(--muted);
      opacity: 0.4;
    }


/* ── First-run onboarding card ───────────────────────────────────
   Mounts on top of the journal view for fresh accounts (zero fills).
   journal.js's maybeShowFirstRun() toggles visibility based on the
   all-time fills count + extension-detected status. */
.first-run-card {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 56px 24px 80px;
  z-index: 40;
  background: radial-gradient(ellipse 60% 80% at 50% 20%,
                              rgba(122,184,245,0.08) 0%,
                              rgba(2,2,5,0.94) 60%,
                              rgba(2,2,5,0.98) 100%);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  overflow-y: auto;
  animation: vvCinematicEnter var(--vv-dur-cinematic, 700ms) var(--vv-ease-emerge, ease-out) both;
}
.first-run-card[hidden] { display: none; }
.first-run-inner {
  max-width: 720px;
  width: 100%;
}
.first-run-eyebrow {
  margin: 0 0 14px;
  font-family: var(--vv-font-label);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.32em;
  color: var(--vv-cyan-accent);
}
.first-run-title {
  margin: 0 0 14px;
  font-family: var(--vv-font-display);
  font-size: 28px;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--vv-text-primary);
  line-height: 1.25;
}
.first-run-lede {
  margin: 0 0 36px;
  font-family: var(--vv-font-display);
  font-size: 15px;
  line-height: 1.55;
  color: var(--vv-text-secondary);
}
.first-run-steps {
  list-style: none;
  margin: 0 0 32px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.first-run-step {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 18px 22px;
  background: linear-gradient(180deg, rgba(255,255,255,0.018) 0%, rgba(255,255,255,0.006) 100%);
  border: 1px solid rgba(255,255,255,0.05);
  border-radius: 14px;
  transition: border-color 240ms ease, background 240ms ease;
}
.first-run-step.is-done {
  border-color: rgba(122,184,245,0.32);
  background: linear-gradient(180deg, rgba(122,184,245,0.06) 0%, rgba(122,184,245,0.02) 100%);
}
.first-run-num {
  flex: 0 0 28px;
  height: 28px;
  border-radius: 50%;
  background: rgba(255,255,255,0.04);
  color: var(--vv-text-quiet);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--vv-font-label);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  margin-top: 2px;
  transition: background 240ms ease, color 240ms ease;
}
.first-run-step.is-done .first-run-num {
  background: rgba(122,184,245,0.18);
  color: var(--vv-cyan-accent);
}
.first-run-step.is-done .first-run-num::before {
  content: '✓';
  font-size: 14px;
}
.first-run-step.is-done .first-run-num {
  font-size: 0;
}
.first-run-body { flex: 1; min-width: 0; }
.first-run-step-title {
  margin: 0 0 5px;
  font-family: var(--vv-font-display);
  font-size: 15px;
  font-weight: 500;
  color: var(--vv-text-primary);
  line-height: 1.35;
}
.first-run-step-desc {
  margin: 0;
  font-family: var(--vv-font-display);
  font-size: 13px;
  line-height: 1.55;
  color: var(--vv-text-secondary);
}
.first-run-actions {
  margin-top: 12px;
  display: flex;
  align-items: center;
  gap: 14px;
}
.first-run-btn {
  display: inline-block;
  padding: 9px 18px;
  background: rgba(122,184,245,0.12);
  border: 1px solid rgba(122,184,245,0.4);
  border-radius: 9px;
  color: var(--vv-cyan-accent);
  font-family: var(--vv-font-label);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  text-decoration: none;
  transition: background 200ms ease, transform 200ms ease, border-color 200ms ease;
}
.first-run-btn:hover {
  background: rgba(122,184,245,0.18);
  border-color: rgba(122,184,245,0.6);
  transform: translateY(-1px);
}
.first-run-ok {
  font-family: var(--vv-font-label);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--vv-cyan-accent);
}
.first-run-foot {
  margin: 0;
  text-align: center;
  font-family: var(--vv-font-display);
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--vv-text-quiet);
}
.first-run-foot a {
  color: var(--vv-cyan-accent);
  text-decoration: none;
}
.first-run-foot a:hover { text-decoration: underline; }

@media (max-width: 600px) {
  .first-run-card { padding: 32px 16px 60px; }
  .first-run-title { font-size: 22px; }
  .first-run-step { padding: 14px 16px; }
}
