/* The top bar's styling, in one place.
 *
 * It was inlined in seven pages. Four variants had already appeared: the
 * dashboard had made its header static, the agent page had added a touch
 * target and a select, and the account page had silently lost the whole
 * `.acctlink` block — so the link to your own settings rendered unstyled on
 * the one page that is about your own settings.
 *
 * Genuinely page-specific chrome still lives on its page, below its own
 * `<style>`; what is here is what every page agrees on.
 */

header { display:flex; align-items:center; gap:0; padding:0 20px; height:72px;
         border-bottom:1px solid rgba(255,255,255,.06);
         background:rgba(5,6,8,.7); backdrop-filter:blur(28px) saturate(1.4);
         -webkit-backdrop-filter:blur(28px) saturate(1.4);
         box-shadow:inset 0 1px 0 rgba(255,255,255,.08);
         position:sticky; top:0; z-index:30; }

/* ── The mark ─────────────────────────────────────────────────────────────
   `.emblem` lives in `brand.css` — the sign-in screen draws the same mark and
   does not load this file. All that belongs here is how big the header wants
   it and the fallback below. */
.brand { display:flex; align-items:center; gap:11px; padding:2px 4px;
         border-radius:8px; margin-right:20px; --emblem:38px; }
/* Belt and braces, and declared before `.emblem img` in `brand.css` cannot
   help here — so it is scoped to `.brand` only, where an unwrapped <img> from
   a cached page would otherwise render at the file's natural 341x512. */
.brand > img { height:var(--emblem); width:auto; }
.brand .name { font:600 20px var(--font-sans); letter-spacing:-.015em;
               white-space:nowrap; }
.branddiv { width:1px; height:32px; background:var(--line-strong); flex:none;
            margin-right:20px; }

/* ── Sections ─────────────────────────────────────────────────────────────
   The active tab is marked by a lit rail on the bar's own bottom edge rather
   than by a filled pill. It is the same device the dashboard's panel uses for
   its top rule, so the chrome and the page it frames say "here" the same way
   once instead of twice. */
nav { display:flex; gap:0; align-self:stretch; }
nav a { display:flex; align-items:center; padding:0 15px; font-size:13px;
        color:var(--dim); white-space:nowrap; position:relative;
        transition:color .15s; }
/* The section that belongs to the other book. Said outright because the UA's
   `[hidden] { display:none }` is an *author-beaten* rule, not a
   specificity-beaten one: `nav a { display:flex }` above wins whatever the
   selector weights say, so setting `.hidden` on the element left Squadron
   sitting in the bar through the whole of Paper. */
nav a[hidden] { display:none; }
/* And the whole bar's worth of it, for a page being read without a session.
   Same reason, one level up: `nav { display:flex }` is an author rule and
   beats the user agent's `[hidden] { display:none }`, so hiding these from
   script did nothing at all until this line existed. */
nav[hidden], #bookswitch[hidden], .acctlink[hidden], #statuspill[hidden],
.me[hidden] { display:none; }
nav a:hover { color:var(--text-secondary); }
nav a.on { color:var(--text); }
nav a.on::after { content:""; position:absolute; left:12px; right:12px;
                  bottom:-1px; height:2px;
                  background:linear-gradient(90deg,var(--accent),rgba(220,234,242,.35));
                  box-shadow:0 0 12px rgba(220,234,242,.45); }
.acctlink .tb-mini { display:none; }
.menu { position:relative; display:flex; }
.menubtn { display:flex; align-items:center; gap:6px; padding:7px 12px;
           border-radius:8px; font:inherit; font-size:13px; font-weight:400;
           color:var(--dim); background:none; border:0; box-shadow:none;
           cursor:pointer; white-space:nowrap;
           transition:color .15s, background .15s; }
.menubtn:hover { color:var(--text-secondary); }
.menubtn.on { background:rgba(255,255,255,.06); color:var(--text); }
.menubtn .caret { font-size:9px; opacity:.8; transition:transform .15s ease; }
.menubtn[aria-expanded="true"] .caret { transform:rotate(180deg); }
/* Fixed, not absolute. On a phone the nav is a horizontal scroll container,
   and `overflow-x:auto` clips absolutely-positioned children — the menu
   would open inside a box it cannot escape. Fixed positioning sidesteps the
   clip entirely; the position is set from the button on open. */
.menulist { position:fixed; z-index:60; min-width:172px; padding:6px;
            background:var(--surface-raised); border:1px solid var(--line-strong);
            border-radius:10px; box-shadow:0 18px 40px -12px rgba(0,0,0,.75);
            display:flex; flex-direction:column; gap:2px; }
.menulist[hidden] { display:none; }
.menulist a { padding:9px 11px; border-radius:7px; font-size:13px;
              color:var(--text-secondary); white-space:nowrap; }
.menulist a:hover { background:rgba(255,255,255,.06); color:var(--text); }
.menulist a:focus-visible { outline:2px solid var(--accent); outline-offset:-2px; }
.menulist a.on { color:var(--text); background:rgba(255,255,255,.06); }
.spacer { flex:1; }

/* ── Which book ───────────────────────────────────────────────────────────
   Two states, one control, and the choice is the app's rather than the page's
   — every page reads it and the two aggregate pages filter on it.

   A track with a lit segment, not two pills: the segment is what makes it
   read as a switch with an off side rather than as two independent buttons,
   and it is the one place in the bar where the accent is spent. Live gets a
   green cast because it is the state where a mistake costs money; paper is
   deliberately quiet. */
.bookswitch { display:flex; align-items:center; gap:2px; padding:3px;
        border-radius:999px; border:1px solid var(--line);
        background:var(--surface); margin-right:12px; flex:none; }
.bookswitch button { padding:5px 12px; border-radius:999px; border:0; background:none;
               box-shadow:none; cursor:pointer; white-space:nowrap;
               font:600 11.5px var(--font-sans); letter-spacing:-.005em;
               color:var(--dim); transition:color .15s, background .15s; }
.bookswitch button:hover { color:var(--text-secondary); }
/* The selected one is lifted, not just lighter: a fill at 8% on a panel that
   is itself near-black is a difference somebody has to look for, and this is
   the control that decides whether every figure on screen is real money. */
.bookswitch button[aria-checked="true"] { color:var(--text);
                                    background:rgba(255,255,255,.12);
                                    box-shadow:inset 0 1px 0 rgba(255,255,255,.14),
                                               0 1px 2px rgba(0,0,0,.45); }
/* Real money reads differently from a rehearsal. Only when it is the state
   you are actually in — an unselected "Live funds" is not a warning. */
.bookswitch button[data-book="live"][aria-checked="true"] {
  color:var(--green-soft); background:rgba(69,222,140,.18);
  box-shadow:inset 0 0 0 1px rgba(69,222,140,.42),
             0 1px 2px rgba(0,0,0,.45); }
.bookswitch button:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
.bookswitch .tb-mini { display:none; }

/* ── Status ───────────────────────────────────────────────────────────────
   `#updated` is the app's one status line: it carries a ticking timestamp
   most of the time, but also "could not load", "building…" and "signed out".
   The dot is driven off that same distinction rather than being decoration —
   green while the page is keeping up, red the moment it says anything else.
   `topbar.js` owns the switch, from the check it already makes for
   `aria-live`. */
.status { display:flex; align-items:center; gap:8px; padding:6px 12px;
          border-radius:999px; border:1px solid var(--line);
          background:var(--surface); font:500 10.5px var(--mono);
          color:var(--dim); white-space:nowrap; }
.status .pip { width:6px; height:6px; border-radius:50%; flex:none;
               background:var(--green); box-shadow:0 0 0 3px rgba(69,222,140,.16);
               transition:background .2s, box-shadow .2s; }
.status.stale .pip { background:var(--bad);
                     box-shadow:0 0 0 3px rgba(251,113,133,.18); }
/* Nothing to report: the pages that do not poll get no capsule rather than an
   empty one. */
.status.quiet { display:none; }

/* Sits apart from the section nav: it is where you are, not what you are
   looking at — so it takes the filled treatment the tabs gave up rather than
   the rail, which would read as a seventh section. */
.acctlink + .acctlink { margin-left:6px; }
.acctlink { margin-left:14px; padding:8px 12px; border-radius:9px;
            font-size:13px; color:var(--dim); white-space:nowrap;
            transition:color .15s, background .15s; }
.acctlink:hover { color:var(--text); background:rgba(255,255,255,.05); }
.acctlink.on { color:var(--text); background:rgba(255,255,255,.06); }

/* You, at the end of the bar.
   An initial rather than an image: nobody has uploaded one, and a generated
   identicon would be decoration standing in for information. The same
   treatment the profile page gives the same letter, so arriving there is
   continuous rather than a surprise. */
/* The way in, for a reader with no account on a page that does not need one.
   Same weight as an account control, not a hero button: they came to read a
   record, and the record is the page. */
.signin { margin-left:auto; flex:none; padding:7px 14px; border-radius:9px;
  border:1px solid var(--line-strong); background:rgba(255,255,255,.03);
  color:var(--text); font:600 12.5px var(--font-sans); letter-spacing:-.01em;
  text-decoration:none; white-space:nowrap; }
.signin:hover { border-color:var(--accent); }
.signin[hidden] { display:none; }

.me { margin-left:10px; flex:none; width:28px; height:28px; border-radius:9px;
      display:flex; align-items:center; justify-content:center;
      font:600 12px var(--font-sans); letter-spacing:-.01em; color:var(--canvas);
      background:linear-gradient(160deg,#dceaf2,#8fa6b4);
      box-shadow:inset 0 1px 0 rgba(255,255,255,.5);
      transition:transform .15s, box-shadow .15s; }
.me:hover { transform:translateY(-1px);
            box-shadow:inset 0 1px 0 rgba(255,255,255,.5),
                       0 6px 14px -6px rgba(220,234,242,.55); }
.me.on { box-shadow:inset 0 1px 0 rgba(255,255,255,.5),
                    0 0 0 2px var(--canvas), 0 0 0 3.5px var(--accent); }
/* `[hidden]` is a `display:none` from the user-agent sheet and any author
   `display` beats it — without this the avatar would hold its 28px while
   empty, on every page, before the session resolves. */
.me[hidden] { display:none; }
/* The picture fills the tile it sits in, and keeps the tile's corners. */
.me img { width:100%; height:100%; object-fit:cover; border-radius:inherit;
          display:block; }
.me img[hidden] { display:none; }

/* ── Narrow ──────────────────────────────────────────────────────────────
   The bar is one flex row, so below about 900px it simply ran off the edge:
   the brand compressed to a sliver, the timestamp wrapped onto two lines
   through the middle of Leaderboard, and the last nav item sat past the right
   edge with no way to reach it. The brand now refuses to shrink and the nav
   scrolls inside its own track, so the page itself never scrolls sideways.

   This starts at 900px, not 760px: the six sections plus a status capsule
   need the room well before phone width, and the tablet case was the one
   nobody looked at. */
@media (max-width: 900px) {
  header { padding:0 16px; gap:10px; }
  .brand { flex:0 0 auto; margin-right:0; }
  .branddiv { display:none; }
  nav { flex:1 1 auto; min-width:0; overflow-x:auto; overflow-y:hidden;
        scrollbar-width:none; -ms-overflow-style:none;
        -webkit-overflow-scrolling:touch;
        /* Fade the trailing edge so a clipped link reads as "more this way"
           rather than as the end of the list. */
        -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 22px),transparent);
        mask-image:linear-gradient(90deg,#000 calc(100% - 22px),transparent); }
  nav::-webkit-scrollbar { display:none; }
  /* 9px, not 11. The six sections wanted 478px of a 468px track — ten pixels
     short, which is the worst place to be: the last item reads as cut off
     rather than as something to scroll to. Two pixels either side of six
     items is twenty-four, so they simply fit. */
  nav a { flex:0 0 auto; white-space:nowrap; padding:0 9px; }
  .spacer { display:none; }
  /* The switch shortens rather than going: it is the only control in the bar
     that changes what every figure on the page means, so "Live"/"Paper" at
     half the width beats hiding it and leaving the reader to guess which
     book they are looking at. */
  .bookswitch { margin-right:8px; }
  .bookswitch .tb-full { display:none; }
  .bookswitch .tb-mini { display:inline; }
  .bookswitch button { padding:5px 9px; }
  /* The routine status line goes, clock and pip together.
     The clock was the first to go because nobody navigates by it, and the pip
     was kept on the reasoning that "is this page keeping up" is worth a dot.
     It is not worth it here: without its label the dot says nothing a reader
     can act on, and it was still holding forty pixels hard against the nav —
     so "Referrals" ran out under a green circle and read as broken rather
     than as scrollable. The nav takes the room.

     **Except when the page is out of date** (3 Oct 2026 audit, §6 "Error
     banner"). Hiding the routine clock is right; hiding the one sign that the
     figures on screen are stale is not — a phone is where most people read
     this, and it was the one place that could not see it. The red pip and its
     words stay, cut short rather than wrapped. */
  .status { display:none; }
  .status.stale { display:flex; flex:0 1 auto; min-width:0; max-width:38vw;
                  padding:5px 9px; }
  .status.stale #updated { min-width:0; overflow:hidden; text-overflow:ellipsis; }
}

/* ── The wordmark goes before the phone does ─────────────────────────────
   The six sections need 454px of track. With "Vaeron" beside the emblem the
   bar runs out of room at about 810px, and the wordmark used to hold on until
   760 — so between those two widths the nav overflowed and Referrals sat half
   under the edge. The emblem still gets you home, which is the same reasoning
   the phone block already gave for dropping it; it just applies 50px sooner
   than it was written to. */
@media (max-width: 810px) {
  .brand .name { display:none; }
  .brand { margin-right:0; }
}

/* ── Phone: the sections move to the bottom of the screen ────────────────
   Everything above this squeezed six sections into a row that also holds the
   brand, the book switch and three account controls. Every squeeze bought a
   little room and cost something — the wordmark, then the clock, then the
   pip — and the nav still ended up a scrollable stamp-sized track. It was the
   wrong shape for the device: a thumb reaches the bottom of a phone and not
   the top of it.

   So on a phone the sections get their own bar, fixed to the bottom edge
   where the thumb already is. The top bar keeps what says *where you are and
   who you are* — the mark, the book you are reading, History, Settings, you —
   and stops competing for width. */
@media (max-width: 760px) {
  /* The blur has to go, and not for taste: `backdrop-filter` makes an element
     a containing block for its `position:fixed` descendants, so with it on
     the header the bar below would anchor to the header instead of the
     viewport and sit under the top of the screen. Opaque rather than
     translucent — without a blur behind it, .7 alpha is just content showing
     through the letters. */
  header { padding:0 14px; height:58px;
           background:#050608;
           backdrop-filter:none; -webkit-backdrop-filter:none; }
  .brand { --emblem:26px; gap:8px; }
  /* The wordmark comes back. It went because the sections and the brand were
     fighting over one row; the sections have their own row now, so the thing
     it was bought with is no longer needed. This is a product people are
     asked to put money into and the emblem alone does not name it. */
  .brand .name { display:inline; font-size:17px; }
  /* Restored: with the nav out of the flow nothing else pushes the account
     controls to the right edge. */
  .spacer { display:block; flex:1 1 auto; }
  /* At least 30px across: History's glyph is narrower than the drawn icons
     beside it, and its link was 23px — under the 24px a tap target needs. */
  .acctlink { margin-left:2px; padding:8px 7px; min-width:30px; text-align:center; }
  .acctlink .tb-full { display:none; }
  /* Wallet and Settings are drawn rather than typed.
     Both were `&#9673;` — the same character — so on a phone, where only the
     glyph shows, the two were literally indistinguishable. A gear and a
     wallet have no dependable glyph across platforms (the nearest are emoji,
     which arrive in colour and at the wrong weight), so they are inline SVG
     that inherits `currentColor` and the link's own hover and active states.
     History keeps its glyph: a page of rows is what it already looked like. */
  .acctlink .tb-mini { display:inline; font-size:15px; line-height:1; }
  .acctlink svg.tb-mini { width:17px; height:17px; display:block;
                       fill:currentColor; }
  .bookswitch { margin-right:4px; }

  nav {
    position:fixed; left:0; right:0; bottom:0; z-index:40;
    align-self:auto; justify-content:space-around;
    /* `env()` and not a constant: on a phone with a home indicator the last
       10 points of the screen are not tappable, and a bar that ends at 0
       puts its labels under it. */
    padding:7px 4px calc(7px + env(safe-area-inset-bottom));
    border-top:1px solid rgba(255,255,255,.07);
    background:rgba(5,6,8,.86);
    backdrop-filter:blur(28px) saturate(1.4);
    -webkit-backdrop-filter:blur(28px) saturate(1.4);
    /* Undoing the 900px block, which shapes the nav as a scrolling track.
       This one holds five items across the full width and never scrolls. */
    flex:none; overflow:visible;
    -webkit-mask-image:none; mask-image:none;
    transition:transform .22s ease;
  }
  nav a { flex:1 1 0; justify-content:center; padding:8px 2px 7px;
          font-size:11px; letter-spacing:.005em; }
  /* The rail moves to the bar's top edge — the edge facing the content it is
     marking. On the top bar it sits on the bottom edge for the same reason. */
  nav a.on::after { top:-8px; bottom:auto; left:20%; right:20%; }

  /* Out of the way on the way down, back on the way up. `topbar.js` sets the
     attribute; the rule lives here so the breakpoint is stated once. */
  html[data-navaway] nav { transform:translateY(115%); }

  /* Room for the bar, so the last row of a table is not under it. */
  main { padding:18px 14px calc(78px + env(safe-area-inset-bottom)); }
}

@media (max-width: 760px) and (prefers-reduced-motion: reduce) {
  nav { transition:none; }
}

/* ── Very narrow ─────────────────────────────────────────────────────────
   The top bar is down to the mark, the book switch and three controls, and at
   360px those still meet in the middle. The switch gives up its padding
   first: it is two words, and it is the one control here that cannot be
   guessed from an icon.

   430px, not 380. At the phone rules above the bar needs 427px, so every
   common phone between them — 390 and 393 (iPhone 12 to 16), 412 and 414 —
   pushed the avatar 23px past the edge (the 19 Sep audit's L5). With these
   rules it ends inside the padding at 390. */
@media (max-width: 430px) {
  .brand .name { font-size:15.5px; }
  .brand { --emblem:23px; gap:7px; }
  .bookswitch button { padding:5px 7px; }
  .acctlink { padding:8px 5px; }
  nav a { font-size:10.5px; padding-left:0; padding-right:0; }
  /* The gap goes too, not just the padding inside each control.
     This block trimmed what was *in* the buttons and left the 10px between
     them from the 900px rule, plus the avatar's own 10px margin — six items
     and five gaps, so 60px of space on a 375px screen. The avatar ended 5px
     past the edge, which put a horizontal scrollbar on every page of the app
     on a 375px phone: an iPhone SE, a mini, and any Android at that width. */
  header { gap:6px; }
  .me { margin-left:6px; }
}


/* --- a first load that failed ------------------------------------------------
 *
 * `loadFailed()` in topbar.js, in place of a "Loading…" that would otherwise
 * wait for ever (3 Oct 2026 audit, §6 "Stuck loading"). Plain on purpose: it
 * sits inside whatever card was waiting, and says what happened and what to
 * press.
 */
.loadfail { padding:14px 16px; display:flex; flex-wrap:wrap; align-items:center;
  gap:10px 14px; font-size:13px; line-height:1.45; color:var(--text-secondary); }
.loadfail p { margin:0; flex:1 1 16em; }
.loadfail b { color:var(--text); }
.loadfail .loadretry { flex:none; min-height:32px; padding:7px 14px; border-radius:9px;
  cursor:pointer; border:1px solid var(--control-edge); background:rgba(255,255,255,.04);
  color:var(--text); font:600 12.5px var(--font-sans); box-shadow:none; }
.loadfail .loadretry:focus-visible { outline:2px solid var(--focus-ring); outline-offset:2px; }
.loadfail .loadretry:disabled { opacity:.6; cursor:default; }


/* --- a figure's definition, for every way of reaching it ---------------------
 *
 * `defined()` in ui.js makes a tile a tab stop described by its definition.
 * The definition shows inside the tile while it has focus — a tab or a tap —
 * rather than floating over the page, because the cards it sits in clip what
 * hangs outside them. A mouse still has the `title` (3 Oct 2026 audit, §6).
 */
[data-def] { cursor:help; }
[data-def]:focus-visible { outline:2px solid var(--focus-ring); outline-offset:-2px;
  border-radius:6px; }
[data-def] > .def { display:none; }
[data-def]:focus > .def, [data-def]:focus-within > .def {
  display:block; margin-top:6px; font:400 11.5px/1.45 var(--font-sans);
  letter-spacing:0; text-transform:none; white-space:normal; text-align:left;
  color:var(--text-secondary); }


/* --- the share card, full size ----------------------------------------------
 *
 * Here rather than in the two pages that open it. `trades.html` shares one
 * agent and `profile.html` shares the account; the dialog is identical, and a
 * component defined twice is the exact shape `test_css_names` exists to catch
 * — `.book` meant two things in two sheets and the second one silently won.
 *
 * It shows the card before it offers the file. Handing over a picture nobody
 * has looked at, or opening the OS share sheet on top of the page, is not what
 * a button called Share P&L should do — and the sheet in particular put a list
 * of AirDrop contacts in front of somebody who wanted to see their P&L.
 *
 * `.sharecard` is the root and owns every role inside it, so `.sheet`, `.shot`
 * and `.acts` stay short and cannot collide with a page's own names.
 */
.sharecard { position:fixed; inset:0; z-index:60; display:none;
  align-items:center; justify-content:center; padding:24px;
  background:rgba(3,5,8,.78); backdrop-filter:blur(6px); }
.sharecard.open { display:flex; }
.sharecard .sheet { width:min(920px, 100%); max-height:100%; display:flex;
  flex-direction:column; gap:14px; }
.sharecard .shot { width:100%; aspect-ratio:1200/600; border-radius:14px;
  border:1px solid var(--line-strong); background:var(--surface);
  object-fit:cover; display:block; box-shadow:0 24px 70px rgba(0,0,0,.6); }
.sharecard .shot[hidden] { display:none; }
/* The render takes about a second. Without a placeholder the dialog opens as
   an empty hole and reads as broken. */
.sharecard .wait { width:100%; aspect-ratio:1200/600; border-radius:14px;
  border:1px solid var(--line); background:var(--surface);
  display:grid; place-items:center; color:var(--dim);
  font:500 12px var(--mono); letter-spacing:.14em; text-transform:uppercase; }
.sharecard .wait[hidden] { display:none; }
.sharecard .acts { display:flex; gap:10px; justify-content:flex-end; }
.sharecard .acts button { padding:10px 18px; border-radius:10px; cursor:pointer;
  font:600 12.5px var(--font-sans); border:1px solid var(--line-strong);
  background:transparent; color:var(--text-secondary); }
.sharecard .acts button:hover { color:var(--text); background:rgba(255,255,255,.05); }
/* The primary action, in the app's own polished platinum.
 *
 * Spelled out here rather than left to the `.platinum` class it also carries:
 * `.sharecard .acts button` is a class-plus-class-plus-type selector and beats
 * a lone `.platinum`, so the button silently rendered as the secondary one and
 * Download and Close looked identical. */
.sharecard .acts button.go { color:#06080a; border-color:transparent;
  background:linear-gradient(160deg, #fff 0%, #dce4ea 45%, #b4c0c9 100%);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.9),
             inset 0 -2px 4px rgba(0,0,0,.12), 0 8px 22px rgba(0,0,0,.5); }
.sharecard .acts button.go:hover { color:#06080a;
  background:linear-gradient(160deg, #fff 0%, #e6edf2 45%, #c2ccd4 100%); }
.sharecard .acts button.go:disabled { opacity:.45; cursor:default; }
.sharecard .acts button:focus-visible { outline:2px solid var(--focus-ring);
  outline-offset:2px; }
.sharecard .err { font-size:12.5px; color:var(--warning); text-align:right; }
.sharecard .err[hidden] { display:none; }
/* Full-width controls on a phone, where they are thumb targets rather than a
   row of buttons in a corner. */
@media (max-width:560px) {
  .sharecard { padding:14px; }
  .sharecard .acts { justify-content:stretch; }
  .sharecard .acts button { flex:1; padding:13px 12px; }
}


