/* App shell: the top bar and left sidebar that every logged-in page draws (templates/_shell.html).
   Lifted out of dashboard.html when /education was added (board #58) so the two pages cannot drift.

   Board #63 (1 Sep 2026): the sidebar is now measured off FX Replay's own, pixel by pixel, from
   the screenshots on that item. Every number below has a line in SHELL_UI_SPEC.md saying which
   pixel it was read from — change one here, change it there. The FX Replay neutrals in play:
     #202123 text-primary   #61646A text-secondary   #E5E5E7 border/fill   #F1F1F3 hover */
 :root{--bg:#f6f7f9;--card:#fff;--line:#e8ebef;--txt:#1f2733;--mut:#8a94a3;--blu:#2563eb;--blu2:#eef3ff;--grn:#16b47f;--red:#e5484d;
       --fxr-txt:#202123;--fxr-txt2:#61646A;--fxr-line:#E5E5E7;--fxr-hov:#F1F1F3;
       /* Surfaces that are WHITE in light but are NOT the card: the top bar, the sidebar and the
          active nav row. They were literal #fff until board #9946 needed them to move in dark. */
       --panel:#fff;--modal:#fff;--input:#fff;--shadow:rgba(20,25,35,.06);
       /* Board #9946: values that were INLINE `style="background:#..."` in the markup, where a
          stylesheet rule cannot reach them. Declared as tokens with their exact previous light
          value, so light is byte-identical and dark has somewhere to go. */
       --fx-togrow:#f6f8fb;--fx-bartrack:#eef0f3;--fx-dot:#C4C5C7;--fx-dl:#1a7f37;
       --imp-bg:#fdeede;--imp-line:#f5d9ac;--imp-txt:#8a5a12;--imp-link:#2f6bff;
       --hint:#b6bdc7;--sub-line:#E8E8EA;--ph-bg:#eef0f3;--ph-ink:#c6c8cc}

 /* ─────────────────────────────────────────────────────────────────────────────────────────
    DARK THEME — board #9946 (Vishnu, 3 Sep 2026): "THIS DARK MODE FEATURE SHOULD ALSO WORK AS
    FX REPLAY ... EACH AND EVERYTHING SHOULD PIXEL PERFECT".

    Every value below is FX Replay's OWN dark token, taken two ways that agree exactly:
      1. their shipped stylesheet (app.fxreplay.com/en-US/styles-VQA4UWT2.css, 894KB) names each
         one — `dark:bg-fxr-bg-primary-dark`, `dark:border-fxr-border-tertiary-dark`, etc. — so
         these are the values they ship, not values read off a glyph;
      2. re-measured as the modal colour of a filled region on a 1x screenshot of their live
         dark app, logged in, at 1632x900. The two never disagreed by a single bit.

    The four that carry the whole theme, and the pixel each was confirmed at:
      #030303  ground   — page, sidebar AND top bar are ONE colour in their dark mode, unlike
                          light where the bar and sidebar are white on a #f6f7f9 ground.
      #414347  1px rule — top-bar underline (y80), sidebar right edge (x256), the nav-group
                          separator (y450) and every card outline are all this exact value.
      #131415  card     — their subscription banner, 35,277 px of solid fill.
      #FFFFFF / #B0B1B4 — primary / secondary text.
    DESIGN.md §2.7 holds the full table. Change a value here, change it there.
    To undo the whole feature: drop this block, the [data-theme=dark] blocks in the templates,
    and the tbTheme handler in _shell.html.
    ───────────────────────────────────────────────────────────────────────────────────────── */
 :root[data-theme=dark]{
   --bg:#030303;        /* fxr-bg-primary-dark                                              */
   --card:#131415;      /* fxr-bg-card-dark                                                 */
   --panel:#030303;     /* their bar + sidebar sit ON the ground in dark, not above it      */
   --modal:#202123;     /* fxr-bg-modal-dark                                                */
   --input:#313235;     /* fxr-bg-number-stepper-dark — their filled field                  */
   --line:#414347;      /* fxr-border-tertiary-dark                                         */
   --txt:#FFFFFF;       /* fxr-text-primary-dark                                            */
   --mut:#B0B1B4;       /* fxr-text-secondary-dark                                          */
   --blu:#0260FD;       /* fxr-btn-bg-primary-active-dark. DESIGN.md §2.3 says a surface
                           MEASURED against FX Replay wears their blue — the dark theme is
                           entirely that, so it does. Light keeps our #2563eb, untouched.   */
   --blu2:#01307F;      /* fxr-banner-bg-info-dark — the blue tint ground                   */
   --grn:#06A77D;       /* fxr-success-dark                                                 */
   --red:#ED6A5A;       /* fxr-text-error-dark                                              */
   --fxr-txt:#FFFFFF;
   --fxr-txt2:#B0B1B4;
   --fxr-line:#414347;  /* fxr-accent-dark — the nav pill fill AND every 1px rule           */
   --fxr-hov:#61646A;   /* fxr-btn-bg-secondary-hover-dark, off their own nav row's
                           `hover:dark:bg-fxr-btn-bg-secondary-hover-dark`                  */
   --shadow:rgba(0,0,0,.5);
   --fx-togrow:#202123;--fx-bartrack:#202123;--fx-dot:#61646A;--fx-dl:#06A77D;
   /* the impersonation banner is a warning surface: fxr-banner-bg/color-warning-dark */
   --imp-bg:#805900;--imp-line:#D49300;--imp-txt:#FFF6E2;--imp-link:#80AFFE;
   --hint:#61646A;--sub-line:#414347;--ph-bg:#202123;--ph-ink:#61646A;
   color-scheme:dark;   /* so form controls, scrollbars and autofill go dark with the page  */
 }
 *{box-sizing:border-box;margin:0;font-family:"Poppins",-apple-system,"Segoe UI",Roboto,sans-serif}
 body{background:var(--bg);color:var(--txt);font-size:14px}
 /* 257px = FX Replay's 256px of sidebar + its 1px right border (SHELL_UI_SPEC §2.1) */
 .wrap{display:grid;grid-template-columns:257px 1fr;grid-template-rows:56px 1fr;height:100vh}
 /* top bar */
 .top{grid-column:1/3;display:flex;align-items:center;gap:14px;padding:0 20px;background:var(--panel);border-bottom:1px solid var(--line)}
 /* THE WORDMARK. Board #9943 (Vishnu, 3 Sep 2026): "our wording should be Trade Replay ...
    make sure the text size should be exactly same as fx replay and distance between them also",
    and "you have to login to website ... never go with the screen shot only". So these two
    numbers were read off FX Replay's OWN live header, logged in as him at 1632x900, 1x:
      · their wordmark (assets/logos/fxreplay-white.svg, painted 128x24) inks a 16px CAP —
        "FX" x72..101 and "REPLAY" x111..199, both y32..47, at a 50%-coverage threshold.
        22px is the size that inks a 16px cap in BOTH faces this shell renders in (Lato on
        the dashboard, system-ui elsewhere) — measured, not derived from a ratio.
      · their icon sits 24px from the wordmark: the 24px sidebar button box ends at x48 and
        the wordmark box starts at x72 (nav gap-6). That is the gap below.
    SHELL_UI_SPEC §1.1. To undo: font-size:17px, drop the flex/gap, and put the bolt back in
    the same span as the word in _shell.html. */
 .logo{display:inline-flex;align-items:center;gap:24px;font-weight:800;letter-spacing:1px;font-size:22px;white-space:nowrap}
 .logo .bolt{letter-spacing:0}
 /* PHONES. "Trade Replay" is a longer string than "REPLAY" — 110px against 85px at the same
    17px — so on a 390px bar it does not fit beside the pill and the four controls whatever size
    it is set to. Measured on live at 390: the OLD "⚡ REPLAY" already wrapped to two lines in a
    56px bar (logo box 72.5x42), so this width was never right; unwrapped it would scroll 18px.
    Under FX Replay's own `sm` breakpoint they answer it by hiding the WORDMARK and keeping the
    mark — `hidden dark:sm:flex` on their <img> — so that is what this does. No wrap, no sideways
    scroll, and the phone bar is now better than it was rather than differently broken.
    Desktop — what this item is about, and the width Vishnu's screenshot is — is untouched.
    To keep the name on phones instead, drop this block and accept an ~18px sideways scroll. */
 @media (max-width:640px){ .logo{gap:0} .logo .wm{display:none} }
 .pill{padding:3px 9px;border-radius:20px;font-size:11px;font-weight:600}
 .pro{background:var(--blu2);color:var(--blu)} .free{background:#C5DAFD;color:#0250D3}
 .pro .ni{width:12px;height:12px;vertical-align:-1px}
 /* The top-bar controls. Buttons, not spans: board #9942 - as bare spans a click landed on the
    text and selected it (Vishnu's 9.25.28 shot catches EN mid-selection) instead of doing
    anything. They also inherit the sidebar's 16px line-icon family, so the bar stops mixing
    colour emoji with line art. */
 .ico{width:34px;height:34px;border-radius:8px;display:flex;align-items:center;justify-content:center;
      gap:2px;color:var(--fxr-txt2);cursor:pointer;background:none;border:none;padding:0;
      font-size:13px;font-weight:500;line-height:1;user-select:none;flex:none}
 .ico.wide{width:auto;padding:0 8px}
 .ico .ni{width:16px;height:16px}
 .ico:hover{background:var(--fxr-hov);color:var(--fxr-txt)}
 .ico:focus-visible{outline:2px solid #0260FD;outline-offset:1px}
 .ico[disabled]{opacity:.45;cursor:default}
 .ico[disabled]:hover{background:none;color:var(--fxr-txt2)}

 /* The language picker (board #9945). FX Replay hangs theirs off the EN button with
    xPosition="Before", i.e. the panel's RIGHT edge lines up with the button's, so a menu
    wider than the button grows leftwards and never runs off the top bar. hasPadding=false
    on their dropdown is why the panel has no inner padding of its own — the rows carry it.
    Every colour is a --fxr-* token, so this follows the theme #9946 built rather than
    pinning a second set of light-only values. */
 .langwrap{position:relative;flex:none}
 .langmenu{position:absolute;top:calc(100% + 6px);right:0;z-index:60;min-width:184px;
           background:var(--modal);border:1px solid var(--fxr-line);border-radius:10px;
           box-shadow:0 10px 30px var(--shadow),0 2px 6px var(--shadow);
           padding:4px;display:flex;flex-direction:column}
 .langmenu[hidden]{display:none}
 .langopt{display:block;width:100%;text-align:left;white-space:nowrap;background:none;
          border:none;border-radius:6px;cursor:pointer;padding:0 10px;height:34px;
          font:inherit;font-size:14px;line-height:34px;color:var(--fxr-txt)}
 .langopt:hover{background:var(--fxr-hov)}
 .langopt:focus-visible{outline:2px solid #0260FD;outline-offset:-2px}
 .langopt.on{background:var(--fxr-line)}
 /* No `.backb` here any more — board #9936 removed the top-bar back arrow from the shell.
    The sidebar is always on screen and reaches every shell page, so the arrow duplicated it;
    FX Replay puts theirs on the chart page only. The chart page keeps its own, inline at
    demo_kline.py:886. To bring this one back, restore the anchor in _shell.html and:
      .backb{width:34px;height:34px;border-radius:8px;display:grid;place-items:center;
             color:var(--fxr-txt);cursor:pointer;text-decoration:none;flex:none;margin-right:-4px}
      .backb:hover{background:#f1f3f6}
    DESIGN.md §6.1. */
 /* sidebar */
 .side{background:var(--panel);border-right:1px solid var(--fxr-line);padding:16px;display:flex;flex-direction:column;gap:16px}
 .prof{display:flex;flex-direction:column;align-items:center;gap:13px;padding:0 0 19px;
       border-bottom:1px solid var(--fxr-line)}
 /* R4 closes the top nav group with a second rule of the same geometry (y382):
    x25->x248, i.e. inset by the sidebar's own 16px on both sides. */
 .sep{height:0;border-top:1px solid var(--fxr-line);flex:none}
 /* 64px, not the 56px we had — Vishnu: "USER PROFILE ICON IMAGE IS SMALLER IN OUR DASHBOARD" */
 .av{width:64px;height:64px;border-radius:50%;background:#47289A;color:#fff;display:grid;place-items:center;
     font-size:26px;font-weight:700;position:relative;flex:none}
 .av .pill{position:absolute;top:-5px;right:-21px;border-radius:6px;padding:2px 7px;line-height:15px}
 .prof b{font-size:16px;font-weight:400;color:var(--fxr-txt);line-height:1}
 /* 40px row, 224px wide, 14px inset, 16px gap — the pitch in R4 is 56px (SHELL_UI_SPEC §2.3) */
 .nav{display:flex;align-items:center;gap:8px;height:40px;padding:0 14px;border-radius:12px;
      color:var(--fxr-txt);cursor:pointer;font-weight:400;font-size:16px;flex:none}
 .nav .ni{flex:none}
 .nav:hover{background:var(--fxr-hov)}
 /* R4 renders Account Settings as a filled grey pill at rest, and marks the page you are ON
    with a blue ring instead — a 2px border drawn OUTSIDE the row (its bbox is 44px tall around
    a 40px row, x23 against the row's x25) whose colour fades out left-to-right. Two different
    marks, which is the point: with the grey pill doing both jobs, /education lit up Education
    AND Account Settings and you could not tell which page you were on.
    Alphas below are read off R4's top border: x23 solid, x40 ~.85, x70 ~.12, x100 ~.02,
    gone by x130 — i.e. 0%, 7%, 21%, 34%, 47% of the 227px ring. */
 .nav.fill{background:var(--fxr-line)}
 .nav.act{background:var(--panel);position:relative}          /* R4's active row is white inside */
 .nav.act.fill{background:var(--fxr-line)}            /* ...unless it is the pill row itself */
 .nav.act::after{content:"";position:absolute;inset:-2px;border-radius:14px;padding:2px;
      background:linear-gradient(90deg,#0260FD 0%,rgba(2,96,253,.85) 7%,rgba(2,96,253,.12) 21%,
                                 rgba(2,96,253,.02) 34%,rgba(2,96,253,0) 47%);
      -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
      mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
      -webkit-mask-composite:xor;mask-composite:exclude;pointer-events:none}
 .nav.adm{color:#c47f16}
 .badge{font-size:11px;background:var(--fxr-line);color:#414347;padding:0 8px;height:20px;
        display:inline-flex;align-items:center;border-radius:10px;font-weight:600}
 .badge.new{background:var(--fxr-line);color:#414347}
 .spacer{flex:1}
 /* x=25 in R4, i.e. the 16px sidebar inset, not the 14px row inset — so no extra padding-left */
 .ver{color:var(--fxr-txt2);font-size:13px;padding:0;margin-top:-20px}
 /* Board #9948: the legal strip at the foot of the sidebar. Paddle's domain review wants the
    Terms, Privacy and Refund policies "clearly accessible via navigation"; this is that
    navigation for every logged-in page, and the login page carries the same three for a
    visitor who has no account yet. 11px and --fxr-txt2, so it reads as a footer and never
    competes with a nav row. */
 .legalnav{display:flex;flex-wrap:wrap;column-gap:6px;row-gap:2px;font-size:11px;line-height:1.6;
           color:var(--fxr-txt2);margin-top:8px}
 .legalnav a{color:inherit;text-decoration:none}
 .legalnav a:hover{color:var(--txt);text-decoration:underline}
 .legalnav i{font-style:normal;opacity:.45}
 .main{overflow-y:auto}
 /* the sidebar avatar can now hold a real uploaded picture (board #55) */
 .av img{width:100%;height:100%;border-radius:50%;object-fit:cover;display:block}

 /* ── DARK: the shell pieces a token swap alone cannot carry (board #9946) ────────────────
    Three of these would be BROKEN, not merely off, if left to the tokens:
      · .badge paints --fxr-line on --fxr-line in dark (#414347 on #414347) — invisible.
        FX Replay's own dark badge is #202123 / #B0B1B4, measured on their "Alpha" pill
        (10px/700, radius 12px) in the sidebar.
      · .free is #0250D3 on #C5DAFD, a light-on-light pill that stays light on a black bar.
        Theirs in dark: rgba(44,123,253,.2) ground, #80AFFE text (= fxr-primary-dark), and a
        2px #030303 border — read off the "Free" pill beside their avatar, box 42x23.
      · .pro would put --blu (#0260FD) on --blu2 (#01307F): their info-banner pair, too close
        to read at 11px. Same treatment as .free, which is what theirs does.
    ──────────────────────────────────────────────────────────────────────────────────────── */
 :root[data-theme=dark] .badge,
 :root[data-theme=dark] .badge.new{background:#202123;color:#B0B1B4}
 :root[data-theme=dark] .free{background:rgba(44,123,253,.2);color:#80AFFE}
 :root[data-theme=dark] .pro{background:rgba(44,123,253,.2);color:#80AFFE}
 /* fxr-text-warning-dark. #c47f16 is a light-ground gold and goes muddy on #030303. */
 :root[data-theme=dark] .nav.adm{color:#D49300}
 /* Their avatar ring reads against the bar it sits on, not against white. */
 :root[data-theme=dark] .av .pill{border:2px solid var(--bg)}
