
  /* owner 2026-10-02: app base text 18px (rem sizes scale from here; px rules unchanged) */
  html{font-size:112.5%}
  :root{
    /* Brand palette — matches the KP Stellar Astro logo (navy/gold/vermillion). */
    --bg:#f7f5f0; --card:#ffffff; --line:#e5e1d6; --line-strong:#cbc5b4;
    --primary:#2e3192; --primary-dark:#23267a; --primary-soft:#ecebf8;
    --accent:#a8401a; --gold:#d8a53a; --blue:var(--primary); --text:#1b1d33; --muted:#525770; --heading:#1e2057;
    /* --accent's own hue (coral/vermillion), ink darkened for AA small text
       on --card (C19: "hue kept, ink darkened" -- NOT a hue swap to navy).
       --accent itself measures 4.11:1 light / 2.88:1 dark on --card, under
       the 4.5:1 AA body-text floor; this is 5.16:1 light. Shared with F2's
       own accent-hued small text (coordinate token, not F3b-only). */
    --green:#1a9c4c; --amber:#e0a017; --red:#e0392b;
    --shadow:0 1px 2px rgba(23,32,79,.08), 0 2px 8px rgba(23,32,79,.06);
    --shadow-md:0 4px 16px rgba(20,22,60,.10);
    --shadow-lg:0 10px 34px rgba(20,22,60,.14);
    /* Home page + Admin Panel tiles. Previously a saturated indigo wash with
       a heavy border, which made twelve tiles read as twelve competing objects
       rather than one list. They now sit as plain cards on the page tint; the
       page background (--bg) is what separates them, not a fill of their own. */
    --tile-tint:var(--card); --tile-line:var(--line);
    --sunk-soft:#efece4;
    /* Buttons are a soft indigo wash rather than a solid navy fill: a page with
       four solid dark buttons reads as four heavy blocks whatever their size.
       The trade-off is that .btn no longer shouts, so .abtn.solid stays solid
       and remains the one treatment that marks a genuine primary action. */
    --btn-soft-line:rgba(46,49,146,.22); --btn-soft-hover:#e1e0f3;
    --faint-text:#62677f;
    /* Redesign (2026-09): a warm paper tint for the one surface on a screen
       that is "the thing you are reading" (active nav item, feature tiles, the
       ask box, the chart), and a dark gold that stays legible as TEXT on it
       (--gold itself is a fill colour and fails as small text on light).
       Three type roles: serif for titles and key statements, sans for body,
       mono for KP notation, birth details, periods and nav numbers. */
    --sheet:#fcfaf4; --gold-ink:#775400;
    --primary-hover:#23267a; --on-primary:#ffffff;
    /* Admin stat tiles in the top bar: a translucent card over the page. */
    --stat-fill:color-mix(in srgb, var(--card) 72%, var(--bg));
    --serif:"Fraunces", "Iowan Old Style", "Sitka Display", Georgia, ui-serif, serif;
    --sans:"Source Sans 3", ui-sans-serif, system-ui, "Segoe UI", Roboto, "Noto Sans", Helvetica, Arial, sans-serif;
    --mono:ui-monospace,"Cascadia Mono","Segoe UI Mono",Consolas,monospace;
    /* Small-badge good/bad TEXT. Deliberately NOT --green/--red: those are
       dual-role (also solid fills carrying white text, e.g. .tbanner/.lwopen),
       so they cannot be lightened for dark without wrecking white-on-fill.
       Measured as small text on --sunk-soft: --green/--red give 3.18/3.92 in
       light and 2.95/2.39 in dark, all under the 4.5:1 AA floor. These pass
       in both (4.85/4.86 light, 6.01/4.59 dark). */
    --dig-good:#1c7a45; --dig-bad:#b3302f;
    /* U2B F2 parity spec section 2.1: the mockup's own state colours
       (strong/likely/possible/none), hue kept from mock.css, ink darkened
       to pass AA on the soft chip background (precedence rule 6). F3b
       reuses --accent-ink for small accent-coloured text that fails AA
       (e.g. .tl-now's label) -- same token, same values, added once here
       so neither unit's merge is a no-op fight. */
    --tw-strong:#1c7a45; --tw-strong-soft:#e3f3ea;
    --tw-likely:#3149b8; --tw-likely-soft:#e9edfb;
    --tw-possible:#8a5a00; --tw-possible-soft:#fbf0d9;
    --tw-none:#5b6079; --tw-none-soft:#eeeef3;
    --tw-on-bar:#ffffff;
    --accent-ink:#a8401a;
    /* R3 ruling (architect, F2-U review): --muted on --sunk-soft fails AA
       for .tl-empty/.jc-why-sub (4.497, just under 4.5) -- F2-only token,
       hue/saturation kept, lightness 46.1->47.5. Dark already passes
       (5.35) so it just reuses --muted there, unchanged. */
    --jc-muted-sunk:#525770;
    /* R3 ruling: .jc-btn.solid in dark mode (white on --primary #6d87ff)
       measures 3.20, under AA -- a darker, same-hue fill restores it
       (5.32) without touching --primary itself or the app-wide .abtn.solid
       pair that shares it (flagged for F4, not changed here). Light keeps
       --primary unchanged (already passing). */
    --jc-btn-solid-bg:var(--primary);
    /* ONE height for every interactive control -- inputs, selects, .btn, .abtn,
       .row button, tabs. They previously ran 30/32/36px, which is invisible on
       a phone (where .btn goes full width and everything stacks) and obvious on
       desktop, where a toolbar puts three of them side by side. */
    --ctl-h:36px;
  }
  /* Dark mode. Safe to do broadly because the whole page already reads
     colors through these variables (see the block above) rather than
     hardcoded hex -- flipping the palette here recolors the large majority
     of the app for free. A genuinely exhaustive audit of every literal hex
     value on a page this size was not attempted; this covers the systemic
     surface (backgrounds/text/borders/cards) and was checked visually
     across the main panels, not every corner.
     Respects the OS preference by default; an explicit toggle (see
     setColorScheme() in app.js) can override it either way via
     documentElement[data-theme], which wins over the media query. */
  @media (prefers-color-scheme:dark){
    :root:not([data-theme="light"]){
      /* Medium dark gray, not near-black -- reported live that the first
         pass (#12141c/#181a22) read as "too dark". */
      --bg:#0e1024; --card:#1a1d3d; --line:#2c3063; --line-strong:#454a8e;
      /* --primary (#1B2A6B) is a dark navy meant for TEXT on a light page --
         .btn and most .abtn/.homebtn-style buttons read it as their text
         color, so left alone it became dark-navy-on-dark-gray: unreadable
         (reported live). It ALSO doubles as a solid FILL with white text in
         several places (.abtn.solid, active tabs, chat bubbles, tab chips)
         -- one variable serving both roles means it can't go as pale as a
         pure text color would want without white-on-fill contrast suffering.
         #6d87ff is the middle ground: clearly readable as text against
         --bg/--card, and still dark/saturated enough that white text on a
         --primary-filled button stays legible. --accent/--gold are bright/
         warm enough already to stay legible on dark and were left untouched. */
      --primary:#a3acff; --primary-dark:#c3c9ff; --accent:#ff9b73; --gold:#f0c96a; --on-primary:#10122b;
      --primary-soft:#272b61; --text:#eeecf8; --muted:#b6b8d8; --heading:#e4e2ff;
      --tile-tint:var(--card); --tile-line:var(--line); --sunk-soft:#161936;
      --btn-soft-line:rgba(255,255,255,.2); --btn-soft-hover:#2f3470; --faint-text:#a2a5c8;
      --sheet:#1f1e36; --gold-ink:#f0c96a; --primary-hover:#b8bfff;
      --dig-good:#5fd394; --dig-bad:#ff8f86;
      --tw-strong:#5fd394; --tw-strong-soft:#15311f;
      --tw-likely:#a4b5ff; --tw-likely-soft:#232a5a;
      --tw-possible:#f0c96a; --tw-possible-soft:#332c1a;
      --tw-none:#b0b3d1; --tw-none-soft:#22254a;
      --tw-on-bar:#0e1024;
      --accent-ink:#ff9b73;
      --jc-muted-sunk:var(--muted);
      --jc-btn-solid-bg:var(--primary);
      --stat-fill:color-mix(in srgb, var(--card) 60%, var(--bg));
      --shadow:0 1px 2px rgba(0,0,0,.45), 0 2px 8px rgba(0,0,0,.3);
      --shadow-md:0 4px 16px rgba(0,0,0,.4); --shadow-lg:0 10px 34px rgba(0,0,0,.45);
    }
  }
  :root[data-theme="dark"]{
    --bg:#0e1024; --card:#1a1d3d; --line:#2c3063; --line-strong:#454a8e;
    --primary:#a3acff; --primary-dark:#c3c9ff; --accent:#ff9b73; --gold:#f0c96a; --on-primary:#10122b;
    --primary-soft:#272b61; --text:#eeecf8; --muted:#b6b8d8; --heading:#e4e2ff;
    --tile-tint:var(--card); --tile-line:var(--line); --sunk-soft:#161936;
    --btn-soft-line:rgba(255,255,255,.2); --btn-soft-hover:#2f3470; --faint-text:#a2a5c8;
    --sheet:#1f1e36; --gold-ink:#f0c96a; --primary-hover:#b8bfff;
    --dig-good:#5fd394; --dig-bad:#ff8f86;
    --tw-strong:#5fd394; --tw-strong-soft:#15311f;
    --tw-likely:#a4b5ff; --tw-likely-soft:#232a5a;
    --tw-possible:#f0c96a; --tw-possible-soft:#332c1a;
    --tw-none:#b0b3d1; --tw-none-soft:#22254a;
    --tw-on-bar:#0e1024;
    --accent-ink:#ff9b73;
    --jc-muted-sunk:var(--muted);
    --jc-btn-solid-bg:var(--primary);
    --stat-fill:color-mix(in srgb, var(--card) 60%, var(--bg));
    --shadow:0 1px 2px rgba(0,0,0,.45), 0 2px 8px rgba(0,0,0,.3);
    --shadow-md:0 4px 16px rgba(0,0,0,.4); --shadow-lg:0 10px 34px rgba(0,0,0,.45);
  }

  /* --- Window Backtest panel ---------------------------------------------
     These live here, not in site.css: index.html does not load site.css at
     all (only panchang.html does), so styles put there never reach the app
     and the chart rendered as unstyled black bars. */
  .btchart{ display:block; margin:4px 0 2px; max-width:100%; }
  .btgrid{ stroke:var(--line); stroke-width:1; }
  .btgrid.mid{ stroke:var(--muted); stroke-dasharray:5 4; opacity:.75; }
  .btaxis{ fill:var(--muted); font-size:11px; font-variant-numeric:tabular-nums; }
  .btaxis.mid{ font-size:10px; letter-spacing:.04em; }
  .btlbl{ fill:var(--text); font-size:12px; }
  .btlbl.dim{ fill:var(--muted); font-size:11px; font-variant-numeric:tabular-nums; }
  .btlbl.warn{ fill:var(--amber); }
  /* Colour encodes the EDGE, not raw accuracy — see btChart() in app.js. */
  .btcol.up{ fill:var(--green); }
  .btcol.down{ fill:var(--red); }
  .btcol.flat{ fill:var(--primary); opacity:.55; }
  .btcol.ctrl{ fill:var(--line-strong); }
  .btedge{ font-size:12px; font-weight:700; font-variant-numeric:tabular-nums; }
  .btedge.up{ fill:var(--green); } .btedge.down{ fill:var(--red); }
  .btedge.flat{ fill:var(--muted); }
  .btlegend{ display:flex; gap:16px; flex-wrap:wrap; align-items:center;
    font-size:.8rem; color:var(--muted); margin:10px 0 6px; }
  .btlegend .sw{ display:inline-block; width:11px; height:11px; border-radius:3px;
    margin-right:6px; vertical-align:-1px; }
  .btlegend .sw.up{ background:var(--green); }
  .btlegend .sw.down{ background:var(--red); }
  .btlegend .sw.ctrl{ background:var(--line-strong); }
  .btlegend-note{ flex-basis:100%; font-style:italic; opacity:.85; }
  .btbar{ display:inline-block; width:84px; height:9px; border-radius:5px;
    background:var(--line); overflow:hidden; vertical-align:middle; margin-right:7px; }
  .btbar-fill{ height:100%; background:var(--primary); }
  .bt-head{ display:flex; align-items:baseline; gap:12px; margin:10px 0; }
  .bt-big{ font-size:1.9rem; font-weight:700; font-variant-numeric:tabular-nums; color:var(--heading); }
  #panel-backtest table td.n, #panel-backtest table th.n{ text-align:right;
    font-variant-numeric:tabular-nums; }
  #panel-backtest tr.dim td{ opacity:.62; }
  #panel-backtest td.pos{ color:var(--green); font-weight:600; }
  #panel-backtest td.neg{ color:var(--red); font-weight:600; }
  /* Live "today" windows. State is carried by a colour band AND a word, never
     colour alone — a red/green-only cue is unreadable to a colour-blind user
     and ambiguous on a projector. */
  /* All-instruments overview: a heading, then its table, repeated. */
  .lwsec{ margin:18px 0 0; padding-top:14px; border-top:2px solid var(--line-strong); }
  .lwsec:first-of-type{ border-top:none; }
  h4.lwhead{ margin:0 0 8px; font-size:1rem; display:flex; align-items:center; gap:8px; }
  .lwopen{ font-size:.64rem; font-weight:700; letter-spacing:.08em; color:#fff;
    background:var(--green); border-radius:20px; padding:2px 8px; }
  .lwtot{ display:flex; gap:20px; flex-wrap:wrap; align-items:center; padding:12px 14px;
    border:1px solid var(--line-strong); border-radius:8px; background:var(--primary-soft);
    margin:10px 0; font-size:.88rem; }
  .lwtot b{ font-variant-numeric:tabular-nums; }
  .lwtot .lwtotcall{ margin-left:auto; }
  @media (max-width:640px){ .lwtot .lwtotcall{ margin-left:0; } }
  /* Small INLINE button. The page's .btn is full-width by design, so it cannot
     sit beside a sentence — using it here would have stretched a "Save as PDF"
     control across the whole panel. */
  .lwbtn{ display:inline-block; width:auto; margin:0 0 0 8px; padding:4px 12px;
    font-size:.78rem; font-weight:600; color:var(--primary); background:var(--primary-soft);
    border:1px solid var(--line-strong); border-radius:6px; cursor:pointer; }
  .lwbtn:hover{ background:var(--card); border-color:var(--primary); }
  /* Date separator in the upcoming table — turns a long list into a calendar. */
  tr.lwday td{ background:var(--primary-soft) !important; font-weight:700;
    font-size:.8rem; letter-spacing:.03em; border-radius:6px; padding:6px 10px;
    border-left:none !important; }
  /* Is the chart time behind these boundaries a fact or a guess? Measured:
     a 30-minute shift in an assumed time relocates every window, zero overlap.
     That has to be visible where the times are read, not buried in a note. */
  .lwtb{ font-size:.62rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
    border-radius:20px; padding:1px 7px; margin-left:6px; white-space:nowrap; cursor:help; }
  .lwtb.ok{ color:#0f7a3d; background:#e6f6ec; border:1px solid #b6e3c6; }
  .lwtb.warn{ color:#8a5d00; background:var(--card)6e3; border:1px solid #f0d79a; }
  .lwwrap{ overflow-x:auto; }
  table.lwtable{ width:100%; border-collapse:separate; border-spacing:0 6px; }
  table.lwtable th{ text-align:left; font-size:.72rem; letter-spacing:.06em;
    text-transform:uppercase; color:#6a7086; padding:0 10px 4px; font-weight:700; }
  table.lwtable td{ padding:10px; background:var(--card); vertical-align:top;
    border-top:1px solid var(--line-strong); border-bottom:1px solid var(--line-strong); }
  table.lwtable td:first-child{ border-left:5px solid #b9bed0;
    border-top-left-radius:8px; border-bottom-left-radius:8px; }
  table.lwtable td:last-child{ border-right:1px solid var(--line-strong);
    border-top-right-radius:8px; border-bottom-right-radius:8px; }
  .lwrow .lwtime{ font-weight:700; white-space:nowrap; font-variant-numeric:tabular-nums; }
  .lwrow .lwtier{ font-weight:700; }
  .lwrow .lwwhy{ font-size:.82rem; color:#5a5f70; margin-top:3px; }
  .lwrow .lwmove{ font-weight:700; font-variant-numeric:tabular-nums; white-space:nowrap; }
  .lwrow .lwstate{ font-weight:700; white-space:nowrap; }
  /* P&L colour follows profit and loss, which is not always the row's state:
     an amber "too early" window still has a live number. */
  /* Entry/exit instructions. The verb is what the eye should land on, so it
     carries the weight and the colour; the time sits beside it in the same
     tabular figures used everywhere else in this panel. */
  .lwact{ white-space:nowrap; line-height:1.5; }
  .lwact b{ display:inline-block; min-width:6.4em; font-size:.74rem;
    letter-spacing:.04em; }
  .lwact b.bu{ color:var(--green); }
  .lwact b.sh{ color:var(--red); }
  .lwact b.ex{ color:#5a5f70; }
  table.lwtable .lwmove.up{ color:var(--green); }
  table.lwtable .lwmove.down{ color:var(--red); }
  /* Legend: the same swatch shapes the rows use, so the key is literally the
     thing being keyed rather than an approximation of it. */
  .lwkey{ display:flex; gap:16px; flex-wrap:wrap; align-items:center;
    padding:10px 12px; border:1px solid var(--line-strong); border-radius:8px;
    background:#fafbfd; margin:10px 0; font-size:.84rem; }
  .lwkey span{ display:flex; gap:7px; align-items:center; }
  .lwkey i{ width:13px; height:13px; border-radius:3px; display:inline-block;
    border-left:5px solid currentColor; background:currentColor; opacity:.9; }
  .lwrow.on_track td{ background:#f2fbf5; }
  .lwrow.on_track td:first-child{ border-left-color:var(--green); }
  .lwrow.on_track .lwstate, .lwrow.on_track .lwmove{ color:var(--green); }
  .lwrow.against td{ background:#fdf3f2; }
  .lwrow.against td:first-child{ border-left-color:var(--red); }
  .lwrow.against .lwstate, .lwrow.against .lwmove{ color:var(--red); }
  .lwrow.too_early td{ background:var(--card)af0; }
  .lwrow.too_early td:first-child{ border-left-color:var(--amber); }
  .lwrow.too_early .lwstate{ color:var(--amber); }
  /* Flat is grey, not amber: amber says "wait", and a closed window is done. */
  .lwrow.neutral td, .lwrow.no_data td, .lwrow.flat td{ background:#fafbfd; }
  .lwrow.upcoming .lwstate, .lwrow.neutral .lwstate,
  .lwrow.no_data .lwstate, .lwrow.flat .lwstate{ color:#767c90; }
  /* An open window is still provisional; the hatch says so without words. */
  .lwrow.live td{ background-image:repeating-linear-gradient(135deg,
      rgba(0,0,0,.028) 0 8px, transparent 8px 16px); }
  .lwlive{ font-size:.68rem; font-weight:700; color:var(--primary);
    background:var(--primary-soft); border-radius:20px; padding:1px 8px; margin-left:6px; }
  /* A finished window says so. Without it the only way to tell a settled
     verdict from a running one was to read the end time against the clock. */
  .lwdone{ font-size:.64rem; font-weight:700; letter-spacing:.07em; color:#6a7086;
    background:#eceef4; border-radius:20px; padding:1px 8px; margin-left:6px; }
  .lwbar{ height:5px; border-radius:3px; background:#e7e9f0; margin-top:6px; overflow:hidden; }
  .lwbar > i{ display:block; height:100%; background:var(--primary); }
  .warnchip{ font-size:.72rem; padding:1px 8px; border-radius:9px;
    background:rgba(224,160,23,.16); border:1px solid rgba(224,160,23,.5); color:#8a6412; }
  .notice{ padding:10px 13px; border-radius:8px; margin:10px 0; font-size:.9rem; }
  .notice.warn{ background:rgba(224,160,23,.12); border:1px solid rgba(224,160,23,.45); }
  *{ box-sizing:border-box; }
  /* Keyboard skip-link: off-screen until focused, then visible top-left. */
  .skip-link{ position:absolute; left:-9999px; top:0; z-index:3000; background:var(--primary); color:var(--on-primary);
    padding:10px 16px; border-radius:0 0 8px 0; font-weight:700; text-decoration:none; }
  .skip-link:focus{ left:0; }
  html{ scroll-behavior:smooth; }
  html,body{ background:var(--bg); overflow-x:hidden; max-width:100%; height:100%; }
  body{ margin:0; color:var(--text); font-family: ui-sans-serif, system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
        line-height:1.45; -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility; }
  ::-webkit-scrollbar{ width:10px; height:10px; }
  ::-webkit-scrollbar-track{ background:transparent; }
  ::-webkit-scrollbar-thumb{ background:var(--line-strong); border-radius:20px; border:2px solid var(--bg); }
  ::-webkit-scrollbar-thumb:hover{ background:#a9aec2; }
  /* ---- app shell: fixed left sidebar + independently-scrolling main column ---- */
  .app-shell{ display:flex; min-height:100vh; align-items:stretch; }
  .sidenav{ flex:none; width:224px; background:var(--card); border-right:1px solid var(--line);
            position:sticky; top:0; height:100vh; overflow-y:auto; scrollbar-width:none; -ms-overflow-style:none;
            display:flex; flex-direction:column; padding:22px 14px 14px; }
  .sidenav::-webkit-scrollbar{ display:none; }
  .sidenav .brand{ padding:0 4px 20px; display:flex; align-items:center; gap:10px; text-decoration:none; cursor:pointer; }
  .sidenav .brand:hover h1{ opacity:.85; }
  .sidenav .brand img{ width:38px; height:38px; flex:none; }
  .sidenav .brand h1{ margin:0; font-size:1.08rem; font-weight:800; letter-spacing:-.01em; line-height:1.2;
    color:var(--primary); }
  .sidenav .brand p{ margin:2px 0 0; color:var(--muted); font-size:.68rem; line-height:1.3; }
  @media (max-width:640px){ .sidenav .brand img{ width:30px; height:30px; } }
  .navlist{ display:flex; flex-direction:column; gap:3px; margin-top:4px; }
  .navlist :is(button,a){ position:relative; display:flex; align-items:center; gap:10px; text-align:left; width:100%; padding:11px 12px 11px 15px;
    background:none; color:var(--muted); border:1px solid transparent; border-radius:10px; cursor:pointer; font-weight:600;
    font-size:.9rem; transition:background .15s,color .15s,transform .12s; text-decoration:none; box-sizing:border-box; }
  /* .navlist :is(button,a){display:flex} (class+element, specificity 0,1,1) otherwise
     outranks the plain .hidden{display:none} (0,1,0), so a nav button given
     just the "hidden" class stayed visible regardless -- the same class of
     bug fixed for #ar_preview_photo above. This silently broke two things:
     tab-community's guest-hide toggle (classList.toggle('hidden', ...)), and
     any feature-flag-gated tab, which had to fall back to an inline-style
     hack in applyFeatureFlags() instead of just using .hidden. Fixed once,
     here, for every nav button rather than per-button workarounds. */
  .navlist :is(button,a).hidden{ display:none; }
  .navlist :is(button,a):hover{ background:var(--primary-soft); color:var(--primary); transform:translateX(1px); }
  .navlist :is(button,a).active{ background:var(--primary-soft); color:var(--primary); }
  /* The active rail was gold, which needs a dark ground to read. On paper the
     terracotta accent is the one that carries at 3px wide. */
  .navlist :is(button,a).active::before{ content:""; position:absolute; left:0; top:8px; bottom:8px; width:3px; border-radius:2px;
    background:var(--accent); }
  .navlist .navicon{ flex:none; width:20px; text-align:center; font-size:1rem; }
  .navlist .navicon svg{ width:18px; height:18px; vertical-align:middle; }
  .sidenav .sidefoot{ padding:6px 4px 0; font-size:.68rem; color:var(--muted); line-height:1.6; }
  /* Collapsible sidebar (desktop only — mobile already has its own overlay
     drawer via .navtoggle/nav-open, untouched below). Fixed/expanded is the
     default; a user's choice to collapse it is remembered per-browser via
     localStorage (see toggleSidenavCollapse) so it stays collapsed across
     visits once they pick that, without affecting any other visitor. */
  .sidenav-collapse-btn{ position:absolute; top:18px; right:-12px; width:24px; height:24px; border-radius:50%;
    background:var(--card); border:1px solid var(--line-strong); color:var(--primary); font-size:.8rem; cursor:pointer;
    display:flex; align-items:center; justify-content:center; box-shadow:0 2px 6px rgba(20,22,34,.25); z-index:2; }
  .sidenav-collapse-btn:hover{ background:var(--primary-soft); }
  /* Settings button pinned at the bottom-left of the nav — same visual
     language as the section buttons above it, always reachable whether the
     sidebar is expanded or collapsed (icon-only when collapsed, same as
     every nav button). */
  .sidenav-settings{ display:flex; align-items:center; gap:10px; width:100%; margin-top:10px;
    padding:11px 12px 11px 15px; background:none; color:var(--muted); border:1px solid var(--line);
    border-radius:10px; cursor:pointer; font-weight:600; font-size:.9rem; text-align:left; }
  .sidenav-settings:hover{ background:var(--primary-soft); color:var(--primary); }
  .sidenav-settings .navicon{ flex:none; width:20px; text-align:center; font-size:1rem; }
  @media (min-width:641px){
    .sidenav{ transition:width .18s ease; }
    body.sidenav-collapsed .sidenav{ width:64px; }
    body.sidenav-collapsed .sidenav .brand p,
    body.sidenav-collapsed .sidenav .brand h1,
    body.sidenav-collapsed .navlist :is(button,a) span:not(.navicon),
    body.sidenav-collapsed .navgroup-label,
    body.sidenav-collapsed .sidebar-trust,
    body.sidenav-collapsed .sidefoot,
    body.sidenav-collapsed .sidenav-settings span:not(.navicon){ display:none; }
    body.sidenav-collapsed .sidenav .brand{ justify-content:center; padding:0 0 20px; }
    body.sidenav-collapsed .sidenav .brand img{ width:30px; height:30px; }
    body.sidenav-collapsed .navlist :is(button,a),
    body.sidenav-collapsed .sidenav-settings{ justify-content:center; padding:11px 0; }
    body.sidenav-collapsed .sidenav-collapse-btn{ transform:rotate(180deg); }
    /* Hover-to-peek: while permanently collapsed, hovering the rail expands
       it as a floating overlay (doesn't reflow the page - the collapsed
       64px still reserves its layout space) and auto-collapses on
       mouseleave. The manual «/» toggle above still controls the PERSISTED
       state (localStorage) independently of this transient hover peek. */
    body.sidenav-collapsed .sidenav:hover{ width:224px; position:fixed; top:0; left:0; height:100vh;
      z-index:1500; box-shadow:var(--shadow-lg); }
    body.sidenav-collapsed .sidenav:hover .brand p,
    body.sidenav-collapsed .sidenav:hover .brand h1,
    body.sidenav-collapsed .sidenav:hover .navlist :is(button,a) span:not(.navicon),
    body.sidenav-collapsed .sidenav:hover .navgroup-label,
    body.sidenav-collapsed .sidenav:hover .sidebar-trust,
    body.sidenav-collapsed .sidenav:hover .sidefoot,
    body.sidenav-collapsed .sidenav:hover .sidenav-settings span:not(.navicon){ display:revert; }
    body.sidenav-collapsed .sidenav:hover .brand{ justify-content:flex-start; padding:0 4px 20px; }
    body.sidenav-collapsed .sidenav:hover .brand img{ width:38px; height:38px; }
    body.sidenav-collapsed .sidenav:hover .navlist :is(button,a),
    body.sidenav-collapsed .sidenav:hover .sidenav-settings{ justify-content:flex-start; padding:11px 12px 11px 15px; }
    body.sidenav-collapsed .sidenav:hover .sidenav-collapse-btn{ transform:none; }
  }
  @media (max-width:640px){ .sidenav-collapse-btn{ display:none; } }
  .langswitch{ flex:none; width:auto; padding:7px 9px; font-size:.8rem; border:1px solid var(--line-strong); border-radius:8px;
               background:var(--card); color:var(--text); cursor:pointer; }
  .langswitch:focus{ outline:none; border-color:var(--primary); box-shadow:0 0 0 3px var(--primary-soft); }
  .liveagent{ font-weight:700; }
  .themetoggle{ padding:0; width:var(--ctl-h); height:var(--ctl-h); display:inline-flex;
    align-items:center; justify-content:center; font-size:1rem; flex:none; }

  /* ---- Back-to-Home bar: a persistent, always-visible way back on every
     non-home panel. The topbar's icon-only Home button already did this,
     but a plain house glyph with no text is easy to miss entirely and the
     SPA has no URL/history support of its own — a user who doesn't spot
     the icon and reaches for the browser Back button gets nothing useful
     either, since the address bar never changes. This bar is the visible
     half of that fix; show()/popstate below wire the other half (real
     hash-based history so the browser Back button also works).

     Deliberately NOT a full-width bordered strip: sitting directly under
     the topbar and (when active) the colored announcement banner, a second
     heavy bar there read as broken layout — three stacked strips competing
     for the same sliver of space. This is sized and inset like a plain
     in-page breadcrumb instead: no border, no full-width background, just
     enough presence to be findable without adding another "bar".

     .panel-backbar-inner reuses .topbar-inner's exact max-width/margin/
     padding recipe (see the comment on .topbar-inner above) so the back
     button's left edge lines up with the Home icon and search box above it
     and the report content below it, rather than sitting flush against the
     raw viewport edge on anything wider than the 1440px content column. ---- */
  .panel-backbar{ padding-top:8px; }
  .panel-backbar.hidden{ display:none; }
  .panel-backbar-inner{ max-width:1440px; width:100%; margin:0 auto; padding:0 20px; }
  @media (max-width:640px){ .panel-backbar-inner{ padding:0 16px; } }
  /* Left edge matches where every .card's own content actually starts
     (.wrap's 20px page margin + .card's 18px internal padding = 38px),
     not the page margin alone -- otherwise Back visually floats 18px to
     the left of the heading/fields it sits above. */
  .panel-backbar-btn{ display:inline-flex; align-items:center; gap:5px; background:none; border:none;
    color:var(--muted); font-weight:600; font-size:.78rem; cursor:pointer; padding:4px 6px; margin-left:12px;
    border-radius:7px; }
  .panel-backbar-btn:hover{ color:var(--primary); background:var(--primary-soft); }

  /* ---- FAQ chat widget: minimized bubble, bottom-right ---- */
  .faqchat{ position:fixed; right:20px; bottom:20px; z-index:1600; }
  .faqchat-bubble{ width:56px; height:56px; border-radius:50%; background:linear-gradient(135deg,var(--primary),var(--primary-dark));
    color:var(--on-primary); border:none; font-size:1.5rem; box-shadow:0 8px 24px rgba(20,22,34,.25); cursor:pointer; }
  .faqchat-bubble:hover{ transform:translateY(-2px); }
  .faqchat-bubble{ position:relative; }
  .faqchat-dot{ position:absolute; top:2px; right:2px; width:12px; height:12px; border-radius:50%;
    background:var(--accent); border:2px solid #fff; }
  .faqchat-dot.hidden{ display:none; }
  .faqchat-panel{ position:absolute; right:0; bottom:68px; width:330px; max-width:calc(100vw - 32px); height:440px;
    max-height:70vh; background:var(--card); border-radius:16px; box-shadow:0 12px 34px rgba(20,22,34,.25); display:flex;
    flex-direction:column; overflow:hidden; border:1px solid var(--line); }
  .faqchat-panel.hidden{ display:none; }
  .faqchat-head{ background:linear-gradient(135deg,var(--primary),var(--primary-dark)); color:var(--on-primary); padding:12px 14px;
    display:flex; align-items:center; justify-content:space-between; font-weight:700; font-size:.9rem; flex:none; }
  .faqchat-head-actions{ display:flex; align-items:center; gap:10px; }
  .faqchat-clear{ background:none; border:none; color:var(--on-primary); font-size:.95rem; cursor:pointer; line-height:1; opacity:.85; }
  .faqchat-clear:hover{ opacity:1; }
  .faqchat-close{ background:none; border:none; color:var(--on-primary); font-size:1.3rem; cursor:pointer; line-height:1; }
  .faqchat-msgs{ flex:1; overflow-y:auto; padding:12px; display:flex; flex-direction:column; gap:8px; font-size:.85rem; }
  .faqchat-msg{ padding:8px 11px; border-radius:12px; max-width:85%; line-height:1.4; }
  .faqchat-msg.bot{ background:var(--bg); align-self:flex-start; color:var(--text); }
  .faqchat-msg.user{ background:var(--primary); color:var(--on-primary); align-self:flex-end; }
  .faqchat-msg.err{ background:#fdecea; color:#a0271e; align-self:flex-start; }
  .faqchat-topics{ display:flex; flex-wrap:wrap; gap:6px; padding:8px 10px; border-top:1px solid var(--line);
    max-height:104px; overflow-y:auto; flex:none; }
  .faqchat-chip{ padding:5px 10px; border-radius:14px; border:1px solid var(--line-strong); background:var(--card);
    font-size:.74rem; cursor:pointer; color:var(--primary); font-weight:600; }
  .faqchat-chip:hover{ background:var(--primary-soft); border-color:var(--primary); }
  .faqchat-inputrow{ display:flex; gap:6px; padding:10px; border-top:1px solid var(--line); flex:none; }
  .faqchat-inputrow input{ flex:1; padding:8px 10px; border:1px solid var(--line); border-radius:20px; font-size:.85rem; }
  .faqchat-inputrow input:focus{ outline:none; border-color:var(--primary); }
  .faqchat-inputrow button{ width:36px; height:36px; flex:none; border-radius:50%; border:none; background:var(--primary); color:var(--on-primary); cursor:pointer; font-size:1rem; }
  .faqchat-mic{ background:var(--card) !important; border:1px solid var(--line-strong); color:var(--primary) !important; }
  .faqchat-mic.listening{ background:var(--accent) !important; color:var(--on-status) !important; animation:atk-pulse 1.4s ease-out infinite; }
  @keyframes atk-pulse{
    0%{ box-shadow:0 0 0 0 rgba(217,79,43,.55); }
    70%{ box-shadow:0 0 0 5px rgba(217,79,43,0); }
    100%{ box-shadow:0 0 0 0 rgba(217,79,43,0); }
  }
  .faqchat-footrow{ text-align:center; padding:6px 10px 10px; border-top:1px solid var(--line); flex:none; }
  .faqchat-footrow a{ font-size:.74rem; color:var(--muted); text-decoration:underline; cursor:pointer; }
  .faqchat-footrow a:hover{ color:var(--primary); }
  @media (max-width:480px){ .faqchat{ right:12px; bottom:12px; } .faqchat-panel{ width:calc(100vw - 24px); } }

  /* ---- User Guide floating icon: fixed top-right, always BELOW the topbar
     (never overlapping its search box / Live Agent / language / account
     controls) since it sits outside topbar-inner's own flex row entirely. ---- */
  .userguide-fab, .verdictlegend-fab{ position:fixed; right:18px; z-index:900; width:44px; height:44px;
    border-radius:50%; background:var(--card); border:1px solid var(--line-strong); color:var(--primary);
    font-size:1.15rem; box-shadow:0 6px 18px rgba(20,22,34,.18); cursor:pointer;
    display:flex; align-items:center; justify-content:center; }
  .userguide-fab:hover, .verdictlegend-fab:hover{ transform:translateY(-2px); background:var(--primary-soft); }
  .userguide-fab{ top:76px; }
  .verdictlegend-fab{ top:130px; }
  @media (max-width:640px){
    .userguide-fab, .verdictlegend-fab{ right:12px; width:40px; height:40px; font-size:1.05rem; }
    /* Below the sticky top bar, which on a phone is one row (~52px) signed
       out and two (~100px, with search) signed in. At 96px the first button
       sat on top of the search box's right edge. */
    .userguide-fab{ top:112px; }
    .verdictlegend-fab{ top:160px; }
  }
  /* Admin stat tiles (Total hits · Total signups · Today's signups), in the
     top bar between the search box and Live Agent, admins only.
     History: speedometer gauges, then an analog odometer (both rejected as
     "not looking good"), then a grey-green LCD readout. This is the fourth
     look, approved as docs/redesign-mockup/admin-stats.html (2026-09-14):
     calm, widget-like tiles in the /app design language rather than a
     skeuomorphic instrument. Each tile is a hairline rounded rectangle with a
     translucent token fill, a small gradient glyph badge (the one place a
     vivid colour earns its keep: it tells the three numbers apart at a
     glance), the number in tabular figures with a small-caps label, and a
     7-day sparkline from /api/admin/ticker.
     The one rule kept from every earlier round: a value shows exactly its
     own digits, never zero-padded (grouping separators only).
     Cheap on purpose: plain spans and ~1KB of inline SVG per tile, rebuilt
     only on the existing 30s poll tick (see _renderStatTile /
     refreshAdminHitsInline in app.js). A number rolls up once, only when its
     value changed since the last tick; nothing moves under reduced motion.
     641–1100px folds each tile to glyph + number; phones hide it (below). */
  .admin-hits-inline{ display:flex; align-items:center; gap:6px; }
  .stat{ display:flex; align-items:center; gap:8px; height:38px; padding:0 10px 0 7px;
    background:var(--card); border:1px solid var(--line); border-radius:12px;
    transition:border-color .15s ease, transform .15s ease, box-shadow .15s ease; }
  @supports (background:color-mix(in srgb, red 50%, blue)){ .stat{ background:var(--stat-fill); } }
  @supports ((-webkit-backdrop-filter:blur(8px)) or (backdrop-filter:blur(8px))){
    .stat{ -webkit-backdrop-filter:saturate(1.4) blur(10px); backdrop-filter:saturate(1.4) blur(10px); }
  }
  .stat:hover{ border-color:var(--line-strong); transform:translateY(-1px); box-shadow:0 3px 10px rgba(20,22,34,.08); }
  .stat-glyph{ flex:none; width:22px; height:22px; border-radius:6.5px; display:grid; place-items:center; color:#fff;
    box-shadow:inset 0 0 0 .5px rgba(255,255,255,.25), 0 1px 2px rgba(20,22,34,.18); }
  .stat-glyph svg{ width:14px; height:14px; }
  .stat-glyph.g-hits{ background:linear-gradient(160deg,#4a63d8 0%,#1B2A6B 100%); }
  .stat-glyph.g-users{ background:linear-gradient(160deg,#f6cf63 0%,#d49a1a 100%); }
  .stat-glyph.g-today{ background:linear-gradient(160deg,#f4845c 0%,#c73e1e 100%); }
  .stat-body{ display:flex; flex-direction:column; justify-content:center; min-width:0; line-height:1; }
  .stat-num{ display:block; height:17px; overflow:hidden; white-space:nowrap; color:var(--text);
    font:600 16px/17px ui-sans-serif,-apple-system,"SF Pro Display",system-ui,"Segoe UI",sans-serif;
    font-variant-numeric:tabular-nums; letter-spacing:-.02em; }
  .stat-num > span{ display:block; }
  .stat-lbl{ margin-top:3px; font-size:.6rem; font-weight:600; letter-spacing:.06em; text-transform:uppercase;
    color:var(--faint-text); white-space:nowrap; }
  .stat-lbl .delta{ font-weight:500; letter-spacing:0; text-transform:none; color:var(--faint-text); }
  .stat-spark{ flex:none; width:44px; height:14px; color:var(--muted); }
  .stat-spark polyline{ fill:none; stroke:currentColor; stroke-opacity:.6; stroke-width:1.25; stroke-linecap:round; stroke-linejoin:round; }
  .stat-spark circle{ fill:currentColor; }
  .stat-num.roll > span{ animation:statRoll .25s cubic-bezier(.2,.8,.3,1); }
  @keyframes statRoll{ from{ transform:translateY(60%); opacity:0; } to{ transform:none; opacity:1; } }
  @media (prefers-reduced-motion:reduce){
    .stat, .stat:hover{ transition:none; transform:none; }
    .stat-num.roll > span{ animation:none; }
  }
  /* Measured, not guessed: an admin's second bar row (tiles, Live Agent,
     theme, language, bell, Admin Panel, account) needs ~1,080px beside the
     sidebar with full tiles, which a 1366px window has and a 1280px one does
     not. So full tiles from 1360px, glyph + number below that; and on desktop
     the Admin Panel button takes chip size and the ADMIN badge is dropped
     (the Admin Panel button already says it), so the bar is no taller than
     the LCD readout's. */
  @media (min-width:641px) and (max-width:1359px){
    .stat{ height:34px; gap:7px; padding:0 9px 0 7px; }
    .stat-lbl, .stat-spark{ display:none; }
  }
  @media (min-width:1360px){
    .admin-hits-inline{ gap:5px; }
    .stat{ gap:6px; padding:0 8px 0 6px; }
    .stat-spark{ width:34px; }
  }
  @media (min-width:641px){
    .topbar .acctbar .abtn.solid.adminpanel-btn{ height:30px; padding:0 12px; border-radius:99px; font-size:.78rem; }
    .topbar .acctbar .badge-admin{ display:none; }
    .topbar .langswitch{ max-width:92px; }
  }
  /* The right-edge buttons start below the top bar, whatever its height.
     They were pinned at fixed offsets, so an admin's bar (which wraps to two
     or three rows on a laptop or tablet) sat underneath them. --topbar-h is
     the bar's measured height, kept current by a ResizeObserver in app.js;
     max() keeps the original offsets as the floor. */
  @media (min-width:641px){
    .userguide-fab{ top:max(76px, calc(var(--topbar-h, 0px) + 14px)); }
    .verdictlegend-fab{ top:max(130px, calc(var(--topbar-h, 0px) + 68px)); }
  }

  /* Home "Coming up in your family" (BI-C): dasha changes due in 60 days on the
     saved birth charts, inside the Now running card. */
  .nr-up{ margin:0 0 10px; }
  .nr-up-list{ list-style:none; margin:6px 0 0; padding:0; }
  .nr-up-row{ display:block; width:100%; text-align:left; font:inherit; font-size:.86rem; color:var(--text);
    background:none; border:0; border-top:1px solid var(--line); padding:7px 0; cursor:pointer; }
  .nr-up-list li:first-child .nr-up-row{ border-top:0; }
  .nr-up-row:hover{ color:var(--primary); }
  .nr-up-row b{ font-weight:600; }
  @media (max-width:700px){ .nr-up-row{ min-height:44px; } }
  .main-col{ flex:1; min-width:0; display:flex; flex-direction:column; }
  /* Sticky: the top bar (menu, Home, search, account) stays put while the
     panel scrolls under it, in the browser and the installed app alike.
     z-index sits above page content (the search dropdown is 80 inside this
     bar's own stacking context) and below the mobile nav drawer (1500) and
     modals (2000), so nothing that should cover it is covered by it. */
  .topbar{ background:var(--card); border-bottom:1px solid var(--line); position:sticky; top:0; z-index:70; min-height:22px; }
  /* Same max-width + side padding as .wrap below, so the search bar's left
     edge and overall column line up exactly with the prediction/report
     content underneath instead of floating independently against the
     sidebar. */
  .topbar-inner{ max-width:1440px; width:100%; margin:0 auto; padding:14px 20px;
           display:flex; align-items:center; gap:16px; flex-wrap:wrap; }
  .topsearch-wrap{ position:relative; flex:1 1 0; min-width:200px; max-width:520px; }
  .topsearch-icon{ position:absolute; left:14px; top:50%; transform:translateY(-50%); color:var(--muted); pointer-events:none; }
  .topsearch-wrap input{ width:100%; padding:10px 14px 10px 38px; background:var(--bg); border:1px solid var(--line-strong);
                          border-radius:10px; font-size:.88rem; transition:.15s; }
  .topsearch-wrap input:hover{ border-color:var(--line-strong); background:var(--card); }
  .topsearch-wrap input:focus{ outline:none; border-color:var(--primary); background:var(--card); box-shadow:0 0 0 3px var(--primary-soft); }
  .topsearch-out{ position:absolute; left:0; right:0; top:calc(100% + 6px); z-index:80; background:var(--card);
                  border:1px solid var(--line-strong); border-radius:12px; box-shadow:var(--shadow); max-height:360px; overflow-y:auto; }
  .topsearch-out:empty{ display:none; }
  .topsearch-out .muted, .topsearch-out .err{ padding:10px 12px; font-size:.82rem; }
  .topbar-right{ margin-left:auto; display:flex; align-items:center; gap:10px; flex-wrap:wrap; justify-content:flex-end; }
  /* App-bar layout on mobile, not a shrunk desktop row: hamburger + Home
     pinned to the left edge, the action cluster pinned to the right (via
     .topbar-right's own margin-left:auto, unchanged), search dropping to
     its own full-width row underneath. `justify-content:center` used to
     wrap all of this into one centered, ragged blob -- hamburger, Home,
     Live Agent, theme, language, notifications, Admin Panel all bunched in
     the middle instead of reading as a normal app toolbar. */
  /* Phones: the bar is sticky now, so every row it wraps onto is screen height
     lost on every panel. The action cluster used to take a full-width row of
     its own; it now shares the first row with the menu and Home buttons, with
     its text labels (Live Agent, Admin Panel, the account name) folded to
     icons. Only the signed-in search keeps a second row.
     Every selector is scoped under .topbar on purpose: the base .acctbar and
     .abtn rules come LATER in this stylesheet, and at equal specificity they
     silently won -- the first version of this block did nothing on a phone. */
  @media (max-width:640px){
    .topbar .topbar-inner{ justify-content:flex-start; flex-wrap:wrap; gap:6px; padding:8px 10px; }
    .topbar .topsearch-wrap{ order:3; flex-basis:100%; max-width:none; }
    .topbar .navtoggle, .topbar .homebtn, .topbar .themetoggle{ width:36px; height:36px; }
    .topbar .topbar-right{ margin-left:auto; width:auto; min-width:0; flex-wrap:nowrap; gap:4px; justify-content:flex-end; }
    .topbar .admin-hits-inline{ display:none; }
    .topbar .topbar-right .abtn{ height:36px; }
    .topbar .liveagent, .topbar .adminpanel-btn, .topbar .acctbar [onclick^="openSupportModal"]{
      font-size:0; gap:0; padding:0; width:36px; min-width:36px; }
    .topbar .liveagent::before{ content:"🎧"; font-size:1rem; }
    .topbar .adminpanel-btn::before{ content:"🛠"; font-size:1rem; }
    .topbar .acctbar [onclick^="openSupportModal"]::before{ content:"🙏"; font-size:1rem; }
    .topbar .langswitch{ max-width:80px; height:36px; padding:0 4px; }
    .topbar .acctbar{ flex-wrap:nowrap; gap:4px; }
    .topbar .acctbar .who{ display:none; }
    .topbar .acctbar .abtn.solid{ padding:0 12px; }
    /* Signed in: the account button printed the full name or phone number
       beside the avatar and pushed the whole cluster onto a second row. The
       avatar alone opens the same menu, whose header still names the account;
       the PREMIUM/ADMIN badge repeats what that menu already says. */
    .topbar .who-btn .who-label, .topbar .who-btn .caret, .topbar .acctbar .badge-admin{ display:none; }
    .topbar .who-btn{ padding:0 3px; height:36px; }
    .topbar .acctbar .credits{ white-space:nowrap; height:36px; padding:0 8px; }
  }
  .sitefooter{ margin-top:auto; padding:22px 20px; border-top:1px solid var(--line); background:var(--bg); }
  /* Grid, not flex-wrap -- explicit correction, 2026-09-08: flex-wrap +
     space-between left the social row orphaned on the left with dead
     space to its right whenever it wrapped onto its own line (there was
     only one item on that line, so space-between had nothing to
     distribute it against). A 3-column grid pins brand/links/social to
     fixed slots that hold their position regardless of content width,
     and the copyright row is a deliberate second grid row, visually
     separated by its own divider rather than just trailing whitespace. */
  .sitefooter-inner{ max-width:1440px; margin:0 auto; display:grid;
    grid-template-columns:auto 1fr auto; align-items:center; column-gap:32px; row-gap:14px; }
  .sitefooter-brand{ grid-column:1; display:flex; align-items:center; gap:8px; font-weight:700; color:var(--primary); font-size:.85rem; }
  .sitefooter-brand img{ width:20px; height:20px; }
  .sitefooter-links{ grid-column:2; display:flex; justify-content:center; gap:18px; flex-wrap:wrap; }
  .sitefooter-links a{ color:var(--muted); font-size:.82rem; text-decoration:none; white-space:nowrap; }
  .sitefooter-links a:hover{ color:var(--primary); text-decoration:underline; }
  /* The "More" disclosure sits inline with the other footer links (not a
     block/full-width dropdown) — summary styled to match its siblings so
     it reads as "one more footer link" that happens to expand, and its
     own links appear right after it, in the same flex-wrapped line/row,
     once opened. */
  .sitefooter-more{ display:inline-flex; align-items:center; }
  /* Closed: sits as one ordinary pill among its <a> siblings. Open: takes
     the whole flex row (its parent is display:flex; flex-wrap:wrap), so
     the expanded link list gets a full-width line of its own below the
     always-visible links, instead of being squeezed into whatever inline
     space was left. */
  .sitefooter-more[open]{ flex-basis:100%; }
  .sitefooter-more summary{ display:inline; color:var(--muted); font-size:.82rem; cursor:pointer;
    list-style:none; }
  .sitefooter-more summary::-webkit-details-marker{ display:none; }
  .sitefooter-more summary::after{ content:" \25be"; }
  .sitefooter-more[open] summary::after{ content:" \25b4"; }
  .sitefooter-more summary:hover{ color:var(--primary); text-decoration:underline; }
  .sitefooter-more-links{ display:flex; flex-wrap:wrap; gap:18px; margin-top:10px; }
  .sitefooter-more-links a{ color:var(--muted); font-size:.82rem; text-decoration:none; white-space:nowrap; }
  .sitefooter-more-links a:hover{ color:var(--primary); text-decoration:underline; }
  .sitefooter-social{ grid-column:3; display:flex; align-items:center; gap:20px; flex-wrap:wrap; justify-self:end; }
  .sitefooter-copy{ grid-column:1 / -1; margin:0; padding-top:14px; border-top:1px solid var(--line);
    color:var(--muted); font-size:.76rem; line-height:1.5; text-align:center; }
  @media (max-width:900px){
    .sitefooter-inner{ grid-template-columns:1fr; justify-items:center; text-align:center; row-gap:12px; }
    .sitefooter-brand, .sitefooter-links, .sitefooter-social{ grid-column:1; justify-self:center; }
  }
  .footlink{ color:var(--primary); font-weight:600; text-decoration:none; }
  .footlink:hover{ text-decoration:underline; }
  /* ---- "Share this site" — public, no sign-in needed ---- */
  .sharesite{ display:flex; align-items:center; gap:10px; }
  .sharesite-label{ color:var(--muted); font-size:.78rem; }
  .sharebtn{ width:34px; height:34px; border-radius:50%; display:flex; align-items:center; justify-content:center;
    border:1.5px solid var(--line-strong); background:var(--card); cursor:pointer; color:var(--muted); position:relative; }
  .sharebtn:hover{ background:var(--sunk-soft); border-color:var(--primary); }
  .sharebtn.fb{ border-color:#1877F2; color:#1877F2; }
  .sharebtn.x{ border-color:#111; color:#111; }
  .sharebtn.pin{ border-color:#E60023; color:#E60023; }
  /* Instagram's mark is a gradient; a single brand pink reads correctly at
     16px and keeps the row consistent with its neighbours. An <a> here, not
     a <button>, so it is a real link — middle-click, open-in-new-tab and
     "copy link address" all work, which a JS-only button breaks. */
  .sharebtn.ig{ border-color:#E4405F; color:#E4405F; text-decoration:none; }
  .sharebtn svg{ width:16px; height:16px; fill:currentColor; }
  .announce-banner{ display:flex; align-items:center; gap:10px; padding:10px 0; font-weight:600; overflow:hidden; }
  .announce-banner.hidden{ display:none; }
  .announce-marquee{ flex:1; overflow:hidden; white-space:nowrap; position:relative; padding-left:16px; min-width:0; }
  .announce-marquee span{ display:inline-block; padding-left:100%; animation:announce-scroll 18s linear infinite; }
  .announce-marquee:hover span{ animation-play-state:paused; }
  @keyframes announce-scroll{ 0%{ transform:translateX(0); } 100%{ transform:translateX(-100%); } }
  @media (prefers-reduced-motion:reduce){ .announce-marquee span{ animation:none; padding-left:20px; } }
  .announce-banner .close{ margin-right:16px; cursor:pointer; opacity:.7; font-weight:400; flex:none; }
  .announce-banner .close:hover{ opacity:1; }
  .announce-banner.lvl-info{ background:#eef1f8; color:var(--primary-dark,#141f52); border-bottom:1px solid #d4dbf0; }
  .announce-banner.lvl-warning{ background:#fdf6e8; color:#7a5a10; border-bottom:1px solid #e8d9ab; }
  .announce-banner.lvl-critical{ background:#fdeceb; color:#9c2b1f; border-bottom:1px solid #f3c3bd; }
  .astro-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(180px,1fr)); gap:14px; margin-top:16px; }
  .astro-card{ background:var(--card); border:1px solid var(--line); border-radius:14px; padding:18px 14px; text-align:center;
    cursor:pointer; transition:.15s; }
  .astro-card:hover{ border-color:var(--primary); box-shadow:var(--shadow); transform:translateY(-1px); }
  .astro-card img{ width:84px; height:84px; border-radius:50%; object-fit:cover; margin:0 auto 10px; display:block; border:1px solid var(--line-strong); }
  .astro-card b{ display:block; color:var(--heading); font-size:.95rem; }
  .astro-card .muted{ font-size:.8rem; margin-top:2px; }
  .astro-fees{ color:var(--primary); font-weight:700; font-size:.82rem; margin-top:6px; }
  .astro-verified{ color:var(--green,#1a9c4c); font-size:.72rem; font-weight:700; vertical-align:middle; }
  .astro-kp-verified{ color:var(--primary); font-size:.72rem; font-weight:700; margin-top:4px; }
  .astro-online-dot{ position:absolute; top:10px; right:10px; font-size:.7rem; font-weight:700;
    color:#1a9c4c; background:var(--card); padding:2px 7px; border-radius:999px; border:1px solid var(--line); }
  .astro-card{ position:relative; }
  .astro-spec-tags{ display:flex; flex-wrap:wrap; justify-content:center; gap:4px; margin-top:8px; }
  .astro-spec-tags span{ font-size:.68rem; padding:2px 7px; border-radius:999px; background:var(--panel,#f3f4f8);
    color:var(--muted); border:1px solid var(--line); }
  .astro-filter-row{ display:flex; flex-wrap:wrap; align-items:center; gap:12px; margin:14px 0 0; }
  .astro-filter-row select{ padding:8px 10px; border-radius:8px; border:1px solid var(--line-strong);
    background:var(--card); color:var(--text); font:inherit; font-size:.85rem; }
  .astro-filter-online{ display:flex; align-items:center; gap:6px; font-size:.85rem; cursor:pointer; }
  .astro-detail-photo{ width:120px; height:120px; border-radius:50%; object-fit:cover; margin:0 auto 14px; display:block; border:1px solid var(--line-strong); }
  .astro-detail-row{ display:flex; justify-content:space-between; gap:10px; padding:8px 0; border-bottom:1px solid var(--line); font-size:.9rem; text-align:left; }
  .astro-detail-row:last-child{ border-bottom:none; }
  .astro-detail-row .k{ color:var(--muted); }
  /* Astrologer registration -- a two-column onboarding layout: the form on
     the left, a live preview of the actual directory tile on the right, so
     signing up feels like building a real listing rather than filling in a
     bare form. Stacks to one column on narrow screens, preview on top. */
  .ar-wrap{ display:flex; gap:22px; align-items:flex-start; flex-wrap:wrap-reverse; }
  .ar-form{ flex:2 1 380px; min-width:0; }
  .ar-preview-col{ flex:1 1 220px; min-width:220px; position:sticky; top:16px; }
  .ar-preview-label{ font-size:.72rem; font-weight:700; letter-spacing:.04em; text-transform:uppercase;
    color:var(--muted); margin:0 0 8px 2px; }
  .ar-section-title{ margin:22px 0 2px; padding-top:16px; border-top:1px solid var(--line);
    font-size:.95rem; font-weight:700; color:var(--heading); }
  .ar-section-title:first-child{ margin-top:0; padding-top:0; border-top:none; }
  .ar-section-sub{ margin:2px 0 14px; font-size:.82rem; color:var(--muted); }
  .ar-grid{ display:grid; grid-template-columns:1fr 1fr; gap:0 14px; }
  .ar-grid > div{ min-width:0; }
  .ar-grid .full{ grid-column:1/-1; }
  @media (max-width:560px){ .ar-grid{ grid-template-columns:1fr; } }
  /* Circular avatar picker: the native file input is visually hidden and
     re-triggered by clicking the styled circle, so this reads as a proper
     profile-photo control instead of a bare browse-file field. */
  .ar-avatar-picker{ display:flex; align-items:center; gap:16px; margin:4px 0 6px; }
  .ar-avatar-circle{ position:relative; width:88px; height:88px; border-radius:50%; flex:none;
    background:var(--panel,#f3f4f8); border:2px dashed var(--line-strong); cursor:pointer;
    display:flex; align-items:center; justify-content:center; overflow:hidden; transition:.15s; }
  .ar-avatar-circle:hover{ border-color:var(--primary); }
  .ar-avatar-circle img{ width:100%; height:100%; object-fit:cover; }
  .ar-avatar-circle .ar-avatar-ico{ color:var(--muted); font-size:1.5rem; }
  .ar-avatar-badge{ position:absolute; right:-2px; bottom:-2px; width:26px; height:26px; border-radius:50%;
    background:var(--primary); color:var(--on-primary); display:flex; align-items:center; justify-content:center;
    font-size:.8rem; border:2px solid var(--card); }
  .ar-avatar-hint{ font-size:.8rem; color:var(--muted); }
  .ar-avatar-hint b{ display:block; color:var(--heading); font-size:.85rem; font-weight:700; margin-bottom:2px; }
  #ar_photo_file{ display:none; }
  /* KP specialization multi-select: plain checkbox chips, not a native
     multi-select (unreadable on mobile and gives no room for the label). */
  .ar-spec-grid{ display:flex; flex-wrap:wrap; gap:8px; margin:4px 0 6px; }
  .ar-spec-chip{ display:inline-flex; align-items:center; gap:6px; padding:7px 12px; border-radius:999px;
    border:1px solid var(--line-strong); font-size:.82rem; cursor:pointer; user-select:none; transition:.15s; }
  .ar-spec-chip:hover{ border-color:var(--primary); }
  .ar-spec-chip.checked{ background:var(--primary-soft); border-color:var(--primary); color:var(--primary); font-weight:600; }
  .ar-spec-chip{ position:relative; }
  .ar-spec-chip > input[type=checkbox]{ position:absolute; opacity:0; width:1px; height:1px; margin:0; pointer-events:none; }
  .ar-spec-chip:focus-within{ outline:2px solid var(--primary); outline-offset:2px; }
  /* ID document box: rectangular (not circular like the profile photo,
     since a document photo/PDF thumbnail isn't a face crop). */
  .ar-doc-box{ position:relative; width:88px; height:88px; border-radius:10px; flex:none;
    background:var(--panel,#f3f4f8); border:2px dashed var(--line-strong); cursor:pointer;
    display:flex; align-items:center; justify-content:center; overflow:hidden; transition:.15s; }
  .ar-doc-box:hover{ border-color:var(--primary); }
  .ar-doc-box img{ width:100%; height:100%; object-fit:cover; }
  .ar-doc-box .ar-avatar-ico{ color:var(--muted); font-size:1.5rem; }
  #ar_doc_file{ display:none; }
  .ar-submit-row{ display:flex; align-items:center; gap:14px; margin-top:18px; padding-top:16px; border-top:1px solid var(--line); }
  .ar-trust{ font-size:.78rem; color:var(--muted); display:flex; align-items:center; gap:6px; }
  .ar-preview-photo-placeholder{ width:84px; height:84px; border-radius:50%; margin:0 auto 10px;
    background:var(--panel,#f3f4f8); display:flex; align-items:center; justify-content:center; font-size:1.6rem; }
  /* .astro-card img{display:block} (element+class) otherwise outranks the
     plain .hidden{display:none} (single class) on specificity, so the
     empty preview <img> stayed visible -- a broken-image sliver sitting
     behind the placeholder circle -- even with .hidden applied. */
  .astro-card img#ar_preview_photo.hidden{ display:none; }
  .adminpanel-btn{ font-weight:700; }
  /* Security console — blocked/suspicious IP rows, monospace for scanability */
  .secconsole{ display:flex; flex-direction:column; gap:6px; max-height:50vh; overflow-y:auto; }
  .secrow{ display:flex; align-items:center; gap:10px; padding:8px 10px; border-radius:8px; background:#141926;
    font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:.82rem; color:#d7dbe6; flex-wrap:wrap; }
  .secrow .sec-badge{ flex:none; font-weight:800; letter-spacing:.02em; padding:2px 8px; border-radius:6px; font-size:.72rem; }
  .secrow .sec-badge.blocked{ background:#4a1414; color:#ff8a80; }
  .secrow .sec-badge.suspicious{ background:#4a3a14; color:#ffcf80; }
  .secrow .sec-id{ font-weight:700; color:#fff; }
  .secrow .sec-meta{ color:#8a92aa; flex:1; min-width:120px; }
  .secrow .abtn{ flex:none; }
  .seclocbar-row{ display:flex; align-items:center; gap:10px; padding:4px 0; font-size:.85rem; }
  .seclocbar-label{ flex:0 0 200px; color:var(--text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .seclocbar-track{ flex:1; background:var(--line); border-radius:6px; height:14px; overflow:hidden; }
  .seclocbar-fill{ height:100%; background:linear-gradient(90deg,var(--accent),#b9331b); border-radius:6px; }
  .seclocbar-count{ flex:0 0 32px; text-align:right; font-weight:700; color:var(--heading); font-size:.8rem; }
  /* Shown when one message held more than one question — a horary chart
     answers ONE question drawn against ONE number, so the extras are
     called out rather than silently dropped. */
  .multiq-note{ background:var(--card)8e6; border:1px solid var(--gold); border-left:4px solid var(--gold);
    border-radius:8px; padding:10px 12px; margin:8px 0; font-size:.86rem; line-height:1.45; color:var(--text); }
  /* A "you asked WHERE" note isn't a warning — the chart answered, it just
     can't return a place name. Indigo/informational, and it carries the
     button that takes the querent straight to the AI Explanation with the
     place question already filled in. */
  .multiq-note.info{ background:var(--tile-tint); border-color:var(--tile-line);
    border-left-color:var(--primary); }
  .multiq-note .note-cta{ display:inline-block; margin-top:8px; padding:6px 12px;
    border:1px solid var(--primary); border-radius:999px; background:var(--primary);
    color:var(--on-primary); font-size:.8rem; font-weight:600; cursor:pointer; }
  .multiq-note .note-cta:hover{ filter:brightness(1.08); }
  .adminpanel-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:14px; margin-top:14px; }
  @media (max-width:900px){ .adminpanel-grid{ grid-template-columns:1fr 1fr; } }
  @media (max-width:520px){ .adminpanel-grid{ grid-template-columns:1fr; } }
  /* Every tool opened FROM the Admin Panel grid (AI Settings, Pricing,
     Analytics, ...) is a single settings-style .card that otherwise
     stretches to .wrap's full 1440px — on a wide monitor that reads as one
     giant edge-to-edge form, unlike the Home page (whose tile GRID divides
     the same width into cards and so never looks stretched). Capping it
     here gives every admin tool the same, consistently sized "window"
     regardless of which one you open. */
  .admin-window{ max-width:1040px; margin:0 auto; }

  /* Home page tool tiles — same idea as .adminpanel-item but with a colored
     icon badge per tool (cycling the brand palette + a few complementary
     hues) instead of a flat emoji, so the grid reads at a glance instead of
     everything looking the same weight. */
  .hometile-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:14px; margin-top:14px; }
  @media (max-width:900px){ .hometile-grid{ grid-template-columns:1fr 1fr; } }
  @media (max-width:520px){ .hometile-grid{ grid-template-columns:1fr; } }
  .hometile{ display:flex; flex-direction:column; align-items:flex-start; gap:8px; text-align:left;
    background:var(--tile-tint); border:1px solid var(--tile-line); border-radius:14px; padding:16px; cursor:pointer;
    transition:transform .14s ease, box-shadow .14s ease, border-color .14s ease; font-family:inherit; }
  .hometile:hover{ transform:translateY(-2px); box-shadow:var(--shadow-md); border-color:var(--primary); }
  .hometile .ht-ico{ display:flex; align-items:center; justify-content:center; width:44px; height:44px;
    border-radius:12px; flex:none; }
  .hometile .ht-ico svg{ width:22px; height:22px; }
  .hometile .ht-label{ font-weight:800; color:var(--heading); font-size:1rem; }
  .hometile.ht-feature{ grid-column:span 2; }
  @media (max-width:640px){ .hometile.ht-feature{ grid-column:span 1; } }
  .hometile .ht-desc{ color:var(--muted); font-size:.84rem; line-height:1.4;
    display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
  a.hometile{ text-decoration:none; }
  .ht-ico.c1{ background:#eef1f9; color:var(--primary); }
  .ht-ico.c2{ background:#fdeee9; color:var(--accent); }
  .ht-ico.c3{ background:#fdf3e0; color:#b9860f; }
  .ht-ico.c4{ background:#e9f7ef; color:#1a9c4c; }
  .ht-ico.c5{ background:#eaf3ff; color:#1668c7; }
  .ht-ico.c6{ background:#f3ecfb; color:#7c3aed; }
  .ht-ico.c7{ background:#fdeee9; color:var(--accent); }
  .ht-ico.c8{ background:#eef1f9; color:var(--primary); }
  .ht-ico.c9{ background:#fdf3e0; color:#b9860f; }
  .ht-ico.c10{ background:#e9f7ef; color:#1a9c4c; }
  .ht-ico.c11{ background:#eaf3ff; color:#1668c7; }
  /* Sized to match .hometile exactly (same padding, icon box, label size,
     description clamp) — they were never off by a token the user could
     name, but longer admin descriptions ("Real questions that found no
     proper domain...") wrapped to an extra line with nothing capping it,
     so those specific cards grew taller than every Home tile next to them.
     Clamping the description to 2 lines makes every card the same height
     regardless of how long its text is. */
  .adminpanel-item{ display:flex; flex-direction:column; align-items:flex-start; gap:8px; text-align:left;
    padding:18px; border:1.5px solid var(--tile-line); border-radius:14px; background:var(--tile-tint); cursor:pointer;
    transition:transform .14s ease, box-shadow .14s ease, border-color .14s ease; font-family:inherit; }
  .adminpanel-item:hover{ transform:translateY(-2px); border-color:var(--primary); box-shadow:var(--shadow-md); }
  .adminpanel-item .mi{ display:flex; align-items:center; justify-content:center; width:44px; height:44px;
    border-radius:12px; font-size:1.2rem; flex:none; }
  .adminpanel-item > span:nth-child(2){ font-weight:800; color:var(--heading); font-size:1rem; }
  .adminpanel-item .muted{ font-size:.84rem; line-height:1.4;
    display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
  /* Same rotating color-badge palette as the Home page's .ht-ico, so the
     Admin Panel reads with the same elegance instead of flat white icons. */
  .adminpanel-item .mi.c1{ background:#eef1f9; color:var(--primary); }
  .adminpanel-item .mi.c2{ background:#fdeee9; color:var(--accent); }
  .adminpanel-item .mi.c3{ background:#fdf3e0; color:#b9860f; }
  .adminpanel-item .mi.c4{ background:#e9f7ef; color:#1a9c4c; }
  .adminpanel-item .mi.c5{ background:#eaf3ff; color:#1668c7; }
  .adminpanel-item .mi.c6{ background:#f3ecfb; color:#7c3aed; }
  .adminpanel-item .mi.c7{ background:#fdeee9; color:var(--accent); }
  .adminpanel-item .mi.c8{ background:#eef1f9; color:var(--primary); }
  .adminpanel-item .mi.c9{ background:#fdf3e0; color:#b9860f; }
  .adminpanel-item .mi.c10{ background:#e9f7ef; color:#1a9c4c; }
  .pill{ display:inline-block; padding:2px 9px; border-radius:999px; font-size:.78rem; font-weight:700; white-space:nowrap; }
  .pill-ok{ background:#e9f7ef; color:#1a9c4c; }
  .pill-bad{ background:#fdeee9; color:#c93a2b; }
  .switch{ position:relative; display:inline-block; width:44px; height:24px; flex:none; }
  .switch input{ opacity:0; width:0; height:0; }
  .switch-track{ position:absolute; inset:0; background:#ccced4; border-radius:24px; cursor:pointer; transition:.15s; }
  .switch-track::before{ content:""; position:absolute; width:18px; height:18px; left:3px; top:3px; background:#fff;
    border-radius:50%; transition:.15s; box-shadow:0 1px 3px rgba(0,0,0,.3); }
  .switch input:checked + .switch-track{ background:var(--primary); }
  .switch input:checked + .switch-track::before{ transform:translateX(20px); }
  .switch input:disabled + .switch-track{ opacity:.5; cursor:default; }

  .pg-bad b{ color:#b3341f; }
  .pg-good b{ color:#1a7f4b; }

  /* Transit-check verdict badges. Two states on purpose: a green
     "CONFIRMED" must not be shown when the scan didn't actually
     discriminate (see renderTransitConfirmation). */
  .badge-ok, .badge-warn{ display:inline-block; padding:3px 10px; border-radius:20px;
    font-size:.74rem; font-weight:700; letter-spacing:.02em; }
  .badge-ok{ background:rgba(26,159,76,.15); color:#12703a; }
  .badge-warn{ background:rgba(232,181,58,.2); color:#7a5a10; }

  /* Ashtakavarga bindu grid. Numbers are compared down columns and across
     rows, so tabular-nums keeps the digits aligned. */
  .av-table{ font-variant-numeric:tabular-nums; }
  .av-table td, .av-table th{ text-align:center; padding:5px 7px; }
  .av-table td:first-child, .av-table th:first-child{ text-align:left; white-space:nowrap; }
  .av-table .av-strong{ background:rgba(26,159,76,.13); font-weight:700; }
  .av-table .av-weak{ background:rgba(224,57,43,.11); color:#9c2b1f; }
  .av-table .av-sav td{ border-top:2px solid var(--line-strong); }

  /* Site Theme admin panel — was four bare label/<input type=color> pairs
     stacked in a 2-col grid, which reads as four awkward little dropdowns
     with no feedback. Replaced with a live mock preview + labelled swatch
     rows + hex entry + one-click presets. */
  .theme-preview{ border:1px solid var(--line); border-radius:12px; overflow:hidden; background:var(--card); }
  .theme-preview-nav{ display:flex; align-items:center; gap:10px; padding:10px 14px;
    background:var(--theme-prev-primary,#1B2A6B); color:#fff; font-size:.85rem; }
  .theme-preview-dot{ width:10px; height:10px; border-radius:50%; background:var(--theme-prev-gold,#E8B53A); flex:none; }
  .theme-preview-btn{ background:var(--theme-prev-accent,#D94F2B); color:#fff; font-weight:700; font-size:.78rem;
    padding:5px 12px; border-radius:7px; }
  .theme-preview-body{ display:flex; align-items:center; gap:14px; padding:12px 14px; flex-wrap:wrap; }
  .theme-preview-badge{ background:var(--theme-prev-gold,#E8B53A); color:#3a2c05; font-weight:700; font-size:.72rem;
    padding:4px 9px; border-radius:20px; }
  .theme-preview-link{ color:var(--theme-prev-primary-dark,#141f52); font-weight:600; font-size:.85rem; text-decoration:underline; }

  .theme-swatches{ margin-top:16px; display:flex; flex-direction:column; gap:10px; }
  .theme-swatch-row{ display:flex; align-items:center; gap:12px; padding:8px 10px;
    border:1px solid var(--line); border-radius:10px; }
  .theme-swatch{ position:relative; width:38px; height:38px; border-radius:9px; flex:none; cursor:pointer;
    box-shadow:inset 0 0 0 1px rgba(0,0,0,.12); }
  .theme-swatch input[type=color]{ position:absolute; inset:0; opacity:0; width:100%; height:100%; cursor:pointer; }
  .theme-swatch-info{ display:flex; flex-direction:column; flex:1; min-width:0; line-height:1.3; }
  .theme-swatch-info .muted{ font-size:.78rem; }
  .theme-hex{ width:88px; flex:none; font-family:ui-monospace,Consolas,monospace; font-size:.85rem;
    text-transform:uppercase; text-align:center; }

  .theme-presets{ display:flex; gap:8px; flex-wrap:wrap; margin-top:8px; }
  .theme-preset{ display:flex; align-items:center; gap:7px; padding:6px 10px; border:1px solid var(--line);
    border-radius:20px; cursor:pointer; background:var(--card); font-size:.8rem; font-weight:600; }
  .theme-preset:hover{ border-color:var(--line-strong); }
  .theme-preset-dots{ display:flex; }
  .theme-preset-dots span{ width:14px; height:14px; border-radius:50%; margin-left:-5px; box-shadow:0 0 0 2px #fff; }
  .theme-preset-dots span:first-child{ margin-left:0; }
  .wrap{ max-width:1440px; width:100%; margin:0 auto; padding:20px 20px 32px; }
  .tabs{ display:flex; gap:8px; margin-bottom:18px; }
  .tabs button{ flex:1; padding:11px; background:var(--card); color:var(--text); border:1px solid var(--line-strong); border-radius:10px;
    cursor:pointer; font-weight:600; font-size:.95rem; transition:background .1s ease,border-color .1s ease,color .1s ease; }
  .tabs button:hover{ border-color:var(--line-strong); color:var(--primary); background:var(--sunk-soft); }
  .tabs button.active{ background:var(--primary); color:var(--on-primary); border-color:var(--primary); }
  /* wide panels: let generate-form + results sit side by side instead of stacking full-height */
  .cols2{ display:grid; grid-template-columns:minmax(300px,380px) 1fr; gap:16px; align-items:start; }
  @media (max-width:900px){ .cols2{ grid-template-columns:1fr; } }
  /* hamburger button — hidden on desktop, shown on mobile */
  .navtoggle{ display:none; flex:none; width:40px; height:40px; border-radius:10px; border:1px solid var(--line-strong);
    background:var(--card); color:var(--primary); cursor:pointer; align-items:center; justify-content:center; }
  .navtoggle svg{ width:20px; height:20px; }
  .homebtn{ display:inline-flex; flex:none; width:40px; height:40px; border-radius:10px; border:1px solid var(--line-strong);
    background:var(--card); color:var(--primary); cursor:pointer; align-items:center; justify-content:center; }
  .homebtn svg{ width:20px; height:20px; }
  .homebtn:hover{ border-color:var(--primary); }
  .nav-backdrop{ display:none; position:fixed; inset:0; background:rgba(20,22,34,.45); z-index:1400; }
  @media (max-width:860px){
    /* Sidebar becomes an off-canvas drawer (replaces the old horizontal
       scroll strip): slides in over a backdrop, closes on selection/backdrop
       tap. */
    .app-shell{ flex-direction:column; }
    .navtoggle{ display:inline-flex; }
    .sidenav{ position:fixed; top:0; left:0; bottom:0; width:250px; height:100vh; z-index:1500;
      transform:translateX(-100%); transition:transform .22s ease; box-shadow:var(--shadow-lg); overflow-y:auto; }
    body.nav-open .sidenav{ transform:translateX(0); }
    body.nav-open .nav-backdrop{ display:block; }
  }
  /* ---- general phone-size pass: smaller base type + tighter cards so
     content fits without the horizontal cramming that causes overlap, and
     inline button/input rows wrap into neat stacks instead of squeezing or
     spilling off-screen. ---- */
  @media (max-width:480px){
    body{ font-size:.93rem; }
    .card{ padding:14px; border-radius:12px; }
    .card h3{ font-size:1rem; }
    .row{ flex-wrap:wrap; }
    .row input, .row select{ flex:1 1 140px; }
    table{ font-size:.82rem; }
  }
  .card{ background:var(--card); border:1px solid var(--line); border-radius:14px; padding:18px; margin-bottom:16px;
         box-shadow:var(--shadow); transition:box-shadow .18s,border-color .18s; }
  .card h3{ margin:0 0 12px; color:var(--heading); font-size:1.05rem; font-weight:700; }
  /* Export-buttons-in-header pattern: title left, export row right, on one
     line — replaces export buttons that used to sit at the bottom of a long
     result card, out of sight until scrolled to. */
  .card-head{ display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:8px; margin-bottom:12px; }
  .card-head h3{ margin:0; }
  .card-head-export{ display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
  .card-head-export .muted{ font-size:.76rem; }
  .card-head-export button{ padding:5px 11px; font-size:.78rem; }
  @media (max-width:480px){ .card-head{ flex-direction:column; align-items:stretch; } }
  label{ display:block; font-size:.75rem; color:var(--muted); margin:9px 0 4px; font-weight:600; letter-spacing:.02em; }
  /* One height for every control. Padding alone did NOT give that: a <select>,
     a date input and a text input each add their own internal metrics, so the
     same padding produced three slightly different heights sitting next to
     each other in a .grid -- which is most of why the forms looked untidy
     rather than merely large. An explicit height removes the difference. */
  input,textarea,select{ width:100%; padding:0 10px; background:var(--card); color:var(--text);
    border:1px solid var(--line-strong); border-radius:8px; font-size:.88rem;
    font-family:inherit; line-height:1.2; transition:border-color .12s,box-shadow .12s; }
  input,select{ height:var(--ctl-h); }
  select{ padding-right:6px; }
  textarea{ padding:9px 10px; min-height:76px; line-height:1.5; }
  input[type=checkbox],input[type=radio]{ width:auto; height:auto; }
  @media (max-width:640px){
    /* Phones get a taller control for the tap target, and 16px type because iOS
       zooms the page whenever a focused field is smaller than that. */
    :root{ --ctl-h:40px; }
    input,textarea,select{ font-size:16px; }
  }
  input:focus,textarea:focus,select:focus{ outline:none; border-color:var(--primary); box-shadow:0 0 0 3px var(--primary-soft); }
  input::placeholder,textarea::placeholder{ color:var(--faint-text); }
  .grid{ display:grid; grid-template-columns:1fr 1fr; gap:10px; }
  .row{ display:flex; gap:8px; }
  .row input{ flex:1; }
  .row button{ white-space:nowrap; height:var(--ctl-h); padding:0 15px; background:var(--card); color:var(--primary); border:1px solid var(--line-strong); border-radius:8px; cursor:pointer; font-weight:600; font-size:.88rem; }
  .row button:hover{ border-color:var(--primary); background:var(--primary-soft); }
  /* Auto width, not 100%. A primary action stretched across a whole card reads
     as a banner rather than a button, and gave every screen the same "long bar"
     silhouette regardless of whether the action was Generate or Cancel. A
     button should be as wide as its label plus breathing room; min-width keeps
     short labels ("Save") from shrinking to a stub so a row of them still
     lines up. On phones they go full width again, where a wide tap target is
     genuinely the better pattern. */
  .btn{ margin-top:16px; width:auto; min-width:140px; display:inline-flex; align-items:center;
        justify-content:center; height:var(--ctl-h); padding:0 20px; background:var(--primary-soft); color:var(--primary);
        border:1px solid var(--btn-soft-line);
        border-radius:9px; font-weight:600; font-size:.92rem; cursor:pointer;
        transition:background .1s ease,border-color .1s ease; }
  @media (max-width:640px){ .btn{ width:100%; } }
  .btn:hover{ background:var(--btn-soft-hover); border-color:var(--primary); }
  /* A 1px inset on press gives the tactile confirmation the hover lift used to
     carry, without animating layout. */
  .btn:active{ background:var(--btn-soft-hover); box-shadow:inset 0 1px 2px rgba(27,42,107,.14); }
  /* Disabled needs its own colour now, not just opacity: a pale button at 50%
     opacity is nearly invisible, where a dark one at 50% still read as a
     button that happened to be off. */
  .btn:disabled{ background:var(--sunk-soft); color:var(--muted); border-color:var(--line);
                 cursor:default; box-shadow:none; }
  /* .row button{background:var(--card);...} (above) is a class+element selector, so
     it out-specifies the plain .btn class and silently turns a primary
     button white whenever it's placed inside a .row flex wrapper (e.g. next
     to a secondary abtn button). This restores .btn's real styling in that
     case rather than relying on every caller to avoid nesting it in .row. */
  .row .btn{ background:var(--primary); color:var(--on-primary); border:1px solid var(--primary);
             width:auto; margin-top:0; flex:1; }
  .row .btn:hover{ background:var(--primary-hover); border-color:var(--primary-hover); }
  /* .hidden is defined at the END of this stylesheet — see the note there. */
  table{ width:100%; border-collapse:collapse; font-size:.86rem; }
  th,td{ text-align:left; padding:8px 10px; border-bottom:1px solid var(--line); vertical-align:top; }
  th{ color:var(--muted); font-weight:600; background:var(--sunk-soft); }
  tbody tr:hover, table tr:hover{ background:var(--sunk-soft); }
  /* Financial Astrology and Home Muhurta result tables only (Multi-
     Timeframe, Key Time Windows, Planetary Strength, Financial Houses,
     Chart Comparison, Astronomical Events, Best Matching Dates) -- scoped
     to .fin-dense so this never touches the Birth Chart / Horary tables,
     which must render unchanged. Compact rows + tabular numerals + a
     sticky header: these tables run to 11+ columns / 15+ rows and are read
     by someone scanning for a specific window, not reading top to bottom. */
  .fin-dense table{ font-variant-numeric:tabular-nums; font-size:.84rem; }
  .fin-dense th{ position:sticky; top:0; background:var(--sunk-soft); }
  .fin-dense th, .fin-dense td{ padding:6px 10px; }
  .verdict{ font-weight:700; }
  .v-promised{ color:var(--green); } .v-weak{ color:var(--amber); } .v-not{ color:var(--red); }
  .windows{ color:var(--muted); font-size:.78rem; }
  .err{ color:var(--red); }
  .ok-note{ color:var(--green); font-size:.8rem; margin-top:8px; font-weight:600; }
  .muted{ color:var(--muted); font-size:.82rem; }
  .chip{ display:inline-block; background:var(--primary-soft); color:var(--primary-dark); border:1px solid #d4dbeb; border-radius:20px; padding:3px 11px; margin:2px; font-size:.8rem; font-weight:600; }
  /* State variants — birth-time confidence, outcome status. Semantic colour,
     deliberately separate from the brand palette so "verified" vs "unknown"
     reads at a glance without competing with --primary. */
  .chip.ok{ background:#e8f6ee; color:#14713a; border-color:#b9e2c9; }
  .chip.warn{ background:#fdf3e0; color:#8a6209; border-color:#f0dcae; }
  /* Cusp derivation expert review (F2b): stacked rows, wrap on a phone, theme variables only. */
  .cdq{ margin-top:4px; min-width:0; }
  .cdq-tabs{ display:flex; gap:6px; flex-wrap:wrap; margin:10px 0; }
  .cdq-filters{ display:flex; gap:8px; flex-wrap:wrap; align-items:flex-end; margin:8px 0; }
  .cdq-filters label{ display:flex; flex-direction:column; gap:2px; font-size:.76rem; color:var(--muted); min-width:0; flex:1 1 120px; }
  .cdq-filters input, .cdq-filters select, .cdq-form input, .cdq-form select{ width:100%; max-width:100%; min-width:0; box-sizing:border-box; }
  .cdq-item{ border:1px solid var(--line); border-radius:10px; padding:10px 12px; margin:8px 0; background:var(--card); min-width:0; overflow-wrap:anywhere; }
  .cdq-item .cdq-q{ font-weight:600; color:var(--text); }
  .cdq-meta{ display:flex; flex-wrap:wrap; gap:4px 12px; font-size:.78rem; color:var(--muted); margin:4px 0; }
  .cdq-acts{ display:flex; flex-wrap:wrap; gap:6px; align-items:center; margin-top:6px; }
  .cdq-acts .abtn, .cdq-tabs .abtn{ padding:6px 10px; font-size:.78rem; }
  .cdq-acts select{ max-width:100%; min-width:0; }
  .cdq-note{ background:var(--primary-soft); color:var(--text); border-radius:10px; padding:8px 12px; font-size:.82rem; margin:8px 0; }
  .cdq-err{ border:1px solid var(--red); color:var(--red); border-radius:10px; padding:8px 12px; margin:8px 0; font-size:.84rem; }
  .cdq-panel{ border:1px solid var(--line-strong); border-radius:12px; padding:12px; margin:10px 0; background:var(--sunk-soft); }
  .cdq-ba{ display:grid; grid-template-columns:1fr 1fr; gap:8px; }
  .cdq-ba > div{ border:1px solid var(--line); border-radius:8px; padding:8px; background:var(--card); min-width:0; overflow-wrap:anywhere; }
  @media (max-width:520px){ .cdq-ba{ grid-template-columns:1fr; } }
  /* Tracked Predictions outcome buttons — a visible, evenly-spaced group
     instead of three borderless .ghost buttons mashed together with just a
     space between them (reported as unclear/misaligned). Colour matches the
     same green/amber/red semantics already used for verdicts (.v-promised/
     .v-weak/.v-not), so "yes it happened" reads the same way everywhere. */
  .tp-chart-heading{ margin:18px 0 6px; font-size:.92rem; color:var(--primary); }
  .tp-chart-heading:first-child{ margin-top:0; }
  /* "Don't track this" — a settled prediction the user asked not to be
     asked about again. Shown greyed rather than hidden: a row that silently
     disappeared would read as data loss, and the choice has to be undoable. */
  .tp-dismissed td{ opacity:.55; }
  .tp-dismissed:hover td{ opacity:1; }
  /* The fourth option in an open-window row, a peer of Yes / No / Not yet —
     it answers the same prompt ("did it happen?") with "stop asking me". */
  .tp-dismiss{ color:var(--muted) !important; }
  .tp-dismiss:hover{ background:var(--bg) !important; }
  /* Offered once, right after the user confirms an event happened — the only
     moment "…and I'm done with this topic" is a natural thought. */
  .mute-offer{ border-color:var(--tile-line); background:var(--primary-soft); }
  #tp_muted .hw-row{ align-items:center; }

  /* ---- Personalised Home ------------------------------------------------
     The Home screen used to be a flat grid of 14 equally-weighted tiles with
     "Pick a tool to get started" above it — identical on a user's first visit
     and their fiftieth. These blocks give a returning user something that has
     actually changed since last time; all of them render from rows the user's
     own actions already wrote, so none of this reaches the engine. */
  .askhero{ border-color:var(--tile-line); background:linear-gradient(180deg,var(--primary-soft),var(--card) 70%); }
  .home-greeting{ margin:0 0 2px; font-size:.86rem; font-weight:600; color:var(--primary); }

  .home-sec{ margin-top:14px; }
  .home-sec .hs-head{ display:flex; align-items:center; gap:8px; margin:0 0 10px; }
  .home-sec .hs-head h3{ margin:0; font-size:1rem; }
  .home-sec .hs-count{ font-size:.68rem; font-weight:700; color:var(--on-status); background:var(--accent);
    border-radius:99px; padding:1px 8px; }

  /* A window row leads with a colour stripe so "act on this now" is legible
     before any text is read — the same at-a-glance job the verdict chips do. */
  .hw-row{ display:flex; align-items:flex-start; gap:10px; padding:10px 0; border-top:1px solid var(--line); }
  .hw-row:first-child{ border-top:none; padding-top:0; }
  .hw-stripe{ width:3px; align-self:stretch; border-radius:2px; flex:none; background:var(--line-strong); }
  .hw-stripe.answerable{ background:var(--green); }
  .hw-stripe.live{ background:var(--amber); }
  .hw-stripe.upcoming{ background:var(--primary); }
  .hw-body{ flex:1; min-width:0; }
  .hw-body b{ display:block; font-size:.88rem; }
  .hw-body em{ font-style:normal; font-size:.78rem; color:var(--muted); }

  .home-track{ display:flex; align-items:center; gap:14px; }
  .home-track .htr-big{ font-size:1.7rem; font-weight:700; color:var(--green); line-height:1;
    font-variant-numeric:tabular-nums; }
  .home-track .htr-cap b{ display:block; font-size:.86rem; }
  .home-track .htr-cap span{ font-size:.78rem; color:var(--muted); }

  /* ---- U2B F4: follow sheet, Questions you follow, Today, Your record ---- */
  :root{ --fw-tag:#c2401f; --fw-stat-label:#5f6786; --fw-knob:#ffffff;
    --fw-orb-1:#fff7da; --fw-orb-2:#e9d9a6; --fw-orb-3:#b9a773; --fw-orb-shade:rgba(0,0,0,.12); }
  @media (prefers-color-scheme:dark){ :root:not([data-theme="light"]){ --fw-tag:#ff8a6b; --fw-stat-label:var(--muted); } }
  :root[data-theme="dark"]{ --fw-tag:#ff8a6b; --fw-stat-label:var(--muted); }
  /* the sheet: the app's own .modal-bg/.modal, restyled to the mockup .card */
  .modal.fw-sheet{ max-width:420px; padding:16px; border-radius:18px; border:1px solid var(--line);
    box-shadow:var(--shadow); background:var(--card); }
  .fw-sec{ margin:4px 0 10px; font-size:.95rem; font-weight:700; color:var(--heading); line-height:1.25; }
  .modal.fw-sheet h3.fw-sec{ font-size:.95rem; margin:4px 0 10px; }      /* beats .modal h3 (1.2rem) */
  .fw-muted{ margin:0; font-size:.8rem; color:var(--muted); }
  .fw-period{ margin-top:-4px; }
  .fw-rows{ margin-top:6px; }
  .fw-toggle-row{ display:flex; align-items:center; justify-content:space-between; gap:12px;
    padding:10px 0; border-top:1px solid var(--line); font-size:.88rem; color:var(--text); }
  .fw-rows > .fw-toggle-row:first-child{ border-top:0; }
  .fw-sw{ position:relative; flex:none; width:42px; height:24px; min-width:0; margin:0; padding:0;
    border:0; border-radius:12px; background:var(--line-strong);
    box-shadow:inset 0 0 0 1px var(--tw-none); cursor:pointer; }          /* K11: 3:1 boundary */
  .fw-sw::after{ content:""; position:absolute; top:3px; left:3px; width:18px; height:18px;
    border-radius:50%; background:var(--fw-knob); transition:left .15s; }
  .fw-sw[aria-checked="true"]{ background:var(--tw-strong); box-shadow:none; }
  .fw-sw[aria-checked="true"]::after{ left:21px; }
  .fw-sw:disabled{ opacity:.45; cursor:not-allowed; }
  .fw-sw:focus-visible, .fw-change:focus-visible, .fw-row-btn:focus-visible{
    outline:2px solid var(--primary); outline-offset:2px; }
  .fw-how b{ font-weight:700; color:var(--text); }
  .fw-change{ flex:none; width:auto; min-width:0; min-height:32px; margin:0; padding:0 0 0 8px;
    border:0; background:none; font-size:.8rem; color:var(--muted); cursor:pointer; }
  .fw-note{ margin:4px 0 0; }
  .fw-err{ margin:10px 0 0; padding:8px 10px; border-radius:8px; background:var(--danger-soft);
    color:var(--danger); font-size:.8rem; }
  .jc-btn.small{ min-height:32px; padding:0 12px; font-size:.8rem; }    /* mockup .btn.small */
  .jc-btn:disabled{ opacity:.5; cursor:not-allowed; }
  /* Home cards (mockup .card) */
  .fw-card{ background:var(--card); border:1px solid var(--line); border-radius:18px; padding:16px;
    margin:0 0 12px; box-shadow:var(--shadow); }
  .fw-tag{ margin:0; font-size:.7rem; font-weight:800; text-transform:uppercase; letter-spacing:.04em;
    color:var(--fw-tag); }
  .fw-moon{ display:flex; gap:12px; align-items:flex-start; margin-top:6px; }
  .fw-moon + .fw-moon{ margin-top:12px; }
  .fw-orb{ width:38px; height:38px; flex:none; border-radius:50%;
    background:radial-gradient(circle at 35% 35%, var(--fw-orb-1), var(--fw-orb-2) 55%, var(--fw-orb-3));
    box-shadow:inset -8px -3px 0 var(--fw-orb-shade); }
  .fw-moon b{ display:block; color:var(--text); }
  .fw-moon .fw-muted{ margin:2px 0 0; }
  .fw-list{ list-style:none; margin:0; padding:0; }
  .fw-row{ border-top:1px solid var(--line); }
  .fw-row:first-child{ border-top:0; }
  .fw-row-btn{ display:flex; align-items:center; gap:10px; width:100%; min-width:0; margin:0;
    padding:10px 0; border:0; background:none; color:inherit; font:inherit; text-align:left; cursor:pointer; }
  .fw-main{ flex:1; min-width:0; }
  .fw-main b{ display:block; font-size:.9rem; color:var(--text); }
  .fw-main span{ font-size:.78rem; color:var(--muted); }
  .fw-stat{ display:flex; gap:10px; text-align:center; }
  .fw-stat > div{ flex:1; min-width:0; background:var(--sunk-soft); border-radius:12px; padding:8px 4px; }
  .fw-stat b{ display:block; font-size:1.25rem; color:var(--heading); }
  .fw-stat span{ font-size:.72rem; color:var(--fw-stat-label); }

  /* Home "Now running" card + 12-month Period Map (NR-2). Own block; every
     rule is prefixed .nr-/#nr_ so it cannot touch the neighbouring cards. */
  #home_personal > .nr-wrap{ grid-column:1/-1; }
  .nr-wrap .nr-block{ margin:0 0 10px; }
  .nr-head{ display:flex; align-items:baseline; justify-content:space-between; gap:10px; }
  .nr-name{ font-size:.92rem; color:var(--heading); min-width:0; overflow-wrap:anywhere; }
  .nr-asof{ font-size:.76rem; color:var(--muted); white-space:nowrap; }
  .nr-period{ margin:6px 0 0; font-size:.95rem; font-weight:600; color:var(--text); }
  .nr-dates-line, .nr-none, .nr-star, .nr-lock{ margin:2px 0 0; font-size:.8rem; }
  .nr-next{ margin:6px 0 0; font-size:.82rem; color:var(--text); }
  .nr-dot{ display:inline-block; width:8px; height:8px; border-radius:50%; background:var(--primary); vertical-align:middle; margin-left:4px; }
  .nr-next .nr-dot{ margin:0 4px 0 0; }
  .nr-sub{ margin:12px 0 6px; font-family:var(--sans); font-size:.74rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--heading); }
  .nr-chips{ display:flex; flex-wrap:wrap; gap:6px; }
  .nr-chip{ font-family:inherit; font-size:.8rem; padding:5px 11px; border-radius:999px; cursor:pointer;
    background:var(--tile-tint); border:1px solid var(--tile-line); color:var(--text); }
  .nr-chip:hover{ border-color:var(--primary); }
  .nr-chip-more{ color:var(--muted); }
  .nr-maptoggle{ margin:12px 0 0; padding:0; border:0; background:none; font:inherit; font-size:.82rem; font-weight:600;
    color:var(--primary); cursor:pointer; }
  .nr-maptoggle::after{ content:" ▾"; }
  .nr-maptoggle[aria-expanded="true"]::after{ content:" ▴"; }
  .nr-map{ margin-top:10px; }
  .nr-axis{ position:relative; height:16px; margin-left:182px; font-size:.68rem; color:var(--faint-text); }
  .nr-axis span{ position:absolute; top:0; transform:translateX(-2px); }
  .nr-bars{ list-style:none; margin:0; padding:0; }
  .nr-bar{ display:grid; grid-template-columns:170px minmax(0,1fr); column-gap:12px; row-gap:2px; align-items:center;
    padding:8px 0; border-top:1px solid var(--line); }
  .nr-bar:first-child{ border-top:0; }
  .nr-bar-label{ grid-column:1; grid-row:1; font-size:.82rem; }
  .nr-now{ margin-left:6px; font-size:.68rem; font-weight:700; text-transform:uppercase; letter-spacing:.05em; color:var(--primary); }
  .nr-track{ grid-column:2; grid-row:1; position:relative; height:10px; border-radius:6px; background:var(--sunk-soft); }
  .nr-fill{ position:absolute; top:0; bottom:0; border-radius:6px; background:var(--muted); opacity:.55; }
  .nr-bar.is-now .nr-fill{ background:var(--primary); opacity:1; }
  .nr-dates{ grid-column:2; grid-row:2; font-size:.74rem; color:var(--muted); }
  .nr-bar > .nr-chips{ grid-column:2; grid-row:3; margin-top:4px; }
  .nr-lock{ margin-top:8px; }
  @media (max-width:559px){
    /* List form: no axis, no bar; label, dates, chips stacked. */
    .nr-axis, .nr-track{ display:none; }
    .nr-bar{ grid-template-columns:minmax(0,1fr); }
    .nr-bar-label, .nr-dates, .nr-bar > .nr-chips{ grid-column:1; }
    .nr-bar-label{ grid-row:1; } .nr-dates{ grid-row:2; } .nr-bar > .nr-chips{ grid-row:3; }
  }

  .home-resume{ display:flex; align-items:center; gap:10px; width:100%; text-align:left; cursor:pointer;
    background:var(--tile-tint); border:1px solid var(--tile-line); border-radius:12px; padding:12px;
    font-family:inherit; color:inherit; transition:border-color .14s ease, transform .14s ease; }
  .home-resume:hover{ border-color:var(--primary); transform:translateY(-1px); }
  .home-resume .hr-body{ flex:1; min-width:0; }
  .home-resume .hr-body b{ display:block; font-size:.88rem; }
  .home-resume .hr-body span{ font-size:.78rem; color:var(--muted); }

  /* "Suggested for you" — same tile shape as .home-resume above, with one
     extra line: the REASON. That line is the point of the card, not
     decoration: a suggestion the reader cannot see the basis for is one
     they cannot disagree with. */
  .hsg-list{ display:flex; flex-direction:column; gap:8px; }
  .hsg-row{ display:flex; align-items:center; gap:10px; width:100%; text-align:left; cursor:pointer;
    background:var(--tile-tint); border:1px solid var(--tile-line); border-radius:12px; padding:12px;
    font-family:inherit; color:inherit; transition:border-color .14s ease, transform .14s ease; }
  .hsg-row:hover{ border-color:var(--primary); transform:translateY(-1px); }
  .hsg-row .hsg-body{ flex:1; min-width:0; }
  .hsg-row .hsg-body b{ display:block; font-size:.88rem; }
  .hsg-row .hsg-pitch{ display:block; font-size:.78rem; color:var(--muted); }
  .hsg-row .hsg-why{ display:block; margin-top:4px; font-size:.75rem; color:var(--primary); font-weight:600; }
  @media (prefers-reduced-motion: reduce){ .hsg-row{ transition:none; } }

  /* Admin: most frequent sign-ins. A horizontal bar per account, because the
     label is an account id — a vertical chart would turn every one of them
     into a rotated, unreadable axis tick. */
  .lf-list{ display:flex; flex-direction:column; gap:4px; }
  .lf-row{ display:grid; grid-template-columns:2.2rem minmax(120px,1.4fr) minmax(80px,2fr) 2.6rem auto;
    align-items:center; gap:10px; padding:4px 0; font-size:.83rem; }
  .lf-rank{ color:var(--faint-text); font-size:.76rem; text-align:right; font-variant-numeric:tabular-nums; }
  .lf-who{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .lf-track{ height:9px; border-radius:5px; background:var(--tile-tint);
    border:1px solid var(--tile-line); overflow:hidden; }
  .lf-track > i{ display:block; height:100%; background:var(--primary); }
  .lf-n{ text-align:right; font-variant-numeric:tabular-nums; }
  .lf-last{ color:var(--muted); font-size:.76rem; white-space:nowrap; }
  /* Phone: the bar and the last-seen date are the first things worth losing —
     the ranking, the name and the count still answer the question. */
  @media (max-width:640px){
    .lf-row{ grid-template-columns:2rem 1fr 2.6rem; }
    .lf-track, .lf-last{ display:none; }
  }

  /* "All tools" disclosure — the 14 tiles are unchanged, just no longer the
     first thing a returning user has to read past. */
  .alltools-toggle{ display:flex; align-items:center; gap:10px; width:100%; text-align:left; cursor:pointer;
    background:none; border:none; padding:0; font-family:inherit; color:inherit; }
  .alltools-toggle .at-title{ display:block; font-size:1rem; font-weight:600; color:var(--heading); }
  .alltools-toggle .at-sub{ display:block; font-size:.82rem; color:var(--muted); margin-top:2px; }
  .alltools-toggle > span:first-child{ flex:1; min-width:0; }
  .alltools-toggle .at-chevron{ font-size:1.2rem; color:var(--muted); flex:none; line-height:1;
    transition:transform .16s ease; }
  .alltools-toggle[aria-expanded="false"] .at-chevron{ transform:rotate(-90deg); }
  @media (prefers-reduced-motion:reduce){
    .alltools-toggle .at-chevron, .home-resume{ transition:none; }
  }
  .tp-table{ table-layout:auto; }
  .tp-table td, .tp-table th{ vertical-align:middle; }
  .tp-outcome-btns{ display:flex; gap:6px; flex-wrap:wrap; align-items:center; }
  .tp-outcome-btns .abtn{ padding:6px 12px; font-size:.78rem; white-space:nowrap; }
  .tp-yes{ border-color:#b9e2c9 !important; color:#14713a !important; }
  .tp-yes:hover{ background:#e8f6ee !important; }
  .tp-no{ border-color:#f3c6c1 !important; color:var(--red) !important; }
  .tp-no:hover{ background:#fdeceb !important; }
  .tp-later{ color:var(--muted) !important; }
  .tp-table td:has(.chip){ white-space:nowrap; }
  .tp-edit{ margin-left:4px; padding:3px 8px !important; font-size:.74rem !important; }
  /* The "Don't track this" text button and the delete icon used to have no
     shared layout rule, so a narrow column stacked them one above the other
     — same problem the outcome buttons already had, fixed the same way: one
     row, wrapping only if it truly has to. */
  .tp-actions{ display:flex; gap:6px; flex-wrap:wrap; align-items:center; white-space:nowrap; }
  .tp-actions .abtn{ padding:6px 10px; font-size:.78rem; white-space:nowrap; }
  @media (max-width:640px){ .tp-outcome-btns{ flex-direction:column; align-items:stretch; }
    .tp-outcome-btns .abtn{ justify-content:flex-start; }
    .tp-actions{ flex-direction:column; align-items:stretch; }
    .tp-actions .abtn{ justify-content:flex-start; } }
  /* ---- KP detail: mini-tabs shared by birth-domain detail & horary ---- */
  .kptabs{ display:flex; gap:4px; flex-wrap:wrap; border-bottom:1px solid var(--line); margin-bottom:14px; }
  .kptab{ display:inline-flex; align-items:center; height:var(--ctl-h); padding:0 14px;
          background:none; border:none; border-bottom:2px solid transparent; cursor:pointer;
          font-weight:600; font-size:.82rem; color:var(--muted); border-radius:8px 8px 0 0; }
  @media (max-width:640px){
    /* A ragged multi-line wrap of tab labels reads as "scattered buttons" on
       narrow screens — a single horizontally-scrollable strip (the standard
       mobile tab pattern) stays tidy at any label count. */
    .kptabs{ flex-wrap:nowrap; overflow-x:auto; -webkit-overflow-scrolling:touch; scrollbar-width:none; }
    .kptabs::-webkit-scrollbar{ display:none; }
    .kptab{ flex:0 0 auto; white-space:nowrap; padding:8px 12px; font-size:.78rem; }
  }
  .kptab:hover{ color:var(--primary); background:var(--primary-soft); }
  .kptab.active{ color:var(--primary); border-bottom-color:var(--primary); }
  .kppane{ display:none; } .kppane.active{ display:block; }
  /* A tab strip nested inside a pane (Doshas under Vedic Predictions). Styled
     down deliberately: two strips of identical weight stacked on top of each
     other read as one confusing row, so the inner one is smaller, pill-shaped
     and rule-less, which says "part of the tab above" rather than "peer of
     it". Selection still reads clearly -- the active pill takes a filled
     background where the outer strip uses an underline. */
  .kpsub > .kptabs{ border-bottom:none; gap:6px; margin:-2px 0 12px; }
  .kpsub > .kptabs .kptab{ height:auto; padding:5px 12px; font-size:.8rem;
    border-bottom:none; border-radius:20px; background:var(--sunk-soft);
    border:1px solid var(--line); }
  .kpsub > .kptabs .kptab:hover{ background:var(--primary-soft); }
  .kpsub > .kptabs .kptab.active{ background:var(--primary-soft);
    border-color:var(--btn-soft-line); color:var(--primary); font-weight:700; }
  /* Birth/Horary slider toggle — shown atop both panels so switching
     between the two doesn't require going back to the side nav. */
  .bh-switch{ position:relative; display:flex; width:100%; max-width:340px; background:var(--primary-soft); border-radius:999px; padding:4px; margin-bottom:14px; }
  .bh-switch .bh-thumb{ position:absolute; top:4px; left:4px; width:calc(50% - 4px); height:calc(100% - 8px); background:linear-gradient(135deg,var(--primary),#233584); border-radius:999px; transition:transform .22s cubic-bezier(.4,0,.2,1); box-shadow:var(--shadow); }
  .bh-switch.on-horary .bh-thumb{ transform:translateX(100%); }
  .bh-switch .bh-opt{ position:relative; z-index:1; flex:1; background:none; border:none; padding:9px 4px; font-weight:700; font-size:.85rem; color:var(--muted); cursor:pointer; border-radius:999px; transition:color .22s; }
  .bh-switch .bh-opt.active{ color:#fff; }
  /* Account & Settings — larger, spaced-out card rows (each field/action gets
     visual room, matching the roomier "settings page" pattern rather than
     the compact form density used elsewhere in the app). */
  #acct_pane_settings h4{ font-size:1.15rem; }
  #acct_pane_settings .acct-card{ background:var(--bg); border:1px solid var(--line); border-radius:14px; padding:6px 18px; }
  #acct_pane_settings .acct-row{ display:flex; align-items:center; justify-content:space-between; gap:20px; padding:16px 0; flex-wrap:wrap; }
  #acct_pane_settings .acct-row.hidden{ display:none; }
  #acct_pane_settings .acct-row + .acct-row{ border-top:1px solid var(--line); }
  #acct_pane_settings .acct-row-label{ font-weight:700; font-size:1rem; }
  #acct_pane_settings .acct-row-sub{ color:var(--muted); font-size:.85rem; margin-top:3px; font-weight:400; }
  #acct_pane_settings .acct-row-field{ flex:0 1 340px; min-width:200px; }
  #acct_pane_settings .acct-row .abtn, #acct_pane_settings .acct-row .btn{ flex:none; width:auto; margin-top:0; }
  #acct_pane_settings .acct-verified{ display:inline-flex; align-items:center; gap:4px; color:var(--green); font-weight:600; font-size:.8rem; background:rgba(35,145,90,.12); padding:3px 10px; border-radius:20px; margin-left:8px; }
  #acct_pane_settings .acct-section{ margin-top:22px; }
  #acct_pane_settings .acct-section-head{ margin-bottom:10px; }
  #acct_pane_settings .acct-section-head h4{ margin:0; }
  #acct_pane_settings .acct-section-head p{ margin:2px 0 0; }
  /* Seven Fundamental KP — numbered step cards */
  .step{ display:flex; gap:12px; padding:12px 14px; background:var(--card); border:1px solid var(--line); border-radius:10px; margin-bottom:8px; }
  .stepnum{ flex:none; width:26px; height:26px; border-radius:50%; background:var(--primary); color:var(--on-primary);
            font-weight:700; font-size:.8rem; display:flex; align-items:center; justify-content:center; }
  .stepbody{ flex:1; min-width:0; }
  .steptitle{ font-weight:700; color:var(--heading); font-size:.86rem; margin-bottom:6px; }
  .steprow{ display:flex; font-size:.98rem; padding:4px 0; gap:14px; }
  .steprow .k{ flex:0 0 auto; width:200px; color:var(--muted); font-weight:600; }
  .steprow .v{ flex:1; color:var(--text); font-weight:600; }
  @media (max-width:560px){ .steprow{ flex-direction:column; gap:1px; } .steprow .k{ width:auto; } }
  .stepverdict{ display:inline-block; background:var(--primary-soft); color:var(--primary-dark); font-weight:700;
                padding:3px 12px; border-radius:8px; font-size:.9rem; margin-bottom:8px; }
  .stepdesc{ font-size:.82rem; color:var(--text); margin:4px 0; } .stepreason{ font-size:.8rem; color:var(--muted); }
  /* Predictive Timing — overall banner + level cards */
  .tbanner{ padding:12px 16px; border-radius:10px; margin-bottom:12px; font-weight:700; color:#fff; }
  .tlevel{ display:flex; align-items:center; gap:12px; padding:10px 14px; background:var(--card); border:1px solid var(--line); border-radius:10px; margin-bottom:6px; }
  .tlevel .lname{ font-weight:700; color:var(--heading); min-width:120px; font-size:.84rem; }
  .tlevel .llord{ font-weight:700; min-width:64px; }
  .tlevel .ldates{ color:var(--muted); font-size:.78rem; margin-left:auto; white-space:nowrap; }

  /* Life-events timing windows. These live HERE, not in site.css:
     index.html does not load site.css at all, so styles put there never
     reach the app -- which is exactly how the first version of this
     panel shipped as an unspaced wall of text (reported with a
     screenshot, 2026-09-11). Same trap as the Backtest panel above.

     Since F3b (addendum section 13.3, mockup 09): this bordered panel is
     used ONLY for the "Check against your life" past-rows fold (A7,
     forward-only -- past periods offered as a check, never a claim). The
     card's forward-looking "ahead" rows (the nearest period plus the
     strongest of the rest) are a separate, unboxed list -- see the
     win-ahead-* rules below. The dasha lord chain is NOT shown here
     either way -- that is working calculation and lives in Notes
     (technical). Redesigned 2026-09-11: the first attempt shipped as an
     unspaced wall of dasha codes with no alignment at all. */
  .win-panel{ margin:10px 0 12px; border:1px solid var(--line); border-radius:12px;
   background:var(--bg); overflow:hidden; }
.win-title{ font-size:.72rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
   color:var(--muted); padding:9px 14px 7px; border-bottom:1px solid var(--line); }
.win-list{ list-style:none; margin:0; padding:0; }
.win-item{ display:grid; grid-template-columns:1fr auto auto; align-items:baseline;
   gap:4px 14px; padding:9px 14px; border-bottom:1px solid var(--line); }
.win-item:last-child{ border-bottom:0; }
.win-item .win-when{ font-weight:650; color:var(--text); font-size:.9rem;
   font-variant-numeric:tabular-nums; }
.win-item .win-age{ color:var(--muted); font-size:.78rem; white-space:nowrap; }
.win-item .win-tag{ font-size:.68rem; font-weight:700; letter-spacing:.04em;
   text-transform:uppercase; color:transparent; white-space:nowrap; }
.win-item.is-top{ background:var(--card); }
.win-item.is-top .win-when{ color:var(--primary); }
.win-item.is-top .win-tag{ color:var(--primary); }
/* Dead since F3b (D2, addendum section 13.3): the Ruling-Planets
   cross-check row this styled was removed from the ahead list outright --
   lifeEventCardEl no longer ever sets class="win-item is-rp" (a pre-F3a
   cached row carrying it is filtered out before rendering, not styled).
   Kept, not deleted, in case an even older stored HTML blob still
   contains it verbatim; safe to remove in a later cleanup pass. */
.win-item.is-rp{ background:rgba(232,181,58,.08); border-top:1px solid var(--line-strong); }
.win-item.is-rp .win-tag{ color:var(--gold); }
.win-item.is-rp .win-when{ color:var(--text); }
@media (max-width:520px){
  .win-item{ grid-template-columns:1fr auto; }
  .win-item .win-tag{ grid-column:1 / -1; }
}
/* Birth-time proof loop (BT-2): proof line, known-events list, entry forms. */
.bt-proof:empty{ display:none; }
.bt-proof{ margin-top:10px; display:flex; flex-direction:column; align-items:flex-start; gap:6px; }
.bt-line{ margin:0; padding:7px 12px; border-radius:8px; border:1px solid var(--line);
   border-left:4px solid var(--green); background:var(--tile-tint); font-size:.9rem; font-weight:600; }
.bt-line.warn{ border-left-color:var(--amber); }
.bt-link{ background:none; border:0; padding:0; color:var(--primary); font-size:.82rem;
   text-decoration:underline; cursor:pointer; }
.bt-list{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:4px; }
.bt-list.hidden{ display:none; }
.bt-rec{ display:inline-flex; align-items:center; gap:6px; font-size:.82rem; color:var(--text);
   border:1px solid var(--line); border-radius:999px; padding:2px 6px 2px 10px; background:var(--card); }
.bt-x{ background:none; border:0; cursor:pointer; color:var(--muted); font-size:.8rem; padding:0 4px; }
.bt-x:hover{ color:var(--red); }
.bt-act{ grid-column:1 / -1; display:flex; flex-wrap:wrap; align-items:center; gap:6px; }
.bt-act .abtn, .bt-le-foot .abtn, .bt-when .abtn, .bt-card .abtn{ padding:5px 12px; font-size:.78rem; }
.bt-act .abtn.ghost, .bt-le-foot .abtn.ghost, .bt-when .abtn.ghost{ border:1px solid var(--line); border-radius:8px; }
.bt-add{ display:flex; flex-wrap:wrap; align-items:center; gap:6px; flex-basis:100%; }
.bt-add.hidden, .bt-nonefit.hidden{ display:none; }
.bt-pick{ display:inline-flex; flex-wrap:wrap; gap:6px; }
.bt-pick select, .bt-pick input, .bt-urow select{ width:auto; margin:0; padding:5px 8px; font-size:.82rem; }
.bt-pick input.bt-d{ width:9.5rem; }
.bt-notice, .bt-short, .bt-hint, .bt-err{ margin:4px 0; font-size:.8rem; }
.bt-line .small{ font-size:.8rem; font-weight:400; }
.bt-le-foot{ margin:0 0 10px; }
.bt-card{ display:flex; flex-direction:column; align-items:flex-start; gap:8px; width:100%; max-width:640px;
   padding:12px 14px; border:1px solid var(--line); border-left:4px solid var(--amber);
   border-radius:10px; background:var(--card); }
.bt-urow{ display:flex; flex-wrap:wrap; gap:6px; align-items:center; }
.bt-when{ display:flex; flex-wrap:wrap; align-items:center; gap:6px; margin-top:8px; }
.bt-when .bt-notice{ flex-basis:100%; }
.bt-when-btns{ display:inline-flex; gap:6px; }
.bt-when-card{ margin-bottom:12px; }
.bt-err:empty{ display:none; }
.bt-err{ margin:0; flex-basis:100%; }
/* Life Events "ahead" rows (F3b, addendum section 13.3, mockup 09):
   row 0 is the nearest period, tagged "Nearest"/"Running now"; rows 1-2
   are the strongest of the rest, untagged -- their word chip (.chip.tw-*,
   F1) already shows their strength. A plain top-rule list on the card
   itself, NOT the bordered win-panel box above -- that box is reserved
   for the UNCHANGED "Check against your life" past-rows fold. */
.win-ahead-title{ font-size:.86rem; font-weight:800; color:var(--heading); margin:10px 0 0; }
.win-order-note{ font-size:.76rem; color:var(--muted); margin:2px 0 4px; }
.win-ahead-rows{ list-style:none; margin:0; padding:0; }
.win-ahead-row{ display:flex; align-items:center; gap:10px; padding:10px 0;
   border-top:1px solid var(--line); }
.win-ahead-row:first-child{ border-top:0; }
.win-ahead-row .win-main{ flex:1 1 auto; min-width:0; }
/* font-weight/font-variant-numeric deliberately NOT set: mockup 09's own
   `.row .main b` carries neither -- a plain <b> already renders at the
   browser default bold (700), and the mockup's dates are not tabular. */
.win-ahead-row .win-main b{ display:block; font-size:.9rem; color:var(--text); }
.win-ahead-row .win-main span{ display:block; font-size:.78rem; color:var(--muted); }
/* Colour: --accent-ink, NOT --accent and NOT --heading. --accent keeps the
   mockup's own hue (coral) but fails AA (4.11:1 light / 2.88:1 dark on
   --card); --heading passes AA but swaps the hue to navy, which review
   flagged as breaking C19 ("hue kept, ink darkened"). --accent-ink is the
   darkened-ink version of --accent's own hue (5.16:1 light / 5.14:1 dark
   on --card) -- shared with F2-U, which reuses this same token. */
.win-ahead-row .win-tag{ margin-left:6px; font-size:.7rem; font-weight:800; letter-spacing:.04em;
   text-transform:uppercase; color:var(--accent-ink); vertical-align:1px; }
.win-ahead-row .chip{ margin:0; flex:none; }
/* Reliability line margin, Life Events ONLY (mockup 09's own `.reliab`:
   margin:6px 0 0) -- scoped under .life-event-card so F1's shared
   `.tw-reliability` rule (predictions list / KP detail, margin-top:8px)
   is untouched. */
.life-event-card .tw-reliability{ margin:6px 0 0; }
/* Life Events card, judgment layout (2026-09-13): the same order as an AI
   Explanation reading -- verdict pill, then Hence / When / What stands in
   the way / Counsel / Check against your life. */
.life-event-card .le-head{ display:flex; align-items:baseline; justify-content:space-between;
   gap:6px 12px; flex-wrap:wrap; }
.life-event-card .le-head h4{ margin:0; }
.le-pill{ font-size:.74rem; font-weight:650; color:var(--primary); border:1px solid var(--line-strong);
   border-radius:999px; padding:2px 10px; white-space:nowrap; }
.le-stars{ color:var(--gold); letter-spacing:.04em; }
.life-event-card .le-headline{ margin:6px 0 4px; }
.le-sec{ margin:14px 0 0; }
.le-sec > h5{ margin:0 0 6px; font-size:.7rem; font-weight:700; letter-spacing:.07em;
   text-transform:uppercase; color:var(--muted); }
.le-sec ul{ margin:0; padding-left:18px; }
.le-counsel{ margin:0; font-style:italic; }
.le-bt{ margin:12px 0 0; font-size:.78rem; color:var(--muted); border-left:3px solid var(--gold);
   padding:2px 0 2px 8px; }
.le-bt.is-steady{ border-left-color:var(--line-strong); }
/* NEW (U5b): the birth-time line at the foot of the natal AI card. Inside .aiout the shared
   ".aiout .ai-answer p" margin (0 0 15px) out-ranks .le-bt, so it is restated here. */
.aiout .ai-answer .jc > p.le-bt{ margin:0 0 4px; line-height:1.5; }
.aicard .aiout .ai-answer .jc > p.le-bt{ font-size:var(--ai-sub); }

/* Save row under the life-events reading - sits after the last card so it
   is reachable at the end of the reading, not hidden in a header above it. */
.life-events-save{ display:flex; align-items:center; gap:10px; flex-wrap:wrap;
   margin:10px 0 4px; padding-top:10px; border-top:1px dashed var(--line); }
.life-events-save button{ padding:5px 14px; font-size:.82rem; }

  .tpill{ padding:2px 10px; border-radius:20px; font-size:.72rem; font-weight:700; color:#fff; white-space:nowrap; }
  .tstar{ color:var(--amber); font-size:.85rem; }
  .tdatecheck{ margin-top:18px; padding-top:14px; border-top:1px solid var(--line); }
  /* Evidence table */
  .matchbadge{ display:inline-block; padding:2px 9px; border-radius:6px; font-size:.7rem; font-weight:700; }
  .m-yes{ background:#e6f4ea; color:var(--green); } .m-no{ background:#fdecea; color:var(--red); }
  .sbar{ position:relative; height:7px; background:var(--line); border-radius:4px; overflow:hidden; min-width:52px; }
  .sbar i{ position:absolute; inset:0 auto 0 0; background:var(--primary); border-radius:4px; }
  .kptip{ font-size:.8rem; color:var(--muted); margin-bottom:10px; }
  /* expandable prediction rows */
  .pexpand{ cursor:pointer; }
  .pexpand:hover{ background:var(--primary-soft)!important; }
  .ptwist{ display:inline-block; width:12px; color:var(--muted); font-size:.7rem; transition:transform .12s; }
  tr.open .ptwist{ transform:rotate(90deg); }
  .pdetail-cell{ padding:0!important; background:#fbfcfe; }
  .pdetail-inner{ padding:16px; }
  /* ---- PPM tools ---- */
  .hgrid{ display:flex; flex-wrap:wrap; gap:6px; margin:6px 0 4px; }
  .hbtn{ width:38px; height:34px; border-radius:8px; border:1px solid var(--line-strong); background:var(--card);
         color:var(--text); font-weight:700; font-size:.82rem; cursor:pointer; }
  .hbtn:hover{ border-color:var(--primary); }
  .hbtn.on{ background:var(--primary); color:var(--on-primary); border-color:var(--primary); }
  .hbtn.neg.on{ background:var(--red); border-color:var(--red); }
  .ppmrow{ display:flex; gap:10px; flex-wrap:wrap; align-items:flex-end; margin:8px 0; }
  .ppmrow > div{ flex:1; min-width:140px; }
  .ppm-out{ margin-top:14px; }
  .ppm-cols{ display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:10px; margin-top:10px; }
  /* Analytics stat tile — lighter than a full .card so section-card > tiles
     doesn't read as heavy nested cards. */
  .astat{ background:var(--bg); border:1px solid var(--line); border-radius:10px; padding:12px 14px; }
  .astat h4{ margin:0 0 4px; font-size:.76rem; font-weight:600; color:var(--muted); text-transform:none; }
  .astat .v{ font-size:1.5rem; font-weight:800; color:var(--heading); line-height:1.15; }
  /* Paginated tables (Splunk-style: N rows/page, </> nav) — used anywhere a
     table could otherwise grow unbounded (Top Locations, Recent
     Signups/Transactions, User Lookup results, ...).

     COLUMNS SIZE TO THEIR CONTENT AND THE TABLE SCROLLS SIDEWAYS. This used
     to be table-layout:fixed with width:100%, which truncated every long
     value to an ellipsis with no way to read it -- reported 2026-09-05 as
     the analytics columns not being able to expand, on mobile AND desktop.
     The two rules also cancelled each other out: the renderer already wraps
     every one of these tables in an overflow-x container, but a table pinned
     to exactly 100% of that container can never overflow it, so the scroller
     never engaged and the ellipsis was the only outcome available.

     min-width:100% rather than width:100% is the whole trick -- the table
     still fills the panel when the data is narrow, and is free to grow past
     it when the data is wide, at which point the wrapper scrolls. nowrap is
     kept deliberately: with auto layout it is what gives each column its
     natural width, and without it a long value would instead produce a very
     tall row that is harder to scan than a sideways scroll. */
  .ptbl-table{ table-layout:auto; width:auto; min-width:100%; }
  .ptbl-table td, .ptbl-table th{ white-space:nowrap; }
  /* Momentum scrolling on iOS, and a scroll container the table can actually
     overflow. Was an inline style on the wrapper; named so the behaviour is
     described in one place rather than repeated per call site. */
  .ptbl-scroll{ overflow-x:auto; -webkit-overflow-scrolling:touch; }
  .ptbl-nav{ display:flex; flex-wrap:wrap; }
  .ptbl-nav select{ padding:2px 6px; font-size:.8rem; width:auto; }
  /* Small "?" help icon beside a panel heading — native title-attribute
     tooltip on hover, no JS needed. Kept visually quiet (muted, small) so it
     reads as an affordance, not a badge. */
  .help-ico{ display:inline-flex; align-items:center; justify-content:center;
    width:16px; height:16px; border-radius:50%; border:1px solid var(--muted);
    color:var(--muted); font-size:.68rem; font-weight:700; margin-left:6px;
    cursor:help; vertical-align:middle; user-select:none; }
  .astat .sub{ font-size:.72rem; color:var(--muted); margin-top:3px; }
  .ppm-col h4{ margin:0 0 6px; font-size:.82rem; }
  .ppm-col.best h4{ color:var(--green); } .ppm-col.moderate h4{ color:var(--amber); }
  .ppm-col.weak h4{ color:var(--red); } .ppm-col.notrec h4{ color:#6a3ab2; }
  .ppm-datelist{ list-style:none; margin:0; padding:0; font-size:.8rem; }
  .ppm-datelist li{ padding:4px 6px; border-radius:6px; cursor:default; }
  .ppm-datelist li.clickable{ cursor:pointer; }
  .ppm-datelist li.clickable:hover{ background:var(--primary-soft); }
  .ppm-reason{ margin-top:10px; padding:12px; background:var(--sunk-soft); border-radius:10px; font-size:.82rem; display:none; }
  .ppm-reason.show{ display:block; }
  .diag-line{ padding:8px 12px; border-radius:8px; margin-bottom:6px; font-size:.85rem; }
  .diag-line.yes{ background:#e6f4ea; color:#1a7f37; } .diag-line.no{ background:#fdecea; color:#b91c1c; }
  .dbatree{ max-height:220px; overflow-y:auto; border:1px solid var(--line); border-radius:10px; padding:6px; margin:6px 0; }
  .dbarow{ display:flex; gap:8px; align-items:center; padding:5px 8px; border-radius:6px; cursor:pointer; font-size:.8rem; }
  .dbarow:hover{ background:var(--primary-soft); }
  .dbarow.sel{ background:var(--primary); color:var(--on-primary); }
  .dbarow .lvl{ font-size:.68rem; color:var(--muted); background:#f0f2f5; border-radius:5px; padding:1px 6px; flex:none; }
  /* favorite-star toggle, used in Clients/Saved Charts/Sessions lists + Global Search */
  .star-btn{ padding:4px 8px; font-size:1rem; line-height:1; color:#c8ccd6; border-color:var(--line-strong); }
  .star-btn.on{ color:#f0b400; border-color:#f0d68a; background:var(--card)9ec; }
  .star-btn:hover{ color:#f0b400; }
  .dbarow.sel .lvl{ background:rgba(255,255,255,.25); color:#fff; }
  .dbachildren{ margin-left:14px; border-left:1px solid var(--line); padding-left:6px; }
  /* AI explanation card */
  /* A1: the AI card frame lives in the A1 block further down (kp_tier_benefit look). */
  .pack-row{ display:flex; flex-direction:column; gap:10px; margin-top:14px; }
  .pack-option{ display:flex; align-items:center; justify-content:space-between; gap:10px;
    background:var(--bg); border:1px solid var(--line); border-radius:12px; padding:14px 16px;
    cursor:pointer; text-align:left; font:inherit; color:var(--text); }
  .pack-option:hover{ border-color:var(--primary); background:var(--card); }
  .pack-credits{ font-weight:700; }
  .pack-price{ font-weight:700; color:var(--primary); font-size:1.1rem; }
  .ai-suggest-row{ display:flex; flex-wrap:wrap; align-items:center; gap:6px; margin-top:8px; }
  /* These all declare display:flex at the same specificity as .hidden's
     display:none, and come later in the stylesheet — so source order alone
     decided the tie and .hidden silently lost, leaving an empty styled bar
     visible whenever these elements were meant to be hidden. Force it. */
  /* These all set their own `display`, which beats the plain `.hidden` rule
     above on equal specificity (later wins) — so each needs hiding named
     explicitly or `.hidden` silently does nothing to them. */
  .ai-suggest-row.hidden, .ai-next-q.hidden, .ai-history-body.hidden,
  .ai-delbar.hidden{ display:none; }
  .ai-pack-row{ display:flex; flex-wrap:wrap; align-items:center; gap:6px; margin:10px 0 4px; }
  /* Descriptive Indicators. Deliberately quieter than the verdict block
     above it: these are traditional correspondences, not the deterministic
     judgment, and the page must not let them borrow its authority. The
     strength band is the only coloured thing, because it is the one piece of
     information a reader most needs and most easily skips. */
  .desc-card{ margin-top:14px; }
  .desc-askrow{ display:flex; gap:8px; flex-wrap:wrap; }
  .desc-input{ flex:1 1 240px; min-width:0; padding:8px 12px; border:1px solid var(--line);
    border-radius:8px; background:var(--bg); color:inherit; font:inherit; }
  .desc-list{ list-style:none; margin:12px 0 0; padding:0; display:flex;
    flex-direction:column; gap:10px; }
  .desc-list li{ display:grid; grid-template-columns:auto 1fr auto; gap:4px 10px;
    align-items:baseline; padding:8px 10px; border:1px solid var(--line); border-radius:8px; }
  .desc-list li.asked{ border-color:var(--primary); }
  .desc-list li b{ text-transform:capitalize; font-size:.8rem; letter-spacing:.02em; }
  .desc-val{ font-size:.92rem; }
  .desc-why{ grid-column:1 / -1; font-size:.76rem; }
  .desc-band{ font-size:.7rem; text-transform:uppercase; letter-spacing:.05em;
    border-radius:999px; padding:2px 8px; border:1px solid var(--line); }
  .band-strong{ border-color:#2e7d32; color:#2e7d32; }
  .band-moderate{ border-color:#b26a00; color:#b26a00; }
  .band-slight{ color:var(--muted); }
  .desc-silent{ margin:12px 0 0; font-size:.85rem; }
  .ai-tier-badge{ display:inline-block; font-size:.7rem; text-transform:uppercase;
    letter-spacing:.06em; border:1px solid var(--line); border-radius:999px;
    padding:2px 10px; margin-bottom:10px; color:var(--muted); }
  /* Dynamic per-answer BASE-tier upsell banner (2026-09-19 redesign).
     Distinct from .ai-tier-badge (the Standard/Premium acknowledgement
     pill) and from #b_ai_tier_note/#h_ai_tier_note (the static general
     explainer elsewhere in the panel). */
  .ai-tier-upsell{ display:flex; align-items:center; gap:var(--s-3); flex-wrap:wrap;
    background:var(--info-soft); border:1px solid var(--line); border-radius:var(--r-md);
    padding:var(--s-3) var(--s-4); margin-bottom:12px; }
  .ai-tier-upsell p{ margin:0; font-size:var(--fs-sm); color:var(--text); flex:1 1 220px; }
  .ai-tier-upsell button{ background:var(--primary); color:var(--on-primary); border:none;
    border-radius:var(--r-md); padding:8px 14px; font-size:var(--fs-sm); font-weight:600;
    cursor:pointer; flex:0 0 auto; }

  /* Header BAND across the top of the AI Explanation card -- a full-width
     tinted strip (existing --primary-soft token, same one used for other
     soft-highlight treatments in this app; not a new color) holding the
     feature's own icon/title/subtitle plus one tier-appropriate action on
     the right. Shape only borrowed from a reference; every string inside it
     is this app's own real wording. */
  /* A1 (kp_tier_benefit look): white header band, 28px "AI" tile, 1px rule under. */
  .ai-card-band{ display:flex; align-items:center; gap:10px; background:var(--ai-band);
    border-radius:0; margin:0; padding:12px 14px; border-bottom:1px solid var(--line); }
  .ai-card-icon{ width:28px; height:28px; border-radius:8px; background:var(--primary-soft); color:var(--primary);
    display:flex; align-items:center; justify-content:center; font-weight:800; font-size:.8rem; flex:0 0 auto; }
  .ai-card-heading{ display:flex; flex-direction:column; gap:1px; min-width:0; }
  .ai-card-title-row{ display:flex; align-items:center; gap:8px; }
  .ai-card-title{ font-weight:700; font-size:.95rem; line-height:1.2; color:var(--heading); }
  .ai-card-subtitle{ font-size:.72rem; color:var(--muted); font-weight:400; margin:0; }
  .ai-band-right{ margin-left:auto; flex:0 0 auto; }
  .ai-band-action{ background:var(--primary); color:var(--on-primary); border:none;
    border-radius:var(--r-md); padding:6px 12px; font-size:var(--fs-xs); font-weight:600; cursor:pointer; }
  /* Pre-answer idle state with credits remaining: a quieter outline
     variant, same shape as .plan-buy-btn.outline on the pricing page --
     this is a balance readout the reader can tap, not an upsell. */
  .ai-band-action.outline{ background:transparent; color:var(--primary);
    border:1px solid var(--primary); }
  .ai-card-band .ai-tier-badge{ margin:0; font-size:.7rem; font-weight:700; text-transform:none; letter-spacing:0;
    border-radius:6px; padding:1px 7px; background:var(--sunk); color:var(--muted); border:1px solid var(--line); }
  #b_ai_card .ai-mic-btn, #h_ai_card .ai-mic-btn{ display:inline-flex; align-items:center; justify-content:center; color:var(--primary); }
  #b_ai_card .ai-mic-btn.listening, #h_ai_card .ai-mic-btn.listening{ color:#fff; }

  /* ---- AI panel redesign (2026-09-19), Birth/Horary only -- built from
     the owner-approved docs/redesign-mockup/ai_panel_mock.html. Reuses the
     EXISTING .ai-card-band/.ai-tier-badge/.ai-band-right header machinery
     above (shared with Financial/Compatibility/Muhurta, untouched by this
     pass) rather than a parallel header class, per the mock's .aihead --
     the mock's meter now lives in #{p}_ai_band_action, merged with the
     tier pill in the same band, never a second element. New class names
     below are prefixed ai2- specifically to avoid any collision with this
     stylesheet's ~8000 existing rules (the mock's own bare names like
     .verdict, .chip, .tag, .thread, .hist, .ans are already real classes
     elsewhere on this page with different meanings) -- structure, spacing
     and behavior follow the mock; only the class prefix differs. */
  .ai2-meter{ display:inline-flex; align-items:center; gap:6px; flex:none;
    border:1px solid var(--line); border-radius:999px;
    padding:2px 9px; font-size:.74rem; color:var(--muted);
    background:none; white-space:nowrap; }
  .ai2-meter b{ color:var(--text); font-variant-numeric:tabular-nums; }
  .ai2-meter.low b{ color:var(--accent); }
  .ai2-meter a{ color:var(--primary); font-weight:600; text-decoration:none; }
  .ai2-meter a:hover{ text-decoration:underline; }

  .ai2-tabs{ display:flex; gap:2px; padding:0 var(--s-5); border-bottom:1px solid var(--line);
    background:var(--surface-2); margin:0 -18px; }
  .ai2-tab{ appearance:none; background:none; border:0; cursor:pointer; position:relative;
    padding:11px 2px; margin-right:22px; font:inherit; font-size:var(--fs-sm);
    font-weight:600; color:var(--text-muted); }
  .ai2-tab[aria-selected="true"]{ color:var(--primary); }
  .ai2-tab[aria-selected="true"]::after{ content:""; position:absolute; left:0; right:0; bottom:-1px;
    height:2px; background:var(--primary); border-radius:2px 2px 0 0; }
  .ai2-tab .tag{ display:inline-block; margin-left:7px; font-size:9px; font-weight:800;
    letter-spacing:.06em; padding:2px 6px; border-radius:var(--r-pill);
    background:var(--gold); color:#3a2a00; vertical-align:1px; }
  .ai2-tab:focus-visible{ outline:2px solid var(--primary); outline-offset:2px; }

  .ai2-composer{ border:1px solid var(--line-strong); border-radius:var(--r-md); background:var(--card);
    transition:box-shadow var(--dur) var(--ease), border-color var(--dur) var(--ease); margin-top:var(--s-3); }
  .ai2-composer:focus-within{ border-color:var(--primary); box-shadow:var(--focus-ring); }
  .ai2-composer textarea{ display:block; width:100%; box-sizing:border-box; border:0; resize:none;
    background:none; color:var(--text); font:inherit; font-size:var(--fs-sm);
    padding:13px 14px 4px; outline:none; }
  .ai2-composer textarea::placeholder{ color:var(--text-faint); }
  .ai2-ctools{ display:flex; align-items:center; gap:var(--s-2); padding:8px 10px 10px 12px; flex-wrap:wrap; }
  .ai2-ctools .grow{ flex:1; }
  .ai2-seg{ display:inline-flex; border:1px solid var(--line); border-radius:var(--r-pill);
    overflow:hidden; background:var(--surface-2); }
  .ai2-seg button{ appearance:none; border:0; background:none; cursor:pointer; font:inherit;
    font-size:var(--fs-xs); color:var(--text-muted); padding:5px 11px; }
  .ai2-seg button[aria-pressed="true"]{ background:var(--card); color:var(--text); font-weight:600;
    box-shadow:var(--shadow-1); }
  .ai2-seg button:focus-visible{ outline:2px solid var(--primary); outline-offset:1px; }
  .ai2-send{ appearance:none; border:0; cursor:pointer; font:inherit; font-size:var(--fs-sm);
    font-weight:700; color:var(--on-primary); background:var(--primary);
    border-radius:var(--r-md); padding:8px 18px; }
  .ai2-send:hover{ background:var(--primary-hover); }
  .ai2-send[disabled]{ background:var(--line-strong); color:var(--card); cursor:not-allowed; }

  .ai2-starters{ display:flex; flex-wrap:wrap; gap:var(--s-2); margin-top:var(--s-3); align-items:center; }
  .ai2-starters .lbl{ font-size:var(--fs-xs); color:var(--text-faint); margin-right:2px; }
  .ai2-chip{ appearance:none; cursor:pointer; font:inherit; font-size:var(--fs-xs);
    border:1px solid var(--line); background:var(--card); color:var(--text);
    border-radius:var(--r-pill); padding:6px 13px; }
  .ai2-chip:hover{ border-color:var(--primary); color:var(--primary); background:var(--primary-soft); }

  .ai2-lockbar{ display:flex; align-items:center; gap:var(--s-3); margin-top:var(--s-3);
    border:1px solid var(--line); border-left:3px solid var(--accent);
    background:var(--surface-2); border-radius:var(--r-md); padding:11px 14px; flex-wrap:wrap; }
  .ai2-lockbar p{ margin:0; font-size:var(--fs-xs); color:var(--text-muted); flex:1; line-height:1.5; }
  .ai2-lockbar b{ color:var(--text); }
  .ai2-btn-sm{ appearance:none; cursor:pointer; font:inherit; font-size:var(--fs-xs); font-weight:700;
    border-radius:var(--r-md); padding:7px 15px; white-space:nowrap; flex:none;
    border:1px solid var(--primary); background:var(--primary); color:var(--on-primary);
    text-decoration:none; display:inline-block; }
  .ai2-btn-sm:hover{ background:var(--primary-hover); }

  .ai2-thread{ display:flex; flex-direction:column; gap:var(--s-4); }
  .ai2-qrow{ display:flex; justify-content:flex-end; }
  .ai2-qbub{ background:var(--primary-soft); color:var(--on-soft-primary); border-radius:var(--r-lg);
    border-top-right-radius:var(--r-sm); padding:10px 15px; font-size:var(--fs-sm);
    max-width:78%; font-weight:600; }
  .ai2-ansfoot{ display:flex; align-items:center; gap:var(--s-3); padding-top:10px;
    border-top:1px solid var(--line); margin-top:4px; flex-wrap:wrap; }
  .ai2-tierline{ font-size:var(--fs-xs); color:var(--text-muted); margin-left:auto; text-align:right; }
  .ai2-tierline a{ color:var(--primary); font-weight:600; text-decoration:none; }
  .ai2-tierline a:hover{ text-decoration:underline; }
  .ai2-tierdot{ display:inline-block; width:6px; height:6px; border-radius:50%;
    background:var(--text-faint); margin-right:6px; vertical-align:1px; }

  .ai2-followups{ margin-top:var(--s-5); }
  .ai2-followups .lbl{ font-size:var(--fs-xs); color:var(--text-faint); display:block; margin-bottom:var(--s-2); }
  .ai2-fgrid{ display:grid; grid-template-columns:1fr 1fr; gap:var(--s-2); }
  .ai2-fq{ appearance:none; cursor:pointer; font:inherit; text-align:left; font-size:var(--fs-xs);
    border:1px solid var(--line); background:var(--card); color:var(--text);
    border-radius:var(--r-md); padding:10px 13px; line-height:1.45; }
  .ai2-fq:hover{ border-color:var(--primary); background:var(--primary-soft); }

  .ai2-hist{ margin-top:var(--s-5); border-top:1px solid var(--line); padding-top:var(--s-3); }
  .ai2-hist-toggle{ cursor:pointer; list-style:none; font-size:var(--fs-xs); font-weight:600;
    color:var(--text-muted); display:flex; align-items:center; gap:7px; background:none; border:0; font:inherit;
    appearance:none; padding:0; }
  .ai2-hist-toggle::before{ content:"▸"; color:var(--text-faint); }
  .ai2-hist-toggle[aria-expanded="true"]::before{ content:"▾"; }
  .ai2-hist-toggle:hover{ color:var(--primary); }
  .ai2-hist-count{ background:var(--surface-2); border-radius:var(--r-pill); padding:1px 8px;
    font-size:10px; color:var(--text-muted); }

  .ai2-le-lead{ font-size:var(--fs-sm); color:var(--text-muted); margin:0 0 var(--s-4); }
  .ai2-le-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s-3); }
  .ai2-le-card{ border:1px solid var(--line); border-radius:var(--r-md); padding:14px;
    background:var(--surface-2); }
  .ai2-le-card .t{ font-size:var(--fs-sm); font-weight:700; margin:0 0 4px; }
  .ai2-le-card .w{ font-size:var(--fs-xs); color:var(--text-muted); margin:0; }
  .ai2-le-card.locked .w{ filter:blur(3.5px); user-select:none; }

  @media (max-width:600px){
    .ai2-fgrid, .ai2-le-grid{ grid-template-columns:1fr; }
    .ai-card-band{ flex-wrap:wrap; }
    .ai2-meter{ order:3; width:100%; justify-content:center; margin-top:6px; }
    .ai2-ansfoot{ flex-wrap:wrap; }
    .ai2-tierline{ margin-left:0; width:100%; text-align:left; }
  }

  /* ---- Part C: dedicated pricing page, GRID (2026-09-21 redesign,
     replaces the native-scroll carousel -- see docs/redesign-mockup/
     pricing_mock.html, owner-approved). All tiles always visible, no
     slider mechanics. Reuses theme.css tokens, not the mockup's inlined
     hex values. ---- */
  .page-title{ font-family:var(--font-serif); color:var(--heading); font-size:var(--fs-2xl); margin:0 0 4px; text-align:center; }
  .page-sub{ margin:0 0 28px; font-size:var(--fs-md); text-align:center; }
  .plans-grid{ display:grid; gap:var(--s-3); margin:0 auto; max-width:1360px;
    grid-template-columns:repeat(5,1fr); align-items:stretch;
    padding-left:env(safe-area-inset-left); padding-right:env(safe-area-inset-right); }
  @media (max-width:1180px){ .plans-grid{ grid-template-columns:repeat(3,1fr); } }
  @media (max-width:960px){ .plans-grid{ grid-template-columns:repeat(2,1fr); } }
  @media (max-width:560px){
    .plans-grid{ grid-template-columns:1fr; gap:var(--s-2); }
    /* recommended tier leads on phones, not stuck second */
    .plan-tile.is-standard{ order:-1; }
    .plan-buy-btn{ padding:13px 14px; font-size:1rem; min-height:44px; }
  }
  /* 1-3 tile case (filtered buy-prompt / gated redirect): center the row
     instead of the grid stretching sparse tiles full-width or left-aligning. */
  .plans-grid.plans-grid-narrow{ grid-template-columns:repeat(auto-fit, minmax(240px, 280px));
    justify-content:center; }
  @media (max-width:560px){ .plans-grid.plans-grid-narrow{ grid-template-columns:1fr; max-width:360px; } }
  .plan-tile{ background:var(--card); border:1px solid var(--line);
    border-radius:var(--r-lg); box-shadow:var(--shadow-2); padding:var(--s-4); box-sizing:border-box;
    display:flex; flex-direction:column; gap:var(--s-2); position:relative;
    transition:transform .15s ease, box-shadow .15s ease; }
  .plan-tile:hover{ transform:translateY(-3px); box-shadow:var(--shadow-3); }
  .plan-tile.is-standard{ border-color:var(--primary); border-width:1.5px; box-shadow:var(--shadow-3); }
  .plan-tile.is-highlighted{ box-shadow:0 0 0 3px var(--primary-soft), var(--shadow-3); }
  .plan-ribbon{ position:absolute; top:-11px; left:50%; transform:translateX(-50%);
    background:var(--primary); color:var(--on-primary); font-size:.66rem; font-weight:700;
    letter-spacing:.08em; text-transform:uppercase; padding:4px 12px; border-radius:var(--r-pill);
    white-space:nowrap; }
  /* Custom plans (custom_plans.py): gold accent on the pricing tile. */
  .plan-tile.is-gold{ border-color:#b8862b; border-width:1.5px; }
  .plan-ribbon.is-gold{ background:#b8862b; color:#fff; }
  /* Admin > Custom plans editor */
  .cp-rail{ display:flex; gap:8px; overflow-x:auto; padding-bottom:4px; margin:10px 0 14px; }
  .cp-chip{ flex:0 0 auto; min-width:120px; text-align:left; background:var(--surface,#fff); border:1px solid var(--border,#e2e5ee);
    border-radius:12px; padding:8px 12px; cursor:pointer; font:inherit; color:inherit; }
  .cp-chip small{ display:block; color:var(--muted); font-size:.72rem; }
  .cp-chip[disabled]{ cursor:default; opacity:.7; }
  .cp-chip.sel{ border-color:#b8862b; box-shadow:0 0 0 3px rgba(184,134,43,.18); }
  .cp-chip.add{ border-style:dashed; color:var(--primary); font-weight:600; }
  .cp-editor{ display:grid; grid-template-columns:minmax(0,1fr) 280px; gap:18px; align-items:start; }
  @media (max-width:820px){ .cp-editor{ grid-template-columns:1fr; } }
  .cp-sec{ padding:14px 0; border-top:1px solid var(--border,#e2e5ee); }
  .cp-sec:first-child{ border-top:0; padding-top:0; }
  .cp-sec h4{ margin:0 0 2px; } .cp-sec .sub{ margin:0 0 10px; }
  .cp-grid2{ display:grid; grid-template-columns:1fr 1fr; gap:10px; }
  @media (max-width:520px){ .cp-grid2{ grid-template-columns:1fr; } }
  .cp-base{ background:var(--primary-soft); border-radius:10px; padding:10px 12px; display:flex; gap:10px; align-items:center; flex-wrap:wrap; }
  .cp-ovr{ display:flex; justify-content:space-between; align-items:center; gap:10px; padding:8px 0; border-top:1px solid var(--border,#eef0f5); flex-wrap:wrap; }
  .cp-ovr:first-of-type{ border-top:0; }
  .cp-ovr .v{ font-size:.76rem; color:var(--muted); }
  .cp-diff{ font-size:.68rem; font-weight:700; color:#8a6214; background:#fbf1dc; padding:1px 7px; border-radius:99px; margin-left:6px; }
  .cp-preview{ position:sticky; top:12px; }
  .cp-preview .plan-tile{ pointer-events:none; }
  .plan-eyebrow{ font-size:var(--fs-xs); text-transform:uppercase; letter-spacing:.06em; color:var(--primary); font-weight:600; margin:0; }
  .plan-name{ font-family:var(--font-serif); font-size:var(--fs-xl); color:var(--heading); margin:0; }
  .plan-price-row{ display:flex; align-items:baseline; gap:var(--s-2); margin:var(--s-1) 0 0; }
  .plan-price{ font-size:var(--fs-2xl); font-weight:700; color:var(--text); }
  .plan-credits{ font-size:var(--fs-sm); color:var(--text-muted); margin:0 0 var(--s-2); }
  .plan-benefits{ list-style:none; margin:0 0 var(--s-3); padding:0; display:flex; flex-direction:column;
    gap:var(--s-2); flex:1 1 auto; }
  .plan-benefits li{ font-size:var(--fs-sm); color:var(--text); display:flex; align-items:flex-start; gap:var(--s-2); }
  .plan-benefits li::before{ content:"✓"; color:var(--success); font-weight:700; }
  .plan-buy-btn{ margin-top:auto; background:var(--primary); color:var(--on-primary); border:none;
    border-radius:var(--r-md); padding:10px 16px; font-size:var(--fs-sm); font-weight:600;
    cursor:pointer; box-shadow:var(--shadow-1); text-align:center; text-decoration:none; display:block; width:100%; }
  .plan-buy-btn.outline{ background:transparent; color:var(--primary); border:1px solid var(--primary);
    padding:10px 16px; font-size:var(--fs-sm); margin-top:0; width:auto; flex:0 0 auto; }
  /* monthly subscription -- its own full-width band below the grid, no
     longer wedged inside the Basic tile */
  .plans-monthly{ margin:var(--s-4) auto 0; max-width:1120px; background:var(--surface-2);
    border:1px solid var(--line); border-radius:var(--r-lg); padding:var(--s-3) var(--s-4);
    display:flex; align-items:center; gap:var(--s-4); flex-wrap:wrap; }
  .plans-monthly:empty{ display:none; }
  .plans-monthly .recurring-badge{ display:inline-flex; align-items:center; gap:4px; font-size:var(--fs-xs);
    background:var(--accent-soft, var(--info-soft)); color:var(--accent, var(--info)); border-radius:var(--r-pill);
    padding:5px 11px; white-space:nowrap; font-weight:700; letter-spacing:.04em; text-transform:uppercase; }
  .plans-monthly .m-body{ flex:1 1 260px; min-width:0; }
  .plans-monthly .m-title{ font-weight:700; font-size:var(--fs-md); margin-bottom:2px; }
  .plans-monthly .m-title span{ color:var(--accent, var(--primary)); }
  .plans-monthly .m-sub{ font-size:var(--fs-sm); color:var(--text-muted); }
  .plans-foot{ text-align:center; margin-top:var(--s-4); font-size:var(--fs-xs); color:var(--text-faint);
    display:flex; gap:8px; justify-content:center; align-items:center; flex-wrap:wrap;
    padding-bottom:env(safe-area-inset-bottom); }
  .plans-foot .dot{ width:4px; height:4px; border-radius:50%; background:var(--text-faint); }
  @media (max-width:560px){
    .plans-monthly{ flex-direction:column; align-items:stretch; text-align:center; gap:var(--s-3); }
    .plans-monthly .recurring-badge{ align-self:center; }
    .plans-monthly .plan-buy-btn.outline{ width:100%; min-height:44px; }
  }
  .ai-evidence-strength{ margin-bottom:10px; }
  .ai-evidence-strength>div{ display:flex; align-items:center; flex-wrap:wrap; gap:6px; }
  .ai-evidence-strength-caption{ margin:4px 0 0; font-size:.72rem; }
  .desc-ai-out{ margin-top:12px; }
  .desc-ai-answer{ border-left:2px solid var(--primary); padding-left:12px; font-size:.92rem; }
  .desc-note{ margin:12px 0 0; font-size:.76rem; }
  .desc-teaser{ margin-top:14px; padding-top:12px; border-top:1px dashed var(--line);
    display:flex; align-items:center; gap:12px; flex-wrap:wrap; }
  .desc-teaser .abtn{ white-space:nowrap; }
  @media (max-width:520px){
    .desc-list li{ grid-template-columns:1fr; }
  }
  .ai-suggest-chip{ background:var(--bg); border:1px solid var(--line); color:var(--muted); border-radius:999px;
    padding:4px 12px; font-size:.78rem; cursor:pointer; line-height:1.3; }
  .ai-suggest-chip:hover{ background:var(--primary); border-color:var(--primary); color:var(--on-primary); }
  .ai-suggest-chip.active{ background:var(--primary); border-color:var(--primary); color:var(--on-primary); font-weight:600; }
  /* The model's own tailored next-question suggestion — distinct from the
     generic static "Follow up:" chips below it, so it reads as specific to
     the answer just given, not another canned prompt. */
  .ai-next-q{ display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin-top:12px;
    padding:10px 12px; background:transparent; border:1px solid var(--line); border-radius:10px; }
  .ai-suggest-chip.primary{ background:var(--primary-soft); border-color:var(--btn-soft-line);
    color:var(--primary); font-weight:600; }
  .ai-suggest-chip.primary:hover{ background:var(--btn-soft-hover); border-color:var(--primary); }
  /* Live question-shape warning under a question box — advisory, shown
     while typing, before anything is submitted or any credit is spent. */
  .qcheck{ margin-top:6px; padding:8px 10px; border-radius:8px; font-size:.82rem; line-height:1.45;
    background:var(--card)8e6; border:1px solid var(--gold); border-left:4px solid var(--gold); color:var(--text); }
  .ai-qtools-row{ display:flex; align-items:center; gap:8px; margin-top:10px; }
  .ai-mic-btn{ background:var(--bg); border:1px solid var(--line); border-radius:8px; width:34px; height:34px;
    font-size:1rem; cursor:pointer; flex:none; }
  .ai-mic-btn.listening{ background:var(--red); color:#fff; border-color:var(--red); animation:pulse-mic 1s infinite; }
  @keyframes pulse-mic{ 0%,100%{opacity:1} 50%{opacity:.55} }
  .ai-level-select{ font-size:.8rem; padding:5px 8px; border-radius:8px; border:1px solid var(--line); background:var(--card); color:var(--text); }
  .ai-history-toggle{ cursor:pointer; user-select:none; color:var(--primary); font-size:.85rem; margin-top:14px; font-weight:600; }
  .ai-history-body{ margin-top:8px; display:flex; flex-direction:column; gap:6px; max-height:280px; overflow-y:auto; }
  .ai-history-item{ background:var(--bg); border:1px solid var(--line); border-radius:10px; padding:8px 10px; cursor:pointer; font-size:.82rem; }
  .ai-history-item:hover{ border-color:var(--primary); }
  /* Grid, not flex: the question text must be free to wrap while the delete
     control stays pinned top-right and never gets squeezed onto its own row. */
  .ai-history-item{ display:grid; grid-template-columns:1fr auto; align-items:start; gap:0 8px; }
  .ai-history-item .q{ font-weight:600; grid-column:1; }
  .ai-history-item .d{ color:var(--muted); font-size:.72rem; margin-top:2px; grid-column:1; }
  .ai-history-del{ grid-column:2; grid-row:1 / span 2; align-self:center;
                   background:none; border:1px solid transparent; border-radius:8px;
                   color:var(--muted); font-size:1rem; line-height:1; cursor:pointer;
                   padding:4px 8px; }
  .ai-history-del:hover, .ai-history-del:focus-visible{
                   color:var(--red); border-color:var(--red); background:rgba(200,40,40,.07); }
  .ai-history-del[disabled]{ opacity:.5; cursor:default; }
  /* Two-step confirm, inline — a saved answer costs a credit to regenerate,
     so a stray tap must not be able to destroy one. */
  .ai-history-item.confirming{ border-color:var(--red); }
  .ai-history-del.confirming{ color:var(--red); border-color:var(--red); font-size:.74rem;
                              font-weight:700; letter-spacing:.02em; }
  /* Delete control for the answer currently on screen. Sits directly under
     the explanation, right-aligned and quiet — it is a rarely-wanted action
     next to a paid-for answer, so it must be findable without competing
     with the follow-up chips below it. */
  .ai-delbar{ display:flex; justify-content:flex-end; margin-top:10px; }
  .ai-delbar button{ background:none; border:1px solid var(--line); border-radius:8px;
                     color:var(--muted); font-size:.78rem; font-weight:600;
                     cursor:pointer; padding:5px 12px; }
  .ai-delbar button:hover, .ai-delbar button:focus-visible{
                     color:var(--red); border-color:var(--red); background:rgba(200,40,40,.07); }
  .ai-delbar button[disabled]{ opacity:.5; cursor:default; }
  .ai-delbar button.confirming{ color:var(--red); border-color:var(--red); }
  .ai-handoff-btn{ display:inline-block; margin-top:12px; text-decoration:none; }
  /* ---- first-visit value/trust/demo — see "show the payoff before asking
     for input" UX pass ---- */
  .sidebar-trust{ font-size:.68rem; color:rgba(255,255,255,.5); line-height:1.4;
    margin-top:auto; padding:14px 4px 0; border-top:1px solid rgba(255,255,255,.12); }
  .navgroup-label{ font-size:.62rem; text-transform:uppercase; letter-spacing:.06em; color:var(--muted);
        margin:14px 14px 4px; opacity:1; }
  .navgroup-label:first-of-type{ margin-top:6px; }
  .chart-2col{ display:grid; grid-template-columns:1fr 1fr; gap:16px; align-items:start; }
  /* Both columns are independent flex stacks — NOT relying on CSS Grid's
     implicit row-2 auto-placement, which used to wait for row 1's *tallest*
     cell (the chart output) before starting the next item, visually pushing
     AI Explanation / PPM tools to the bottom of the page regardless of how
     short the form column itself was. */
  .chart-2col-left{ display:flex; flex-direction:column; gap:16px; min-width:0; }
  .chart-2col-right{ display:flex; flex-direction:column; gap:16px; min-width:0; }
  @media (max-width:900px){ .chart-2col{ grid-template-columns:1fr; } }
  .hero-value{ font-weight:700; font-size:1.02rem; color:var(--heading); margin:0 0 4px; }
  .hero-sub{ color:var(--muted); font-size:.84rem; margin:0 0 14px; }
  .demo-cta{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-top:10px; }
  .abtn.demo-toolbar-btn{ background:linear-gradient(135deg,var(--accent),var(--now)); color:var(--on-status); border-color:transparent; font-weight:800; }
  .abtn.demo-toolbar-btn:hover{ filter:brightness(1.06); }
  .form-hint{ color:var(--muted); font-size:.78rem; margin-top:8px; }
  .ai-preview{ position:relative; background:var(--sunk-soft); border:1px solid var(--line); border-radius:10px;
    padding:14px; margin-bottom:12px; overflow:hidden; min-height:52px; }
  .ai-preview-overlay{ position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
    background:linear-gradient(180deg,rgba(245,246,247,.15),rgba(245,246,247,.85) 60%); }
  .ai-preview-overlay span{ font-size:.78rem; font-weight:700; color:var(--primary); background:var(--card);
    border:1px solid var(--line-strong); border-radius:20px; padding:5px 12px; }
  .airow{ display:flex; gap:18px; margin:10px 0 2px; }
  .radio{ display:flex; align-items:center; gap:6px; font-size:.9rem; color:var(--text); font-weight:600; margin:0; cursor:pointer; }
  .radio input{ width:auto; }
  .chkline{ display:flex; align-items:center; gap:7px; font-size:.82rem; color:var(--muted); margin:10px 0 2px; cursor:pointer; }
  .chkline input{ width:auto; }
  .aiout{ margin:0; background:none; border:0; border-radius:0; padding:0; max-height:none; overflow:visible;
          word-wrap:break-word; font-size:.94rem; line-height:1.65; display:flex; flex-direction:column; gap:0; }
  /* Chat-log mode (2026-09-02): #{p}_ai_out is the whole scrolling
     conversation, appended to per turn rather than replaced — each turn is
     one user bubble (the question, right-aligned, filled) followed by one
     AI bubble (the answer, left-aligned, plain). Everything the old single-
     answer layout rendered (evidence strength, tier badge, the deterministic
     "Question Analysis" header) still renders exactly as before, just INSIDE
     that turn's own AI bubble instead of the shared container. */
  .ai-bubble{ max-width:88%; border-radius:14px; padding:10px 14px; } /* A1 overrides below */
  .ai-bubble.user{ align-self:flex-end; background:var(--primary); color:var(--on-primary);
                    border-bottom-right-radius:4px; white-space:pre-wrap; font-size:.92rem; }
  .ai-bubble.ai{ align-self:flex-start; background:var(--card); border:1px solid var(--line);
                 border-bottom-left-radius:4px; width:100%; }
  .aiout p{ margin:0 0 12px; }
  .aiout p:last-child{ margin-bottom:0; }
  .aiout ul, .aiout ol{ margin:0 0 12px; padding-left:22px; }
  .aiout li{ margin:3px 0; }
  .aiout h4{ margin:16px 0 8px; font-size:1.02rem; color:var(--heading); }
  .aiout h4:first-child{ margin-top:0; }
  .aiout strong{ color:var(--heading); }
  /* Question Analysis / Astrological Logic / Validity Check — the
     deterministic header ai_explain.analysis_header() always prepends.
     Rendered as a labeled "verdict card" so it visually reads as "how this
     was judged", separated from the model's own prose answer below it,
     rather than three more sentences indistinguishable from the narrative.
     display:contents on each row lets the grid align every label/value
     pair into two shared columns, instead of each row negotiating its own
     label width independently (which is what a flex-row-per-line layout
     does, and it visibly zigzags once Domain Confidence/Note make some
     labels longer than others). */
  /* Live text while the explanation streams in. Deliberately plain: this is
     half-written markdown, so it is shown as pre-wrapped plain text and only
     handed to the formatter once the full answer has arrived. */
  .ai-streaming{ white-space:pre-wrap; line-height:1.65; color:var(--text); font-size:.95rem; }
  .ai-caret{ display:inline-block; width:8px; height:1.05em; margin-left:2px;
             vertical-align:text-bottom; background:var(--primary); border-radius:1px;
             animation:ai-blink 1s steps(1,end) infinite; }
  @keyframes ai-blink{ 50%{ opacity:0; } }
  @media (prefers-reduced-motion: reduce){ .ai-caret{ animation:none; } }
  /* Own border+shadow+rounded corners removed 2026-09-19: with its own full
     box treatment sitting directly under the header band (also its own
     tinted box) at the very top of #{p}_ai_out/.ai-bubble.ai, this read as
     a THIRD separate floating card rather than the first labeled block of
     the answer -- reported live, repeatedly, as ".ai-meta looks like it's
     outside the AI Explanation card" even though DOM nesting was always
     correct (.ai-meta is the first child of the card's own answer
     container, confirmed by direct trace). A left accent bar + bottom
     border now ties it visually into the SAME surface as the prose below
     it, instead of looking like a disconnected component. */
  /* Below ~420px an "auto" label column (e.g. "Domain Confidence") eats
     most of the width, squeezing the value column into a narrow ragged
     strip. Stack label-above-value instead -- .ai-meta-row is
     display:contents normally (see below), so this only needs to turn
     that off and let each row's own children flow as a normal block. */
  @media (max-width:420px){
    .ai-meta-row{ display:block !important; margin-bottom:2px; }
    .ai-meta-label{ display:block; margin-bottom:2px; }
  }
  /* U4 cusp check (docs/u4_cusp_check_plan.md §7, mockup 02): the "not fully
     verified" line. Sans, a word label so the state never rests on colour
     alone, the same caution rule (--gold) the meta area already uses.
     Sits directly under the meta row it qualifies, its own field
     (cusp_check_notice), never inside the ranked .multiq-note slot. The
     "Change" correction editor (fc-*) that used to live here is retired
     (owner Q2): POST /api/frame-correction is gone. */
  .tr-unverified{ margin-top:8px; padding:4px 0 4px 10px; border-left:3px solid var(--gold);
    font-size:.82rem; line-height:1.45; color:var(--muted); max-width:70ch; }
  .tr-unverified b{ color:var(--text); font-weight:700; }
  /* U2 mandatory check (docs/mockups/kp_model_unhardcode/unverified.html,
     state A): the "couldn't check how your question was read" card shown in
     place of a reading. Values are the mockup's own (card, 14px radius, 18px
     padding; question 600/heading; meta 13px muted; h3 17px; primary button),
     on the app's own theme tokens. Used by the horary result page and by the
     paid AI Explanation. */
  .uv-card{ background:var(--card); border:1px solid var(--line); border-radius:14px; padding:18px; min-width:0;
    font-size:15px; line-height:1.55; }
  .uv-q{ font-weight:600; color:var(--heading); margin:0 0 4px; }
  .uv-meta{ font-size:13px; color:var(--muted); }
  /* Specificity is deliberate: inside the AI card (.aiout) the shared
     ".aiout p" (margin 0 0 12px) and the page's own h3 rules would otherwise
     win and break parity with the mockup. */
  .uv-card h3.uv-h{ margin:14px 0 6px; color:var(--heading); font-size:17px; line-height:1.55; font-weight:700;
    font-family:inherit; }
  .uv-card h3.uv-h.uv-h0{ margin-top:0; }
  .uv-card p.uv-p{ margin:0; }
  .uv-card p.uv-p.uv-p2{ margin:8px 0 0; }
  /* --on-primary is the app's own "ink on --primary" token: white in light, dark ink
     in dark -- the mockup's #fff / #1c2138 pair, on the design system's token. */
  .uv-try{ display:inline-block; margin-top:12px; background:var(--primary); color:var(--on-primary); border:0;
    border-radius:10px; padding:9px 18px; font:inherit; font-weight:600; cursor:pointer; }
  /* Vedic Predictions tab (renderVedicBhava). Every colour reads a theme
     variable -- this shipped after the dark-mode contrast fix, so it is built
     theme-aware rather than retrofitted. */
  .vb-row{ border:1px solid var(--line); border-radius:10px; padding:12px 14px; margin-bottom:10px;
    background:var(--card); }
  .vb-head{ display:flex; align-items:baseline; gap:10px; flex-wrap:wrap; }
  .vb-graha{ font-weight:700; font-size:1rem; color:var(--heading); }
  .vb-place{ font-size:.84rem; color:var(--muted); font-variant-numeric:tabular-nums; }
  .vb-dig{ font-size:.76rem; font-weight:700; letter-spacing:.05em; text-transform:uppercase;
    border-radius:99px; padding:3px 10px; background:var(--card); color:var(--muted);
    border:1px solid var(--line-strong); }
  .vb-dig.good{ color:var(--dig-good); border-color:var(--dig-good); }
  .vb-dig.bad{ color:var(--dig-bad); border-color:var(--dig-bad); }
  .vb-theme{ margin:2px 0 8px; font-size:.75rem; color:var(--faint-text); letter-spacing:.02em; }
  .vb-reading{ margin:0; font-size:.9rem; line-height:1.6; color:var(--text); }
  .vb-note{ margin:6px 0 0; font-size:.82rem; color:var(--muted); font-style:italic; }
  /* Lordship + dasha: the two chart-SPECIFIC layers. Given a label chip and a
     rule so they read as derived facts about this chart, not more prose. */
  .vb-lord, .vb-dasha{ margin:8px 0 0; padding:7px 10px; border-radius:8px;
    background:var(--sunk-soft); font-size:.83rem; line-height:1.55; color:var(--text); }
  .vb-lbl{ display:inline-block; font-size:.62rem; font-weight:800; letter-spacing:.07em;
    text-transform:uppercase; color:var(--muted); margin-right:8px; vertical-align:1px; }
  .vb-dasha{ border-left:3px solid var(--line-strong); border-radius:0 8px 8px 0; }
  .vb-dasha.now{ border-left-color:var(--dig-good); }
  .vb-dasha.now .vb-lbl{ color:var(--dig-good); }
  .vb-dasha.past{ opacity:.72; }
  .vb-mods{ margin:8px 0 0; padding-left:18px; }
  .vb-mods li{ font-size:.82rem; color:var(--muted); margin:3px 0; line-height:1.5; }
  .vb-tags{ margin-top:8px; display:flex; gap:6px; flex-wrap:wrap; }
  /* --heading, not --primary: as small text on --primary-soft the latter
     measures 3.41:1 in dark (under AA), --heading 5.91:1. */
  .vb-tag{ font-size:.76rem; font-weight:700; letter-spacing:.03em; padding:3px 10px; border-radius:99px;
    background:var(--primary-soft); color:var(--heading); border:1px solid var(--btn-soft-line); }
  /* The house-system divergence notice. Deliberately prominent: a Parashari
     sentence shown against a KP house number would be quietly wrong, so where
     the systems disagree the reader is told, not left to assume. */
  .vb-diff{ margin:8px 0 0; padding:8px 10px; border-left:3px solid var(--amber);
    background:var(--sunk-soft); border-radius:0 8px 8px 0; font-size:.8rem; color:var(--muted); line-height:1.5; }
  .vb-sysnote{ padding:10px 12px; border:1px solid var(--amber); border-radius:10px;
    background:var(--sunk-soft); font-size:.82rem; color:var(--text); line-height:1.55; margin:0 0 12px; }
  /* Doshas tab (renderDoshas). Shares the .vb-* vocabulary above -- same
     tradition, same whole-sign houses, so it should not look like a different
     product. What is deliberately NOT borrowed is alarm colouring: a finding
     that half of all charts carry must not be painted like a warning, so a
     present dosha is neutral-bordered and only the cancellation panel is
     given a colour (green -- it is the reassuring half, and the half most
     other sites omit). */
  .dsh-card{ border:1px solid var(--line); border-radius:10px; padding:14px 16px; margin-bottom:12px;
    background:var(--card); }
  .dsh-head{ display:flex; align-items:baseline; gap:10px; flex-wrap:wrap; margin-bottom:2px; }
  .dsh-name{ font-weight:700; font-size:1rem; color:var(--heading); }
  .dsh-verdict{ font-size:.76rem; font-weight:800; letter-spacing:.05em; text-transform:uppercase;
    border-radius:99px; padding:3px 10px; border:1px solid var(--line-strong);
    background:var(--card); color:var(--muted); }
  .dsh-verdict.present{ color:var(--heading); border-color:var(--btn-soft-line); background:var(--primary-soft); }
  .dsh-verdict.absent{ color:var(--dig-good); border-color:var(--dig-good); }
  .dsh-sub{ margin:2px 0 10px; font-size:.75rem; color:var(--faint-text); letter-spacing:.02em; }
  .dsh-reason{ margin:0 0 8px; font-size:.88rem; line-height:1.6; color:var(--text); }
  .dsh-list{ margin:6px 0 0; padding-left:18px; }
  .dsh-list li{ font-size:.84rem; color:var(--muted); margin:4px 0; line-height:1.55; }
  /* The cancellations. Given the most visual weight in the whole tab, because
     they are the half of the tradition a frightened reader most needs and
     least often gets. */
  .dsh-cancel{ margin:10px 0 0; padding:10px 12px; border-left:3px solid var(--dig-good);
    background:var(--sunk-soft); border-radius:0 8px 8px 0; }
  .dsh-cancel .vb-lbl{ color:var(--dig-good); }
  .dsh-cancel li{ color:var(--text); }
  /* Base rate: the context line that turns a verdict back into a statistic. */
  .dsh-rate{ margin:8px 0 0; padding:8px 10px; border-radius:8px; background:var(--sunk-soft);
    font-size:.8rem; color:var(--muted); line-height:1.5; }
  .dsh-remedy{ border:1px solid var(--line); border-radius:10px; padding:12px 14px;
    background:var(--card); margin-bottom:10px; }
  .dsh-remedy dl{ display:grid; grid-template-columns:auto 1fr; gap:4px 12px; margin:6px 0 0; }
  .dsh-remedy dt{ font-size:.62rem; font-weight:800; letter-spacing:.07em; text-transform:uppercase;
    color:var(--muted); margin-top:2px; }
  .dsh-remedy dd{ margin:0; font-size:.86rem; color:var(--text); line-height:1.5; }
  .ai-meta-row{ display:contents; }
  .ai-meta-label{ grid-column:1; margin-top:1px; font-size:.68rem; font-weight:700; letter-spacing:.05em;
                  text-transform:uppercase; color:var(--primary); background:var(--card); border:1px solid var(--primary-soft);
                  border-radius:5px; padding:3px 9px; white-space:nowrap; justify-self:start; }
  .ai-meta-value{ grid-column:2; font-size:.88rem; color:var(--text); line-height:1.55; }
  .ai-meta-value em{ color:var(--muted); font-style:normal; }
  .ai-meta-badge{ display:inline-flex; align-items:center; gap:5px; padding:2px 11px; border-radius:20px;
                  font-weight:700; font-size:.85rem; line-height:1.6; }
  .ai-meta-badge.high{ background:rgba(26,156,76,.13); color:var(--green); }
  .ai-meta-badge.low{ background:rgba(224,160,23,.15); color:var(--amber); }
  .ai-meta-badge.neutral{ background:var(--primary-soft); color:var(--primary); }
  /* The model's own prose — branded distinctly from the deterministic
     verdict card above and the sourced evidence below it, so the reading
     order (what was judged -> the AI's reading -> the receipts) is visually
     obvious rather than one undifferentiated wall of text. */
  .ai-answer-badge{ display:inline-flex; align-items:center; gap:6px; font-size:.72rem; font-weight:700;
                     letter-spacing:.05em; text-transform:uppercase; color:var(--accent); }
  /* Badge and Save share one row at the TOP of the reading, so the export is
     reachable without scrolling past four sections and the evidence block. */
  .ai-export-btn{ flex:none; height:28px; padding:0 11px; font-size:.75rem; font-weight:600;
                  border:1px solid var(--line-strong); border-radius:7px;
                  background:var(--card); color:var(--muted); cursor:pointer;
                  transition:background .1s ease,border-color .1s ease,color .1s ease; }
  .ai-export-btn:hover{ background:var(--sunk-soft); border-color:var(--primary); color:var(--primary); }
  /* Question packs shown as comparable tiers, side by side, instead of one
     "Buy questions" button and a single price with nothing to weigh it
     against. auto-fit rather than a fixed 4 columns: the same markup gives
     four across on a desktop, two on a tablet and one on a phone without a
     second layout to keep in step. */
  .tierrow{ display:grid; grid-template-columns:repeat(auto-fit,minmax(132px,1fr));
            gap:8px; margin:14px 0 10px; text-align:left; }
  .tier{ position:relative; display:flex; flex-direction:column; gap:2px;
         padding:13px 12px 12px; border:1px solid var(--line-strong); border-radius:10px;
         background:var(--card); cursor:pointer; font-family:inherit;
         transition:border-color .1s ease, background .1s ease; }
  .tier:hover{ border-color:var(--primary); background:var(--primary-soft); }
  .tier-name{ font-size:.72rem; font-weight:700; letter-spacing:.06em;
              text-transform:uppercase; color:var(--muted); }
  .tier-credits{ font-size:.8rem; color:var(--text); }
  .tier-price{ font-size:1.3rem; font-weight:700; color:var(--heading);
               font-variant-numeric:tabular-nums; margin-top:3px; }
  /* Which AI answers your questions is the whole difference between Premium
     and Advanced — the question COUNT is identical on those two — so it is on
     the card, quietly, rather than left to be discovered after paying. */
  .tier-model{ font-size:.68rem; color:var(--muted); letter-spacing:.02em; margin-top:2px; }
  .tier-note{ font-size:.76rem; margin:8px 0 0; line-height:1.45; }
  /* The best per-question value is marked rather than left to be worked out.
     Accent border only — a filled card would make four of these read as a
     wall, which is what the rest of this pass was undoing. */
  .tier.best{ border-color:var(--accent); }
  .tier-flag{ position:absolute; top:-8px; right:8px; font-size:.6rem; font-weight:700;
              letter-spacing:.05em; text-transform:uppercase; color:var(--on-status);
              background:var(--accent); border-radius:99px; padding:2px 7px; }
  .tier-more{ background:none; border:none; padding:0; cursor:pointer; font-family:inherit;
              font-size:.8rem; color:var(--primary); text-decoration:underline; }
  .aiout .ai-answer{ font-size:1rem; line-height:1.75; }
  .aiout .ai-answer p{ margin:0 0 15px; }
  .aiout .ai-answer p:last-child{ margin-bottom:0; }
  /* The model's own 4 sections (Core Verdict / Nearest Strong Dates /
     Obstructions / Takeaway) were rendered as plain, unstyled <h4>s --
     visually identical weight to the surrounding paragraphs, so the whole
     answer read as one undifferentiated scroll of text. A light, uppercase
     eyebrow label with a thin rule above (not a boxed card -- this is
     meant to stay airy, not add more visual weight) gives each section a
     clear start without competing with the AI Reading title above it. */
  .aiout .ai-answer h4{ margin:22px 0 8px; padding-top:16px; border-top:1px solid var(--line);
    font-size:.74rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
    color:var(--primary); }
  .aiout .ai-answer h4:first-child{ margin-top:0; padding-top:0; border-top:none; }
  /* The reading is now four named sections (Core Verdict / Nearest Strong
     Dates / Obstructions / Takeaway) rather than a wall of paragraphs. Each
     gets a light card so a reader can jump straight to the part they came
     for -- most people want the verdict and the dates and nothing else.
     Deliberately quiet: hairline border, no fill, colour only on the small
     numeral, so four of them stacked do not read as four heavy blocks. */
  .ai-sec{ border:1px solid var(--line); border-radius:10px; padding:14px 16px;
           margin:0 0 10px; background:var(--card); }
  .ai-sec:last-child{ margin-bottom:0; }
  .ai-sec-head{ display:flex; align-items:center; gap:9px; margin:0 0 8px; }
  .ai-sec-n{ flex:none; width:22px; height:22px; border-radius:50%;
             background:var(--primary-soft); color:var(--primary);
             font-size:.72rem; font-weight:700; display:flex;
             align-items:center; justify-content:center; }
  .ai-sec-t{ font-size:.82rem; font-weight:700; letter-spacing:.04em;
             text-transform:uppercase; color:var(--heading); }
  .ai-sec-body p{ margin:0 0 10px; }
  .ai-sec-body p:last-child{ margin-bottom:0; }
  /* The verdict is the one thing everyone reads, so it gets the accent rail
     and slightly larger type. The other three stay uniform. */
  .ai-sec.verdict{ border-left:3px solid var(--accent); }
  /* Traditional indications, not the computed verdict. Dashed where the others
     are solid, and muted: the border is the fastest way to tell a reader that
     what is inside is held to a different standard than what is above it. */
  .ai-sec.person{ border-left:3px dashed var(--muted, #8a90a6); opacity:.94; }
  .ai-sec.verdict .ai-sec-body{ font-size:1.04rem; }
  /* Dates are scanned, not read — tabular figures stop them jittering. */
  .ai-sec.dates .ai-sec-body{ font-variant-numeric:tabular-nums; }
  /* The judgment layout (app.js _renderJudgment): the verdict, the chain that
     proves it, then when it acts. Serif for the two sentences a reader
     remembers (the verdict and "Hence"), monospace for KP notation, so the
     statement and its plain meaning read as two different kinds of line. */
  .jc{ --jc-serif:"Iowan Old Style","Sitka Display",Georgia,ui-serif,"Times New Roman",serif;
       --jc-mono:ui-monospace,"Cascadia Mono","Segoe UI Mono",Consolas,monospace;
       --jc-gold-ink:#8a6400; --jc-sheet:color-mix(in srgb, var(--gold) 6%, var(--card)); }
  @media (prefers-color-scheme:dark){
    :root:not([data-theme="light"]) .jc{ --jc-gold-ink:#f2c65a; --jc-sheet:color-mix(in srgb, var(--gold) 5%, var(--card)); }
  }
  :root[data-theme="dark"] .jc{ --jc-gold-ink:#f2c65a; --jc-sheet:color-mix(in srgb, var(--gold) 5%, var(--card)); }
  .jc-attrib{ margin:0 0 16px; padding:0 0 12px; border-bottom:1px solid var(--line); font-size:.8rem; color:var(--muted); }
  .jc-attrib i{ font-style:normal; font-family:var(--jc-serif); font-size:.95rem; color:var(--text); }
  .jc-verdict{ position:relative; padding:2px 0 2px 16px; margin:0 0 22px; }
  .jc-verdict::before{ content:""; position:absolute; left:0; top:4px; bottom:4px; width:3px; border-radius:3px; background:var(--accent); }
  .ai-answer .jc-verdict-line p{ font-family:var(--jc-serif); font-size:1.4rem; line-height:1.32; margin:0 0 10px; color:var(--text); text-wrap:balance; }
  .jc-tags{ display:flex; flex-wrap:wrap; gap:8px; }
  .jc-pill{ display:inline-flex; align-items:center; gap:5px; font-size:.76rem; font-weight:700; padding:3px 10px;
            border-radius:99px; border:1px solid var(--line-strong); color:var(--muted); background:var(--card); }
  .jc-pill.gold{ border-color:var(--gold); color:var(--jc-gold-ink); background:color-mix(in srgb, var(--gold) 14%, transparent); }
  .jc-stars{ letter-spacing:1px; }
  .jc-sec{ margin:0 0 22px; }
  .jc-sec-h{ display:flex; align-items:baseline; justify-content:space-between; gap:12px; flex-wrap:wrap; margin:0 0 10px; }
  .ai-answer .jc-sec-h h4{ margin:0; padding:0; border:none; font-size:.74rem; font-weight:700; letter-spacing:.07em;
                           text-transform:uppercase; color:var(--heading); }
  .jc-sec-h > span{ font-size:.76rem; color:var(--faint-text); }
  .jc-ladder{ list-style:none; margin:0; padding:0; background:var(--jc-sheet); border:1px solid var(--line); border-radius:12px; overflow:hidden; }
  .jc-step{ display:grid; grid-template-columns:30px minmax(0,1fr) minmax(0,1.05fr); gap:12px; padding:12px 14px;
            border-top:1px solid var(--line); align-items:start; }
  .jc-step:first-child{ border-top:none; }
  .jc-n{ width:24px; height:24px; border-radius:50%; border:1px solid var(--line-strong); background:var(--card); color:var(--muted);
         font-size:.74rem; font-weight:700; display:flex; align-items:center; justify-content:center; font-variant-numeric:tabular-nums; }
  .jc-kp{ font-family:var(--jc-mono); font-size:.8rem; line-height:1.55; color:var(--text); overflow-wrap:anywhere; }
  .jc-kp em{ font-style:normal; font-weight:600; color:var(--primary); }
  .jc-plain{ font-size:.86rem; line-height:1.55; color:var(--muted); }
  .jc-plain b{ color:var(--text); font-weight:600; }
  .jc-step.jc-decides{ background:color-mix(in srgb, var(--accent) 7%, transparent); }
  .jc-step.jc-decides .jc-n{ border-color:var(--accent); background:var(--accent); color:var(--on-status); }
  .jc-tag{ margin-left:6px; font-family:ui-sans-serif,system-ui,sans-serif; font-size:.64rem; font-weight:700;
           letter-spacing:.06em; text-transform:uppercase; color:var(--accent); }
  .jc-retro{ font-family:ui-sans-serif,system-ui,sans-serif; font-size:.7rem; font-weight:700; color:var(--jc-gold-ink); }
  .jc-houses{ display:flex; flex-wrap:wrap; gap:5px; margin-top:5px; }
  .jc-h{ font-size:.72rem; padding:1px 7px; border-radius:6px; background:var(--sunk-soft); border:1px solid var(--line); color:var(--muted); }
  .jc-h b{ color:var(--text); }
  .jc-hence{ padding:13px 14px 14px 56px; border-top:1px solid var(--line-strong); background:var(--card); }
  .ai-answer .jc-hence p{ margin:0; font-family:var(--jc-serif); font-size:1.05rem; line-height:1.5; color:var(--text); }
  .ai-answer .jc-hence p + p{ margin-top:6px; }
  .ai-answer .jc-prose p{ margin:0 0 8px; }
  .ai-answer .jc-counsel p{ font-family:var(--jc-serif); font-size:1.08rem; line-height:1.55; margin:0 0 6px; }
  .jc-strip{ border:1px solid var(--line); border-radius:14px; padding:16px 16px 10px; background:var(--card); margin-top:8px; }
  .jc-track{ position:relative; height:46px; margin:6px 4px 4px; }
  .jc-rail{ position:absolute; left:0; right:0; top:20px; height:8px; border-radius:4px; background:var(--sunk-soft); border:1px solid var(--line); }
  .jc-win{ position:absolute; top:14px; height:20px; border-radius:6px; }
  .jc-win.now{ background:var(--accent); }
  .jc-win.next{ background:repeating-linear-gradient(135deg, color-mix(in srgb, var(--gold) 60%, transparent) 0 5px,
                color-mix(in srgb, var(--gold) 22%, transparent) 5px 10px); border:1px solid var(--gold); }
  .jc-win.past{ background:color-mix(in srgb, var(--primary) 32%, transparent); border:1px solid var(--primary); }
  .jc-win.confirmed{ background:transparent; border:2px solid var(--heading); }
  .jc-today{ position:absolute; top:4px; bottom:0; width:2px; background:var(--accent); border-radius:2px; }
  .jc-today span{ position:absolute; top:-16px; left:50%; transform:translateX(-50%); font-size:.62rem; font-weight:700; letter-spacing:.04em;
                  text-transform:uppercase; white-space:nowrap; color:var(--accent); }
  .jc-axis{ position:relative; height:18px; margin:2px 4px 0; }
  .jc-tick{ position:absolute; top:3px; transform:translateX(-50%); font-size:.7rem; color:var(--faint-text); font-variant-numeric:tabular-nums; }
  /* Legend: only the kinds actually present in this reading (built in JS). */
  .jc-legend{ display:flex; gap:14px; flex-wrap:wrap; margin:10px 2px 0; padding-top:10px; border-top:1px dashed var(--line); }
  .jc-legend-i{ display:inline-flex; align-items:center; gap:6px; font-size:.72rem; color:var(--muted); }
  .jc-legend-dot{ width:11px; height:11px; border-radius:3px; display:inline-block; }
  .jc-legend-dot.now{ background:var(--accent); }
  .jc-legend-dot.next{ background:var(--gold); }
  .jc-legend-dot.past{ background:color-mix(in srgb, var(--primary) 45%, transparent); border:1px solid var(--primary); }
  .jc-legend-dot.confirmed{ background:transparent; border:2px solid var(--heading); }
  /* Period cards: a coloured left spine + badge, dates as the hero, a quiet
     duration/meaning/lords stack underneath. The "now" card gets a tint so
     "where am I today" is immediate, not something you have to read for. */
  .jc-periods{ list-style:none; margin:16px 0 8px; padding:0; display:flex; flex-direction:column; gap:10px; }
  .jc-period{ display:grid; grid-template-columns:auto 1fr; gap:2px 14px; align-items:start;
              padding:13px 14px; border-radius:10px; border:1px solid var(--line); background:var(--card);
              position:relative; overflow:hidden; }
  .jc-period::before{ content:''; position:absolute; left:0; top:0; bottom:0; width:4px; }
  .jc-period.now::before{ background:var(--accent); }
  .jc-period.next::before{ background:var(--gold); }
  .jc-period.past::before{ background:var(--primary); }
  .jc-period.confirmed::before{ background:var(--heading); }
  .jc-period.now{ background:color-mix(in srgb, var(--accent) 6%, var(--card)); }
  .jc-k{ grid-row:1; align-self:center; font-size:.64rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
         padding:4px 9px; border-radius:999px; white-space:nowrap; background:var(--sunk-soft); color:var(--muted); border:1px solid var(--line); }
  .jc-period.now .jc-k{ background:var(--accent); color:var(--on-status); border-color:var(--accent); }
  .jc-period.next .jc-k{ background:color-mix(in srgb, var(--gold) 18%, var(--card)); color:var(--jc-gold-ink); border-color:var(--gold); }
  .jc-period.past .jc-k{ color:var(--primary); border-color:var(--line-strong); }
  .jc-period.confirmed .jc-k{ background:color-mix(in srgb, var(--heading) 12%, var(--card)); color:var(--heading); border-color:var(--heading); }
  .jc-main{ grid-column:2; grid-row:1; display:flex; align-items:baseline; gap:10px; flex-wrap:wrap; }
  .jc-d{ font-variant-numeric:tabular-nums; font-weight:700; font-size:1rem; color:var(--text); letter-spacing:-.01em; }
  .jc-dur{ font-size:.74rem; color:var(--faint-text); }
  /* .jc-m/.jc-lords/.jc-days (a row's meaning, confirmation/best-months
     description, and days-to-watch line) no longer pin a fixed grid-row:
     a row can now show more than one of these at once (F2), so they
     auto-flow down column 2 in DOM order instead of overlapping at a
     shared row number. */
  .jc-m{ grid-column:2; font-size:.85rem; color:var(--muted); line-height:1.4; margin-top:3px; }
  .jc-days{ grid-column:2; font-size:.78rem; color:var(--muted); margin-top:3px; }
  .jc-lords{ grid-column:2; font-size:.7rem; color:var(--faint-text); margin-top:5px; letter-spacing:.02em; }
  .jc-empty{ margin:8px 0 0; font-size:.86rem; color:var(--muted); }
  /* U2B F2 parity spec section 2.2 (kp_u2b_f2_parity_spec.md): the answer
     card [A], Why panel [W] and timeline/steps card [T], built to match
     the approved mockups literally -- exact px/rem values, tw-* tokens
     (2.1), no inline hex. Replaces the F2-round-1/2/3 approximation this
     block used to hold. .jc-strip (legacy _jcStrip widget, above) is
     UNCHANGED -- still used by Cusp Judgment 1-12 and a pre-F2-shaped
     saved reading's fallback. */
  .jc-when-card, .jc-tl-card, .jc-why{ background:var(--card); border:1px solid var(--line);
    border-radius:18px; padding:16px; margin:0 0 12px; box-shadow:var(--shadow); }
  .jc-why.hidden{ display:none; }
  .jc-when-eyebrow{ margin:0; font-size:.74rem; font-weight:700; letter-spacing:.05em;
    text-transform:uppercase; color:var(--muted); }
  .jc-when-meta{ margin:2px 0 0; font-size:.78rem; color:var(--muted); }
  .jc-when{ margin:14px 0 6px; }
  .jc-when-card > .jc-when:first-child{ margin-top:0; }
  .jc-when-lead{ margin:0; font-size:.82rem; font-weight:400; color:var(--muted); }
  .jc-when-range{ margin:2px 0 0; font-family:var(--jc-serif); font-size:1.7rem; font-weight:400;
    line-height:1.15; color:var(--heading); }
  .jc-when-range.is-sentence{ font-size:1.35rem; }
  .jc-when-range small{ display:block; margin-top:4px; font-family:var(--sans);
    font-size:.85rem; color:var(--muted); }
  .jc-chips{ display:flex; flex-wrap:wrap; gap:6px; margin:10px 0 2px; }
  .jc-chips .chip{ margin:0; }
  .chip.tw-strong, .chip.tw-likely, .chip.tw-possible, .chip.tw-none, .chip.tw-count{
    display:inline-flex; align-items:center; gap:6px; border-radius:999px; padding:4px 10px;
    font-size:.78rem; font-weight:700; border:1px solid transparent; }
  .chip.tw-strong{ background:var(--tw-strong-soft); color:var(--tw-strong); }
  .chip.tw-likely{ background:var(--tw-likely-soft); color:var(--tw-likely); }
  .chip.tw-possible{ background:var(--tw-possible-soft); color:var(--tw-possible); }
  .chip.tw-none{ background:var(--tw-none-soft); color:var(--tw-none); }
  .chip.tw-count{ background:var(--primary-soft); color:var(--heading); }
  .tw-dot{ width:8px; height:8px; border-radius:50%; background:currentColor; flex:none; }
  .jc-when-plain{ margin:8px 0 0; font-size:.92rem; color:var(--text); }
  .jc-honest{ margin:12px 0 0; font-size:.8rem; color:var(--muted);
    border-left:3px solid var(--line-strong); padding-left:10px; }
  .jc-actions{ display:flex; flex-wrap:wrap; gap:8px; margin-top:14px; }
  .jc-btn{ display:inline-flex; align-items:center; justify-content:center; gap:6px; min-height:40px;
    padding:0 16px; border-radius:999px; font-weight:700; font-size:.86rem; cursor:pointer;
    border:1px solid var(--line-strong); background:var(--card); color:var(--heading); margin:0;
    width:auto; min-width:0; }
  .jc-btn.solid{ background:var(--jc-btn-solid-bg); border-color:var(--jc-btn-solid-bg); color:var(--on-primary); }
  .jc-sec-title{ margin:4px 0 10px; font-size:.95rem; font-weight:700; color:var(--heading); }
  /* Mockup 01/02 A's own simple percentage-bar "Your timeline"/"Next 4
     months" widget -- a SEPARATE widget from .jc-strip above, app tokens
     in place of the mockup's own --strong/--likely/--possible/--gold/--now. */
  /* R2 ruling (architect, F2-U review): the "Today" label was clipped,
     top (the scroll box's overflow-x:auto computes overflow-y to auto,
     and the span's top sits outside the padding edge) and left (left:
     -16px sits past the left padding edge, unreachable by scrolling,
     whenever today is near lo -- always on horary, 0%). Fixed by a top
     padding on the scroll box and an edge-aware translateX keyed off
     the renderer's own `--p` custom property (written alongside
     `left:{x}%` on every .tl-now, natal, horary and the empty natal
     track -- see jwTimelineWidget/jwNatalEmptyTrack). Drawn layout is
     otherwise unchanged (bars/marks/ticks keep their exact horizontal
     positions); the track only sits a little lower. */
  .tl{ margin:0 0 4px; }
  .tl-scroll{ overflow-x:auto; overflow-y:hidden; margin-top:-6px; padding:22px 0 4px; }
  .tl-inner{ position:relative; min-width:320px; }
  .tl-track{ position:relative; height:46px; background:var(--sunk-soft); border-radius:8px; border:1px solid var(--line); }
  .tl-win{ position:absolute; top:8px; height:28px; border-radius:7px; display:flex; align-items:center; justify-content:center;
           font-size:.72rem; font-weight:800; color:var(--tw-on-bar); min-width:14px; }
  .tl-win.strong{ background:var(--tw-strong); }
  .tl-win.likely{ background:var(--tw-likely); }
  .tl-win.possible{ background:transparent; border:2px dashed var(--tw-possible); color:var(--tw-possible); }
  .tl-now{ position:absolute; top:-6px; bottom:-6px; width:2px; background:var(--accent); }
  /* Addendum (F3b review follow-up, 2026-10-01): --accent-ink, not --accent,
     for this small accent-coloured text label (C19 "hue kept, ink
     darkened" -- the bar ITSELF needs no text-contrast check, only the
     label text does). */
  .tl-now span{ position:absolute; top:-16px; left:0; transform:translateX(calc(var(--p, 50) * -1%));
    font-size:.66rem; font-weight:800; color:var(--accent-ink); white-space:nowrap; }
  .tl-day{ position:absolute; bottom:-9px; width:8px; height:8px; margin-left:-4px; border-radius:50%; background:var(--gold); border:2px solid var(--card); }
  .tl-month{ position:absolute; top:3px; height:4px; border-radius:2px; background:var(--gold); }
  .tl-axis{ position:relative; height:20px; margin-top:8px; font-size:.68rem; color:var(--muted); }
  .tl-axis span{ position:absolute; transform:translateX(-50%); white-space:nowrap; }
  .tl-legend{ display:flex; flex-wrap:wrap; gap:6px 14px; font-size:.72rem; color:var(--muted); margin-top:6px; }
  .tl-legend i{ display:inline-block; width:12px; height:8px; border-radius:2px; margin-right:5px; vertical-align:middle; }
  .tl-lg-strong{ background:var(--tw-strong); }
  .tl-lg-likely{ background:var(--tw-likely); }
  .tl-lg-possible{ border:2px dashed var(--tw-possible); }
  .tl-lg-month{ background:var(--gold); }
  .tl-lg-day{ background:var(--gold); border-radius:50%; width:8px !important; }
  .tl-lg-period{ background:var(--tw-strong); }
  .tl-empty{ position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
    font-size:.78rem; color:var(--jc-muted-sunk); }
  /* The row list beneath the bars (mockup's own "<ul class=rows>"). */
  .jc-tl-rows{ list-style:none; margin:8px 0 0; padding:0; }
  .jc-tl-row{ display:flex; align-items:center; gap:10px; padding:10px 0; border-top:1px solid var(--line); }
  .jc-tl-row:first-child{ border-top:0; }
  .jc-tl-n{ width:22px; height:22px; flex:none; border-radius:50%; background:var(--primary-soft);
    color:var(--heading); font-size:.72rem; font-weight:800; display:flex; align-items:center;
    justify-content:center; }
  .jc-tl-main{ flex:1; min-width:0; }
  .jc-tl-main b{ display:block; font-size:.9rem; }
  .jc-tl-main span{ font-size:.78rem; color:var(--muted); }
  /* "Why this answer?" steps (mockup's own .steps/.step/.ico/.sub). */
  .jc-why-steps{ list-style:none; margin:0; padding:0; }
  .jc-tl-card .tl + .jc-why-steps{ margin-top:6px; }
  .jc-why-step{ display:grid; grid-template-columns:34px 1fr; gap:10px; padding:10px 0;
    border-top:1px solid var(--line); }
  .jc-why-step:first-child{ border-top:0; }
  .jc-why-n{ width:30px; height:30px; border-radius:50%; display:flex; align-items:center;
    justify-content:center; font-weight:800; font-size:.85rem; border:0; }
  .jc-why-n.ok{ background:var(--tw-strong-soft); color:var(--tw-strong); }
  .jc-why-n.mid{ background:var(--tw-possible-soft); color:var(--tw-possible); }
  .jc-why-n.no{ background:var(--tw-none-soft); color:var(--tw-none); }
  .jc-why-n.blue{ background:var(--tw-likely-soft); color:var(--tw-likely); }
  .jc-why-h{ margin:0; font-size:.9rem; font-weight:700; color:var(--text); }
  .jc-why-b{ margin:2px 0 0; font-size:.86rem; color:var(--muted); }
  .jc-why-b b{ color:var(--text); }
  .jc-why-sub{ margin-top:6px; background:var(--sunk-soft); border-radius:8px; padding:8px 10px;
    font-size:.8rem; color:var(--jc-muted-sunk); }
  .jc-why-sub b{ color:var(--text); }
  /* The KP working fold (mockup details.more / .kv). */
  .jc-working{ border-top:1px solid var(--line); margin-top:12px; padding-top:8px; }
  .jc-working > summary{ cursor:pointer; font-weight:700; font-size:.85rem; color:var(--heading);
    list-style:none; min-height:32px; display:flex; align-items:center; }
  .jc-working > summary::-webkit-details-marker{ display:none; }
  .jc-working > summary::before{ content:none; }
  .jc-working > summary::after{ content:"+"; margin-left:auto; font-size:1.1rem; }
  .jc-working[open] > summary::after{ content:"\2013"; }
  .jc-kv{ display:grid; grid-template-columns:minmax(96px,34%) 1fr; gap:4px 10px; font-size:.8rem; margin:6px 0; }
  .jc-kv dt{ color:var(--muted); font-weight:400; }
  .jc-kv dd{ margin:0; color:var(--text); min-width:0; }
  /* H-A (F2-U review round 2): inside the real AI container
     (#x_ai_out.aiout > .ai-answer) the SHARED rules `.aiout .ai-answer p`
     (margin), `.aiout .ai-answer p:last-child`, `.aiout .ai-answer h4`
     (uppercase .74rem --primary eyebrow, fails AA in dark) and `.card h3`
     out-rank the single-class F2 rules above. The shared rules are NOT
     edited; every colliding F2 rule is restated here with enough
     specificity (.aiout .ai-answer .jc-... el.cls) to win, so the
     section 2.2 values hold in the real container. */
  .aiout .ai-answer .jc-when-card p.jc-when-eyebrow{ margin:0; font-size:.74rem; font-weight:700;
    letter-spacing:.05em; text-transform:uppercase; color:var(--muted); line-height:1.55; }
  .aiout .ai-answer .jc-when-card p.jc-when-meta{ margin:2px 0 0; font-size:.78rem; color:var(--muted); line-height:1.55; }
  .aiout .ai-answer .jc-when-card p.jc-when-lead{ margin:0; font-size:.82rem; font-weight:400; color:var(--muted); line-height:1.55; }
  .aiout .ai-answer .jc-when-card p.jc-when-range{ margin:2px 0 0; font-size:1.7rem; font-weight:400;
    line-height:1.15; color:var(--heading); }
  .aiout .ai-answer .jc-when-card p.jc-when-range.is-sentence{ font-size:1.35rem; }
  .aiout .ai-answer .jc-when-card p.jc-when-plain{ margin:8px 0 0; font-size:.92rem; color:var(--text); line-height:1.55; }
  .aiout .ai-answer .jc-when-card p.jc-honest{ margin:12px 0 0; font-size:.8rem; color:var(--muted); line-height:1.55; }
  .aiout .ai-answer .jc-when-card .jc-when{ margin:14px 0 6px; }
  .aiout .ai-answer .jc-when-card > .jc-when:first-child{ margin-top:0; }
  .aiout .ai-answer .jc-when-card, .aiout .ai-answer .jc-why, .aiout .ai-answer .jc-tl-card{ line-height:1.55; }
  /* Round 3: .aiout ul/ol/li (padding-left 22, margin 0 0 12 / 3px 0) also out-rank the F2 lists. */
  .aiout .ai-answer ol.jc-why-steps, .aiout .ai-answer ul.jc-tl-rows{ padding:0; margin:0; }
  .aiout .ai-answer ul.jc-tl-rows{ margin-top:8px; }
  .aiout .ai-answer .jc-tl-card .tl + ol.jc-why-steps{ margin-top:6px; }
  .aiout .ai-answer li.jc-why-step, .aiout .ai-answer li.jc-tl-row{ margin:0; }
  /* NEW (U5f): horary "Periods ahead" (mockup docs/mockups/kp_u5f_horary_bands). 1. .jc-band: the band tag on a
     period row, same look as Life Events' ".win-ahead-row .win-tag". 2. .jc-tl-row.is-empty: a band with no clear
     period. 3. Inside .aiout the shared "p" margin out-ranks .win-ahead-title / .win-order-note, so both are restated. */
  .jc-tl-main .jc-band, .jc-kv .jc-band{ margin-left:6px; font-size:.7rem; font-weight:800; letter-spacing:.04em;
    text-transform:uppercase; color:var(--accent-ink); vertical-align:1px; white-space:nowrap; }
  .jc-tl-row.is-empty .jc-tl-n{ background:var(--tw-none-soft); color:var(--tw-none); }
  .jc-tl-row.is-empty .jc-tl-main b{ font-weight:600; color:var(--muted); }
  .aiout .ai-answer .jc-when-card p.win-ahead-title{ margin:14px 0 0; line-height:1.4; }
  .aiout .ai-answer .jc-when-card p.win-order-note{ margin:2px 0 0; line-height:1.4; }
  .aiout .ai-answer .jc-when-card .jc-when-range small{ font-size:.85rem; color:var(--muted); }
  .aiout .ai-answer .jc-why p.jc-honest{ margin:12px 0 0; font-size:.8rem; color:var(--muted); line-height:1.55; }
  .aiout .ai-answer .jc-why h3.jc-sec-title,
  .aiout .ai-answer .jc-tl-card h3.jc-sec-title{ margin:4px 0 10px; font-size:.95rem;
    font-weight:700; letter-spacing:0; color:var(--heading); line-height:1.25; font-family:var(--sans); }
  .aiout .ai-answer .jc-why-steps h4.jc-why-h{ margin:0; padding:0; border:0; font-size:.9rem; font-weight:700;
    letter-spacing:0; text-transform:none; color:var(--text); line-height:1.25; }
  .aiout .ai-answer .jc-why-steps p.jc-why-b{ margin:2px 0 0; font-size:.86rem; color:var(--muted); line-height:1.55; }
  /* NEW (U5a): the "Asked: ..." block (mockup docs/mockups/kp_u5a_asked_period). One block, used in two places: under
     the verdict pills of the AI card, and in the horary result page's verdict band. Spans, not <p>, so the shared
     ".aiout .ai-answer p" margin never applies and nothing has to be restated. */
  .jc-asked{ margin:12px 0 0; padding:9px 12px 10px; border:1px solid var(--line); border-radius:10px; background:var(--card); }
  .jc-asked-k{ display:block; font-size:.7rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--muted); }
  .jc-asked-v{ display:block; margin-top:2px; font-size:.9rem; line-height:1.5; color:var(--text); }
  .jc-asked-v b{ color:var(--heading); }
  .jc-asked-v .chip{ margin:0 0 0 6px; vertical-align:1px; }
  .aicard .jc-asked-k{ font-size:var(--ai-sub); }
  .aicard .jc-asked-v{ font-size:var(--ai-body); }
  .tr-verdict .jc-asked{ max-width:80ch; }
  /* "Each part of the matter": the Job/<Family> Outlook findings as rows. */
  .jc-parts{ list-style:none; margin:0; padding:0; border:1px solid var(--line); border-radius:12px; overflow:hidden; background:var(--card); }
  .jc-part{ display:grid; grid-template-columns:minmax(0,170px) minmax(0,1fr); gap:14px; padding:10px 14px;
            border-top:1px solid var(--line); align-items:baseline; }
  .jc-part:first-child{ border-top:none; }
  .jc-part.jc-part-wide{ grid-template-columns:minmax(0,1fr); }
  .jc-part-k{ font-size:.8rem; font-weight:700; color:var(--heading); }
  .jc-part-v{ font-size:.88rem; line-height:1.55; color:var(--text); }
  .ai-answer .jc-part-note{ margin:8px 2px 0; font-size:.8rem; color:var(--muted); }
  /* Cautions: advisory notes, folded under the obstacles they qualify. */
  .jc-cautions{ margin-top:10px; border:1px solid var(--line); border-left:3px solid var(--gold); border-radius:10px; background:var(--card); }
  .jc-cautions > summary{ cursor:pointer; list-style:none; padding:9px 14px; display:flex; gap:8px; align-items:center;
                          font-size:.76rem; font-weight:700; letter-spacing:.05em; text-transform:uppercase; color:var(--jc-gold-ink); }
  .jc-cautions > summary::-webkit-details-marker{ display:none; }
  .jc-cautions > summary::before{ content:'▸'; transition:transform .15s ease; }
  .jc-cautions[open] > summary::before{ transform:rotate(90deg); }
  .jc-cautions > summary:focus-visible{ outline:2px solid var(--primary); outline-offset:2px; border-radius:8px; }
  .jc-cautions-body{ padding:0 14px 12px; font-size:.88rem; }
  /* Cusp Judgment 1-12 (app.js _cjHtml): the twelve cusps as a grid, then the
     chosen one judged in the same ladder the AI Explanation uses. A cusp's
     lean is carried by a word and an underline together, never colour alone. */
  .cj-intro{ margin:0 0 12px; font-size:.86rem; color:var(--muted); max-width:68ch; }
  .cj-grid{ display:grid; grid-template-columns:repeat(6, minmax(0,1fr)); gap:8px; margin:0 0 18px; }
  .cj-tile{ display:flex; flex-direction:column; align-items:flex-start; gap:2px; min-width:0; text-align:left; cursor:pointer;
            padding:9px 10px 8px; border:1px solid var(--line); border-bottom-width:3px; border-radius:10px;
            background:var(--card); color:var(--text); font:inherit; }
  .cj-tile:hover{ border-color:var(--line-strong); }
  .cj-tile:focus-visible{ outline:2px solid var(--primary); outline-offset:2px; }
  .cj-tile.supports{ border-bottom-color:var(--green); }
  .cj-tile.mixed{ border-bottom-color:var(--gold); }
  .cj-tile.obstructs{ border-bottom-color:var(--red); }
  /* No natal matter on this cusp: no lean, so no coloured underline. */
  .cj-tile.none{ border-bottom-width:1px; }
  .cj-nomatters{ margin:0 0 18px; max-width:68ch; }
  .cj-tile.sel{ background:var(--jc-sheet); border-top-color:var(--primary); border-left-color:var(--primary); border-right-color:var(--primary);
                box-shadow:inset 0 1px 0 var(--primary); }
  .cj-tn{ font-size:.68rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--faint-text); font-variant-numeric:tabular-nums; }
  .cj-tt{ font-size:.8rem; font-weight:600; line-height:1.25; overflow-wrap:anywhere; }
  .cj-tp{ font-family:var(--jc-mono); font-size:.76rem; color:var(--primary); }
  .cj-tl{ display:flex; flex-wrap:wrap; gap:4px; align-items:baseline; font-size:.7rem; color:var(--muted); }
  .cj-lock{ font-size:.6rem; font-weight:700; letter-spacing:.04em; text-transform:uppercase; color:var(--jc-gold-ink); }
  .cj-detail{ border-top:1px solid var(--line); padding-top:16px; }
  .cj-head{ display:grid; grid-template-columns:auto minmax(0,1fr) auto; gap:12px; align-items:center; margin:0 0 16px; }
  .cj-hn{ font-family:var(--jc-serif); font-size:1.7rem; line-height:1; color:var(--accent); font-variant-numeric:tabular-nums; }
  .cj-head h3{ margin:0; font-family:var(--jc-serif); font-size:1.2rem; font-weight:600; text-wrap:balance; }
  .cj-head p{ margin:2px 0 0; font-size:.8rem; color:var(--muted); }
  .cj-sec{ margin:0 0 18px; }
  .cj-sec-h{ display:flex; align-items:baseline; justify-content:space-between; gap:12px; flex-wrap:wrap; margin:0 0 8px; }
  .cj-sec-h h4{ margin:0; padding:0; border:none; font-size:.74rem; font-weight:700; letter-spacing:.07em; text-transform:uppercase; color:var(--heading); }
  .cj-sec-h > span{ font-size:.76rem; color:var(--faint-text); }
  .cj-matters{ list-style:none; margin:0; padding:0; border:1px solid var(--line); border-radius:12px; overflow:hidden; background:var(--card); }
  .cj-matter{ display:grid; grid-template-columns:minmax(0,160px) minmax(0,1fr) auto; gap:12px; padding:10px 14px;
              border-top:1px solid var(--line); align-items:center; }
  .cj-matter:first-child{ border-top:none; }
  .cj-mt{ font-size:.86rem; font-weight:600; }
  .cj-mh{ font-family:var(--jc-mono); font-size:.74rem; line-height:1.55; color:var(--muted); overflow-wrap:anywhere; }
  .cj-mh em{ font-style:normal; font-weight:600; color:var(--primary); }
  .cj-matter .jc-pill{ white-space:nowrap; }
  .cj-matter .jc-pill.v-promised{ border-color:var(--green); color:var(--green); }
  .cj-matter .jc-pill.v-weak{ border-color:var(--gold); color:var(--jc-gold-ink); }
  .cj-matter .jc-pill.v-not{ border-color:var(--red); color:var(--red); }
  .cj-delivered{ display:flex; flex-wrap:wrap; gap:6px; }
  .cj-locked{ padding:16px; border:1px dashed var(--line-strong); border-radius:12px; background:var(--jc-sheet); }
  .cj-locked p{ margin:0 0 10px; font-size:.9rem; max-width:62ch; }
  @media (max-width:900px){ .cj-grid{ grid-template-columns:repeat(4, minmax(0,1fr)); } }
  @media (max-width:640px){
    .cj-grid{ grid-template-columns:repeat(3, minmax(0,1fr)); }
    .cj-matter{ grid-template-columns:minmax(0,1fr) auto; }
    .cj-mh{ grid-column:1 / -1; }
  }
  @media (max-width:640px){
    .jc-step{ grid-template-columns:26px minmax(0,1fr); }
    .jc-step .jc-plain{ grid-column:2; }
    .jc-hence{ padding-left:14px; }
    .jc-period{ grid-template-columns:1fr; }
    .jc-k{ grid-column:1; grid-row:1; justify-self:start; margin-bottom:6px; }
    .jc-main, .jc-m, .jc-days, .jc-lords{ grid-column:1; }
    .ai-answer .jc-verdict-line p{ font-size:1.2rem; }
    .jc-part{ grid-template-columns:minmax(0,1fr); gap:2px; }
  }
  @media (max-width:640px){ .ai-sec{ padding:12px 13px; } }
  /* Job Outlook: the deterministic multi-cusp note, rendered OPEN (not a
     foldCard) directly under the main answer -- the whole point is that a
     customer sees the broader reasoning without an extra click. Styled
     distinctly from .ai-sec (gold rail, tinted background) so it reads as
     its OWN card and not a fifth numbered section of the model's four. */
  .ai-job-outlook{ border:1px solid var(--gold); border-left:3px solid var(--gold);
    border-radius:10px; padding:14px 16px; margin:14px 0; background:var(--primary-soft); }
  .ai-job-outlook-head{ font-size:.82rem; font-weight:700; letter-spacing:.02em;
    color:var(--heading); margin:0 0 8px; }
  .ai-job-outlook-body p{ margin:0 0 8px; }
  .ai-job-outlook-body p:last-child{ margin-bottom:0; }
  .ai-job-outlook-body ul{ margin:0 0 8px 20px; padding:0; }
  .ai-job-outlook-body li{ margin:0 0 6px; }
  @media (max-width:640px){ .ai-job-outlook{ padding:12px 13px; } }
  /* A retrograde star lord's own dated denial/delay finding (case_
     explanation._star_lord_retrograde_line / retrograde_gate_line), inline
     within a Detailed Astrological Analysis house block — previously
     indistinguishable from the two plain lines around it. Amber, same
     signal color as .multiq-note's caution styling, but inline (a <span>,
     not a block) since it sits mid-paragraph. */
  .ai-retro-flag{ background:color-mix(in srgb, var(--gold) 20%, transparent);
    border:1px solid var(--gold); border-radius:4px; padding:1px 6px; font-weight:600; }
  /* "<Event> Advisory Notes" (marriage/job/finance/health) — a small,
     always-visible deterministic block inside Detailed Astrological
     Analysis, previously flattened into the same plain paragraph style as
     the surrounding derivation-chain text. --accent rather than --gold's
     caution tone (this is informational color/nuance, not a caution) --
     and NOT --blue, which is only defined for the light theme and would
     go near-invisible against a dark background. */
  .ai-advisory-block{ background:color-mix(in srgb, var(--accent) 10%, transparent);
    border:1px solid var(--accent); border-left:3px solid var(--accent);
    border-radius:8px; padding:10px 14px; margin:10px 0; }
  .ai-advisory-label{ font-size:.72rem; font-weight:700; letter-spacing:.04em;
    text-transform:uppercase; color:var(--accent); margin-bottom:6px; }
  .ai-advisory-block ul{ margin:0; padding-left:18px; }
  .ai-advisory-block li{ margin:0 0 4px; }
  .ai-advisory-block li:last-child{ margin-bottom:0; }
  /* KP Evidence — the sourced, deterministic bullet list appended after the
     model's prose. A native <details> (no JS needed, keyboard/screen-reader
     friendly by default) rather than always-on muted bullets: open by
     default so nothing is hidden, but collapsible once read so a long
     reading doesn't end on a wall of dense receipts. */
  /* "How should I word my question?" — nested inside the help body, so it is
     collapsed by default: it is the longest thing in there and only wanted
     once, whereas the surrounding help is skimmed every visit. Native
     <details> for the same reasons as .ai-evidence below. */
  /* Foldable card (foldCard() in app.js) — Significators and Vimshottari Dasha
     are long reference tables that sat between the reading and the
     predictions, so they ship collapsed. Native <details>, same reasons as
     .ask-guide below. The summary keeps the card's own padding so a folded
     card is the same object as an open one, just shorter. */
  details.cardfold > summary.cardfold-head{
    cursor:pointer; list-style:none; display:flex; align-items:baseline;
    gap:12px; flex-wrap:wrap; }
  details.cardfold > summary.cardfold-head::-webkit-details-marker{ display:none; }
  details.cardfold > summary.cardfold-head::before{
    content:'▸'; color:var(--primary); font-size:.85rem; flex:none;
    transition:transform .15s ease; }
  details.cardfold[open] > summary.cardfold-head::before{ transform:rotate(90deg); }
  details.cardfold > summary.cardfold-head h3{ margin:0; display:inline; }
  .cardfold-hint{ font-size:.82rem; color:var(--muted); display:flex;
    align-items:center; gap:6px; flex-wrap:wrap; }
  /* The predictions fold puts verdict counts in the summary, where they are
     small bold text on --card. Measured there, the shared --green/--red give
     3.33/2.70 in dark and 3.56/4.38 in light — all under the 4.5:1 AA floor.
     The AA-checked pair measures 6.81/5.20 dark and 5.42/5.44 light. Scoped to
     the hint on purpose: --green/--red are used elsewhere as solid fills
     carrying white text, so they cannot simply be lightened globally. */
  .cardfold-hint .v-promised{ color:var(--dig-good); }
  .cardfold-hint .v-not{ color:var(--dig-bad); }
  .cardfold-body{ margin-top:12px; }
  /* Ashtakavarga loads into its own container after the predictions, so it
     keeps the standalone spacing the old plain card carried inline. */
  details.cardfold.av-fold{ margin-top:16px; }
  /* Sits directly after the .ai-sec cards (whose last child has its own
     margin-bottom:0) inside the same .ai-answer -- needs its own top gap
     so the fold reads as a separate unit, not glued to the Takeaway card. */
  details.cardfold.ai-detailed-fold{ margin-top:16px; margin-bottom:0; }
  details.cardfold.ai-detailed-fold .ai-detailed-body p{ font-size:.92rem; }
  /* The focus ring belongs on the whole clickable row, not just the marker. */
  details.cardfold > summary.cardfold-head:focus-visible{
    outline:2px solid var(--primary); outline-offset:3px; border-radius:6px; }
  .ask-guide{ border:1px solid var(--line); border-radius:12px; padding:10px 14px; margin:10px 0 4px;
              background:var(--card); }
  .ask-guide > summary{ cursor:pointer; list-style:none; font-weight:700; font-size:.9rem;
                        display:flex; align-items:center; gap:8px; }
  .ask-guide > summary::-webkit-details-marker{ display:none; }
  .ask-guide > summary::before{ content:'▸'; color:var(--primary); transition:transform .15s ease; }
  .ask-guide[open] > summary::before{ transform:rotate(90deg); }
  .ask-guide[open] > summary{ margin-bottom:10px; }
  .ask-guide h5{ margin:14px 0 4px; font-size:.85rem; }
  .ask-guide h5:first-of-type{ margin-top:4px; }
  .ask-guide p, .ask-guide li{ font-size:.86rem; line-height:1.6; }
  .ask-guide table{ width:100%; border-collapse:collapse; margin:6px 0 2px; font-size:.84rem; }
  .ask-guide td{ padding:5px 8px; vertical-align:top; border-bottom:1px solid var(--line); }
  .ask-guide td:first-child{ white-space:nowrap; width:1%; font-weight:700; }
  .ask-no{ color:var(--red,#e0392b); }
  .ask-yes{ color:var(--green,#1a8a4a); }
  .ai-evidence{ margin-top:18px; background:var(--card); border:1px solid var(--line); border-radius:12px;
                padding:14px 16px; }
  .ai-evidence-title{ font-size:.72rem; font-weight:700; letter-spacing:.05em; text-transform:uppercase;
                       color:var(--muted); cursor:pointer; list-style:none; display:flex; align-items:center; gap:6px; }
  .ai-evidence-title::-webkit-details-marker{ display:none; }
  .ai-evidence-title::before{ content:'▸'; font-size:.7rem; color:var(--primary); transition:transform .15s ease; }
  .ai-evidence[open] .ai-evidence-title::before{ transform:rotate(90deg); }
  /* .ai-meta-row is display:contents (set above), so this grid is what
     actually aligns the label/value pairs — same mechanism as .ai-meta. */
  .ai-evidence-body{ margin-top:12px; display:grid; grid-template-columns:auto 1fr; gap:9px 14px; align-items:start; }
  .ai-evidence ul{ margin:0; padding-left:20px; }
  /* Same label-above-value stack as .ai-meta at the same breakpoint --
     .ai-evidence-body's rows already share the .ai-meta-row class this
     reuses (see comment above), so only the grid column itself needs
     restating here. */
  @media (max-width:420px){ .ai-evidence-body{ grid-template-columns:1fr; } }
  .ai-evidence li{ font-size:.84rem; color:var(--muted); margin:4px 0; }
  /* ===== A1: AI Explanation card, the owner-approved kp_tier_benefit look
     (docs/plans/kp_ui_mockup_parity_plan.md section 4.1). One thin solid card,
     white header band, flat sections, foot line. Placed after every earlier AI
     rule on purpose so it wins on source order; .card.aicard out-ranks the
     later generic `.card` shadow reset. ===== */
  #b_ai_card, #h_ai_card, .card.aicard{ background:var(--card); border:1px solid var(--line); border-radius:18px;
    box-shadow:var(--shadow); overflow:hidden; padding:0 0 14px; }
  /* The card has no padding of its own (the band and answers run edge to edge),
     so everything that is not a band/answer gets the mock's 14px gutter. */
  .aicard > :not(.ai-card-band):not(.ai2-tabs):not(.aiout):not([role="tabpanel"]){ margin-left:14px !important; margin-right:14px !important; }
  .aicard > [role="tabpanel"] > :not(.aiout){ margin-left:14px !important; margin-right:14px !important; }
  .aicard .ai-lede{ margin-top:10px; margin-bottom:0; font-size:.78rem; }
  .aicard > [role="tabpanel"] > .ai2-composer, .aicard > [role="tabpanel"] > .ai2-starters{ width:auto; }
  .aicard .ai2-tabs{ margin:0 0 12px; padding:0 14px; background:var(--card); }
  .aicard > .aiout, .aicard > [role="tabpanel"] > .aiout{ margin-left:0 !important; margin-right:0 !important; }
  .aiout:not(:has(.ai-answer)):not(:has(.ai-bubble)):not(:empty){ padding:12px 14px; }
  .aiout .ai-bubble.ai:not(:has(.ai-answer)){ padding:12px 14px; }
  .aiout .ai-bubble.ai{ max-width:none; width:100%; background:none; border:0; border-radius:0; padding:0; align-self:stretch; }
  .aiout .ai-bubble.ai ~ .ai-bubble.ai{ border-top:1px solid var(--line); }
  .aiout .ai-bubble.user:has(+ .ai-bubble.ai .qline){ display:none; }
  .aiout .ai-bubble.user{ max-width:none; align-self:stretch; border-radius:0; margin:10px 14px 0; border-radius:12px; }
  .aiout .ai-answer{ padding:14px 14px 10px; font-size:.94rem; line-height:1.55; }
  .aiout .ai-answer > p:first-child{ margin-top:0; }
  .qline{ font-size:.78rem; color:var(--faint); margin:0 0 10px; line-height:1.5; }
  .qline b{ color:var(--text); font-weight:600; }
  /* ---- Birth AI follow-ups (FU-2; mockup docs/mockups/kp_birth_ai_followups) ----
     Follow-up / related answers are slim blocks inside the same AI card;
     "New question" divider, rephrase notice, system notes, history count. */
  .aiout .fu{ padding:0 0 10px; }
  .aiout .fu-ref{ display:flex; flex-wrap:wrap; align-items:center; gap:4px 6px; font-size:.8125rem; color:var(--faint); margin:0 0 8px; }
  .fu-ref .arrow{ color:var(--primary); font-weight:700; }
  .fu-ref a{ color:var(--primary); font-weight:600; text-decoration:none; border-bottom:1px dotted var(--primary); }
  .aiout .fu-body p{ margin:0 0 8px; font-size:1rem; line-height:1.6; }
  .aiout .fu-body p.lead{ font-family:var(--serif); font-size:1.2rem; line-height:1.4; color:var(--text); margin-bottom:10px; }
  .fu.rel .fu-ref .k{ font-weight:700; color:var(--heading); }
  .fu.rel .jc-tags{ display:flex; flex-wrap:wrap; gap:6px; margin:2px 0 10px; }
  .fu.rel details.cardfold{ margin-top:6px; }
  .fu .jc-ladder{ margin:0; }
  .fu .jc-step{ padding:9px 12px; gap:10px; }
  .fu .jc-kp{ font-size:.8125rem; line-height:1.55; }
  .fu .jc-plain{ font-size:.86rem; color:var(--muted); line-height:1.5; }
  .fu details.cardfold{ padding:0; }
  .newq{ display:flex; align-items:center; gap:10px; margin:18px 14px 0; color:var(--faint); font-size:.74rem; font-weight:700; letter-spacing:.07em; text-transform:uppercase; }
  .newq::before, .newq::after{ content:""; flex:1; height:1px; background:var(--line-strong); }
  .aiout .staleline{ font-size:.86rem; color:var(--faint); margin:12px 14px 0; }
  .reph{ margin:10px 14px 4px; border:1px solid var(--line); border-left:3px solid var(--primary); background:var(--sunk); border-radius:12px; padding:10px 12px; font-size:.92rem; color:var(--muted); }
  .reph p{ margin:0 0 8px; }
  .reph .row{ display:flex; flex-wrap:wrap; gap:8px; align-items:center; }
  .reph .fine{ font-size:.78rem; color:var(--faint); }
  .sysmsg{ margin:10px 14px 4px; border:1px solid var(--line); border-left:3px solid var(--accent); background:var(--sunk); border-radius:12px; padding:10px 12px; font-size:.92rem; color:var(--muted); }
  .sysmsg p{ margin:0; }
  .ai-history-item .fus{ display:inline-block; margin-left:6px; font-size:.72rem; font-weight:700; color:var(--primary); background:var(--primary-soft); border-radius:99px; padding:1px 8px; }
  .ai-history-item .hconfirm{ grid-column:1 / -1; font-size:.82rem; color:var(--muted); display:flex; gap:8px; flex-wrap:wrap; align-items:center; padding-top:4px; border-top:1px dashed var(--line); margin-top:4px; cursor:default; }
  .hconfirm button{ appearance:none; background:none; border:0; font:inherit; font-weight:600; color:var(--muted); cursor:pointer; padding:2px 4px; }
  .hconfirm button.del{ font-weight:700; color:var(--accent); }
  .ai-bubble .ai-delbar{ margin:0 14px 4px; }
  /* evidence strength */
  .aiout .ai-evidence-strength{ margin:0 0 14px; }
  .ai-evidence-strength .ai-meta-badge{ display:inline-flex; align-items:center; gap:5px; padding:2px 10px;
    border-radius:20px; font-size:.74rem; font-weight:600; line-height:1.55; }
  .ai-evidence-strength .ai-meta-badge.high{ background:var(--strong-soft); color:var(--strong); }
  .ai-evidence-strength .ai-meta-badge.low{ background:var(--warning-soft); color:var(--warning); }
  .ai-evidence-strength-caption{ font-size:.72rem; color:var(--faint); margin:4px 0 0; }
  /* judgment */
  .jc{ --jc-serif:var(--serif); --jc-mono:var(--mono); --jc-gold-ink:var(--gold-ink); --jc-sheet:var(--sunk); }
  .jc-attrib{ margin:0 0 14px; padding:0 0 10px; font-size:.78rem; }
  .jc-attrib i{ font-size:.92rem; }
  .jc-verdict{ padding:2px 0 2px 14px; margin:0 0 18px; }
  .aiout .ai-answer .jc-verdict-line p{ font-size:1.22rem; line-height:1.32; margin:0 0 8px; text-wrap:initial; }
  .jc-tags{ gap:6px; }
  .jc-pill{ font-size:.72rem; padding:2px 9px; }
  .jc-pill.gold{ border-color:var(--gold); color:var(--gold-ink); background:var(--gold-soft); }
  .jc-sec{ margin:0 0 18px; }
  .jc-sec-h{ gap:8px 10px; margin:0 0 8px; }
  .ai-answer .jc-sec-h h4{ font-size:.72rem; }
  .jc-sec-h > span{ font-size:.74rem; color:var(--faint); }
  .jc-ladder{ background:var(--sunk); border-radius:12px; }
  .jc-step{ padding:9px 12px; gap:10px; font-size:.8rem; }
  .jc-n{ width:22px; height:22px; font-size:.7rem; }
  .jc-kp{ font-size:.76rem; }
  .jc-hence{ display:block; background:var(--card); padding:10px 12px 11px 48px; }
  .ai-answer .jc-hence p{ font-family:var(--serif); font-size:.98rem; }
  .ai-answer .jc-prose p{ margin:0 0 8px; font-size:.92rem; }
  .ai-answer .jc-counsel p{ font-family:var(--serif); font-size:1rem; line-height:1.5; margin:0; }
  /* numbered extra sections */
  .ai-sec{ border:1px solid var(--line); border-radius:12px; padding:10px 12px; margin:0 0 12px; background:var(--card); }
  .ai-sec.person{ border-left:1px solid var(--line); opacity:1; }
  .ai-sec-head{ display:flex; align-items:center; gap:8px; margin-bottom:6px; }
  .ai-sec-n{ width:20px; height:20px; font-size:.68rem; }
  .ai-sec-t{ font-size:.74rem; letter-spacing:.05em; }
  .ai-sec-body p{ margin:0; font-size:.9rem; }
  @media (max-width:640px){ .ai-sec{ padding:10px 12px; } }
  /* folds: Full working + KP Evidence */
  details.ai-detailed-fold, details.cardfold.ai-detailed-fold, details.ai-evidence{ border:1px solid var(--line);
    border-radius:10px; margin:0 0 10px; background:var(--card); padding:0; box-shadow:none; }
  details.ai-detailed-fold > summary, details.ai-evidence > summary{ padding:9px 12px; font-size:.84rem; font-weight:600;
    display:flex; align-items:center; gap:8px; flex-wrap:wrap; list-style:none; text-transform:none; letter-spacing:0;
    color:var(--text); cursor:pointer; }
  details.ai-detailed-fold > summary h3{ margin:0; font-size:.84rem; font-weight:600; color:var(--text); font-family:inherit; }
  details.ai-detailed-fold > summary .cardfold-hint{ font-weight:400; color:var(--faint); font-size:.76rem; }
  details.ai-detailed-fold > summary::-webkit-details-marker, details.ai-evidence > summary::-webkit-details-marker{ display:none; }
  details.ai-detailed-fold > summary::before, details.ai-evidence > summary::before{ content:"+"; width:14px; color:var(--muted);
    font-weight:700; font-size:.84rem; transform:none; }
  details[open].ai-detailed-fold > summary::before, details[open].ai-evidence > summary::before{ content:"-"; transform:none; }
  details.ai-detailed-fold > .cardfold-body, details.ai-evidence > .ai-evidence-body{ margin:0; padding:0 12px 10px; }
  details.ai-evidence > .ai-evidence-body{ gap:9px 14px; }
  .aiout > details.ai-evidence, .aiout > .ai-bubble > details.ai-evidence{ margin:0 14px 10px; }
  .ai-evidence-title{ text-transform:none; letter-spacing:0; font-size:.84rem; color:var(--text); }
  /* foot line (Save / Share on the left, tier line on the right) */
  .ansfoot, .ai2-ansfoot{ display:flex; align-items:center; gap:10px; padding:10px 14px 12px; border-top:1px solid var(--line);
    margin:0; flex-wrap:wrap; }
  .ansfoot .ai-export-btn{ height:auto; padding:2px 9px; font-size:.72rem; font-weight:700; border-radius:99px;
    border:1px solid var(--line-strong); color:var(--muted); background:var(--card); }
  .ai2-tierline{ font-size:.76rem; color:var(--muted); margin-left:auto; text-align:right; line-height:1.45; width:auto; }
  .ai2-tierline::before{ content:""; display:inline-block; width:7px; height:7px; border-radius:50%; background:var(--gold);
    margin-right:6px; vertical-align:middle; }
  .ai2-tierdot{ display:none; }
  @media (max-width:520px){ .ai2-tierline{ margin-left:0; text-align:left; } }
  /* ===== A1b (owner ruling 2026-10-01, "except logo you can change everything as
     mockup"): the ladder as drawn (steps 1, 2 and the last, one line each) and the
     When section's timeline as the mockup's strip + period rows. The U2B answer box
     [A] and Why panel [W] keep their approved values. Tokens only. ===== */
  /* Direct children of .aiout that are not an answer/fold (financial disclaimer, tier upsell) get the card gutter. */
  .aiout > .ai-disclaimer, .aiout > .ai-bubble > .ai-disclaimer{ margin:14px 14px 12px; }
  .aiout > .ai-tier-upsell, .aiout > .ai-bubble > .ai-tier-upsell{ margin-left:14px; margin-right:14px; }
  @media (max-width:640px){ .aicard > .btn{ width:calc(100% - 28px); } }
  .jc-parts .jc-part{ grid-template-columns:minmax(0,120px) minmax(0,1fr); gap:10px; padding:8px 12px; margin:0; font-size:.82rem; }
  .jc-parts .jc-part.jc-part-wide{ grid-template-columns:minmax(0,1fr); }
  .jc-parts .jc-part-k{ font-size:inherit; color:var(--text); }
  .jc-parts .jc-part-v{ font-size:inherit; line-height:1.4; color:var(--muted); }
  /* ===== A1c: larger type in the AI card (owner request 2026-10-01: "the font is very small
     can you increase the font?"). Overrides the tier mockup's sizes, which were drawn in a
     phone frame. Body 16px (17px from 1024px), headings 13/14px, serif statements 1.2/1.35rem,
     mono ladder 13/14px, sub-labels 13px. Local custom properties on .aicard (not :root); body
     size is the theme's --fs-md. ===== */
  .aicard{ --ai-body:var(--fs-md); --ai-head:.8125rem; --ai-serif:1.2rem; --ai-verdict:1.45rem; --ai-hence:1.1rem;
    --ai-mono:.8125rem; --ai-sub:.8125rem; --ai-small:calc(var(--fs-md) - 2px); }
  @media (min-width:1024px){
    .aicard{ --ai-body:1.0625rem; --ai-head:.875rem; --ai-serif:1.35rem; --ai-verdict:1.65rem; --ai-hence:1.2rem;
      --ai-mono:.875rem; --ai-small:calc(1.0625rem - 2px); }
  }
  .aicard .aiout .ai-answer{ font-size:var(--ai-body); line-height:1.6; }
  .aicard .qline{ font-size:var(--ai-body); line-height:1.6; }
  .aicard .aiout .ai-evidence-strength .ai-meta-badge{ font-size:var(--ai-sub); }
  .aicard .aiout .ai-evidence-strength > div > span.muted{ font-size:var(--ai-sub) !important; }
  .aicard .ai-evidence-strength-caption{ font-size:var(--ai-sub); }
  .aicard .jc-attrib{ font-size:var(--ai-sub); }
  .aicard .jc-attrib i{ font-size:calc(var(--ai-body) + 1px); }
  .aicard .aiout .ai-answer .jc-verdict-line p{ font-size:var(--ai-verdict); line-height:1.3; }
  .aicard .jc-pill{ font-size:var(--ai-sub); }
  .aicard .ai-answer .jc-sec-h h4{ font-size:var(--ai-head); }
  .aicard .jc-sec-h > span{ font-size:var(--ai-sub); }
  .aicard .jc-kp{ font-size:var(--ai-mono); }
  .aicard .jc-plain{ font-size:var(--ai-body); line-height:1.6; }
  .aicard .ai-answer .jc-hence p{ font-size:var(--ai-hence); }
  .aicard .ai-answer .jc-prose p{ font-size:var(--ai-body); line-height:1.6; }
  .aicard .ai-answer .jc-counsel p{ font-size:var(--ai-serif); line-height:1.5; }
  .aicard .jc-parts .jc-part{ grid-template-columns:minmax(0,150px) minmax(0,1fr); font-size:var(--ai-body); line-height:1.5; }
  .aicard .jc-parts .jc-part.jc-part-wide{ grid-template-columns:minmax(0,1fr); }
  .aicard .ai-answer .jc-part-note{ font-size:var(--ai-small); }
  .aicard .ai-sec-t{ font-size:var(--ai-head); }
  .aicard .ai-sec-n{ width:22px; height:22px; font-size:var(--ai-sub); }
  .aicard .ai-sec-body p{ font-size:var(--ai-body); line-height:1.6; }
  .aicard .ai-sec .fine, .aicard .ai-sec-body .muted{ font-size:var(--ai-sub); }
  .aicard details.ai-detailed-fold > summary, .aicard details.ai-evidence > summary,
  .aicard details.ai-detailed-fold > summary h3{ font-size:calc(var(--ai-body) - 1px); }
  .aicard details.ai-detailed-fold > summary .cardfold-hint{ font-size:var(--ai-sub); }
  .aicard .jc-cautions > summary{ font-size:var(--ai-sub); }
  .aicard .ansfoot .ai-export-btn{ font-size:var(--ai-sub); }
  .aicard .ai2-tierline{ font-size:var(--ai-sub); }
  /* When: the U2 answer box, Why panel and timeline keep their proportions on the new scale. */
  .aicard .aiout .ai-answer .jc-when-card p.jc-when-eyebrow{ font-size:var(--ai-head); }
  .aicard .aiout .ai-answer .jc-when-card p.jc-when-meta,
  .aicard .aiout .ai-answer .jc-when-card p.jc-honest,
  .aicard .aiout .ai-answer .jc-why p.jc-honest{ font-size:var(--ai-small); }
  .aicard .aiout .ai-answer .jc-when-card p.jc-when-lead{ font-size:var(--ai-small); }
  .aicard .aiout .ai-answer .jc-when-card p.jc-when-plain{ font-size:var(--ai-body); line-height:1.6; }
  .aicard .aiout .ai-answer .jc-when-card p.jc-when-range{ font-size:calc(var(--ai-verdict) + .25rem); }
  .aicard .aiout .ai-answer .jc-when-card p.jc-when-range.is-sentence{ font-size:var(--ai-serif); }
  .aicard .aiout .ai-answer .jc-when-card .jc-when-range small{ font-size:var(--ai-small); }
  .aicard .jc-btn{ font-size:var(--ai-small); }
  .aicard .chip.tw-strong, .aicard .chip.tw-likely, .aicard .chip.tw-possible, .aicard .chip.tw-none, .aicard .chip.tw-count{ font-size:var(--ai-sub); }
  .aicard .aiout .ai-answer .jc-why h3.jc-sec-title, .aicard .aiout .ai-answer .jc-tl-card h3.jc-sec-title{ font-size:var(--ai-head); }
  .aicard .aiout .ai-answer .jc-why-steps h4.jc-why-h{ font-size:var(--ai-body); }
  .aicard .aiout .ai-answer .jc-why-steps p.jc-why-b{ font-size:calc(var(--ai-body) - 1px); }
  .aicard .jc-sec .jc-tl-main b{ font-size:var(--ai-body); }
  .aicard .jc-sec .jc-tl-main span{ font-size:var(--ai-small); }
  .aicard .jc-sec .jc-tl-row .chip{ font-size:var(--ai-sub); }
  .aicard .tl-axis{ font-size:var(--ai-sub); }
  .aicard .tl-legend{ font-size:var(--ai-sub); }
  .jc-ladder-card .jc-step{ grid-template-columns:26px minmax(0,1fr); }
  .jc-ladder-card .jc-step.jc-decides{ background:none; }
  .jc-ladder-card .jc-step.jc-decides .jc-n{ border-color:var(--line-strong); background:var(--card); color:var(--muted); }
  .jc-ladder-card .jc-tag{ margin-left:.3em; font-family:inherit; font-size:inherit; font-weight:400; letter-spacing:0;
    text-transform:none; color:inherit; }
  .jc-ladder-card .jc-hence{ border-top:1px solid var(--line); }
  .jc-sec > .jc-prose + .jc-when-card, .jc-sec > .jc-prose + .jc-tl-card{ margin-top:10px; }
  .jc-sec .jc-tl-card{ border-radius:14px; padding:12px 12px 8px; box-shadow:none; margin:6px 0 10px; }
  .jc-sec .jc-tl-card .jc-sec-title{ margin:0 0 6px; font-size:.72rem; letter-spacing:.07em; text-transform:uppercase; }
  .jc-sec ul.jc-tl-rows{ display:flex; flex-direction:column; gap:8px; margin:12px 0 4px; }
  .jc-sec .jc-tl-row{ position:relative; border:1px solid var(--line); border-radius:10px; background:var(--card);
    padding:10px 12px 10px 16px; overflow:hidden; }
  .jc-sec .jc-tl-row:first-child{ border-top:1px solid var(--line); }
  .jc-sec .jc-tl-row::before{ content:""; position:absolute; left:0; top:0; bottom:0; width:4px; background:var(--gold); }
  .jc-sec .jc-tl-main b{ font-size:.94rem; font-variant-numeric:tabular-nums; }
  .jc-sec .jc-tl-main span{ font-size:.8rem; }
  /* location autocomplete dropdown */
  .acwrap{ position:relative; }
  .ac{ position:absolute; left:0; right:0; top:100%; margin-top:2px; z-index:60; background:var(--card);
       border:1px solid var(--line-strong); border-radius:9px; box-shadow:var(--shadow); max-height:260px; overflow-y:auto; }
  .ac-item{ padding:9px 12px; font-size:.85rem; cursor:pointer; border-bottom:1px solid var(--line); }
  .ac-item:last-child{ border-bottom:none; }
  .ac-item:hover, .ac-item.hi{ background:var(--primary-soft); }
  .ac-note{ padding:9px 12px; font-size:.82rem; color:var(--muted); }
  /* advanced settings disclosure */
  .adv{ margin-top:14px; }
  .adv-toggle{ cursor:pointer; user-select:none; font-weight:600; color:var(--heading); font-size:.85rem; padding:4px 0; }
  .adv-toggle:hover{ color:var(--primary); }
  .adv-body{ margin-top:8px; padding:12px; background:var(--sunk-soft); border:1px solid var(--line); border-radius:10px; }
  .help-toggle{ cursor:pointer; user-select:none; display:inline-flex; align-items:center; gap:6px;
    font-weight:600; color:var(--primary); font-size:.85rem; padding:6px 0 2px; }
  .help-toggle:hover{ text-decoration:underline; }
  .help-body{ margin:8px 0 14px; padding:14px 16px; background:var(--primary-soft); border:1px solid #d4dbf0;
    border-radius:10px; font-size:.86rem; }
  .confidence-disclosure{ margin:0 0 12px; padding:10px 12px; background:var(--bg); border:1px solid var(--line);
    border-left:3px solid var(--primary); border-radius:8px; font-size:.8rem; color:var(--text-muted,#5a6072); line-height:1.45; }
  .field-err{ color:var(--red); font-size:.78rem; margin:4px 0 2px; }
  .field-invalid{ border-color:var(--red) !important; outline:none; }
  .time-echo{ font-size:.75rem; color:var(--text-muted,#5a6072); margin:3px 0 2px; min-height:1em; }
  .help-body h4{ margin:12px 0 4px; font-size:.85rem; color:var(--primary); }
  .help-body h4:first-child{ margin-top:0; }
  .help-body p{ margin:0 0 8px; color:var(--text); }
  .help-body ul{ margin:0 0 8px; padding-left:20px; }
  .help-body li{ margin:3px 0; }
  /* Vimshottari dasha tree */
  .dtree{ font-size:.94rem; }
  .drow{ display:flex; align-items:center; gap:8px; padding:6px 8px; border-radius:8px; cursor:pointer; user-select:none; transition:.1s; }
  .drow:hover{ background:var(--sunk-soft); }
  .dnode > .drow .dtwist{ display:inline-block; width:14px; color:var(--muted); font-size:.76rem; transition:transform .12s; flex:none; text-align:center; }
  .dnode.open > .drow .dtwist{ transform:rotate(90deg); }
  .dnode.leaf > .drow .dtwist{ visibility:hidden; }
  .dlord{ font-weight:700; color:var(--heading); min-width:70px; }
  .dlevel{ font-size:.78rem; color:var(--muted); background:#f0f2f5; border:1px solid var(--line); border-radius:6px; padding:1px 7px; font-weight:600; flex:none; }
  .ddates{ color:var(--muted); font-size:.86rem; margin-left:auto; white-space:nowrap; }
  .dchildren{ margin-left:16px; border-left:2px solid var(--line); padding-left:8px; display:none; }
  .dnode.open > .dchildren{ display:block; }
  .drow.active{ background:linear-gradient(90deg,var(--primary-soft),#fff); }
  .drow.active .dlord{ color:var(--primary-dark); }
  .drow.active::after{ content:"● now"; color:var(--green); font-size:.68rem; font-weight:700; margin-left:8px; flex:none; }
  /* South Indian chart wheel */
  /* container-type turns .wheel into its own layout container, so the
     @container rules below react to the wheel's actual rendered box —
     whether that's a full-width phone screen or a half-width desktop
     column — instead of only the @media rules further down, which only
     ever knew about the *viewport*, not the space this element actually got. */
  /* The WRAPPER is the container, not .wheel — see wheelBodySouth() in app.js.
     While .wheel carried container-type itself, none of the `.wheel{...}` rules
     inside @container wheel could ever match, because an element does not
     respond to a query on its own container. */
  /* 560px, down from 720. A row is at least a quarter of the width (the
     square floor on .wheel below), so width is the main lever on row height
     — at 720 a row ran ~175px against ~23px of flow content, which is the
     "lots of white space in the houses" that was reported. Narrower rows put
     the sign name, the centred planets and the ruler closer together without
     touching a single font size. */
  .wheel-wrap{ max-width:680px; margin:4px auto;
    container-type:inline-size; container-name:wheel; }
  /* Rows are AUTO-sized, not locked to a quarter of the width. aspect-ratio:1
     here fixed the wheel's HEIGHT, so every row was exactly width/4 whatever
     was in it — a house holding a stellium had its planets fall outside the
     cell, where overflow:hidden silently ate them. The square shape is now a
     FLOOR (the ::before spacer below), so a normal chart still comes out
     exactly square and only a chart that genuinely needs the room grows. */
  .wheel{ display:grid; grid-template-columns:repeat(4,1fr); grid-template-rows:repeat(4,auto); gap:6px; }
  /* Empty spacer covering the whole grid: percentage padding resolves against
     its grid area's WIDTH, so padding-top:100% asks for a box as tall as the
     wheel is wide, and the four rows together can never come out shorter than
     that. It draws nothing, and must never swallow a hover. */
  .wheel::before{ content:''; grid-area:1/1/-1/-1; padding-top:100%; pointer-events:none; }
  .wcell{ background:var(--card); border:1px solid var(--line-strong); border-radius:8px; padding:8px; position:relative; overflow:hidden; font-size:1.05rem; cursor:default; transition:.12s; min-height:100px; min-width:0;
          display:flex; flex-direction:column; justify-content:space-between; }
  .wcell:hover{ border-color:var(--primary); box-shadow:0 0 0 2px var(--primary-soft); }
  .wcell.asc{ border-color:var(--primary); background:var(--primary-soft); }
  .wcell .hn-asc{ display:inline-block; color:#fff; background:#dc2626; border-radius:5px;
                  padding:1px 7px; font-size:.72rem; font-weight:900; letter-spacing:.04em; margin-right:6px; vertical-align:middle; }
  /* One row per house-cusp / planet entry — the house cusp row sits between
     the planets whose degree falls before it (boundary<0, rendered above)
     and after it (rendered below), same convention as a published KP chart.
     Each row is its own click target (data-kind on the row, not the whole
     cell) so clicking a specific planet shows THAT planet, not the house.
     Rows never wrap — label, degree and Star.Sub lords stay one line each;
     the wheel is widened (.wheel-wrap) and each cell allowed to shrink the
     grid track that holds it (min-width:0 above) rather than the text
     wrapping onto a second line. */
  .wcell-rows{ display:flex; flex-direction:column; gap:3px; flex:1; justify-content:center; min-width:0; }
  .wcell-row{ display:flex; align-items:baseline; gap:5px; cursor:pointer; font-size:.86rem; line-height:1.3; flex-wrap:nowrap; white-space:nowrap; }
  .wcell-row:hover .wcell-row-label{ text-decoration:underline; }
  .wcell-row-label{ font-weight:800; flex:none; }
  .wcell-house-row{ padding:2px 0; }
  .wcell-house-row .wcell-row-label{ color:var(--heading); font-size:.92rem; }
  .wcell-row .cusp{ color:#9096ad; font-size:.78rem; font-variant-numeric:tabular-nums; }
  /* Star Lord . Sub Lord pair, the same KP shorthand every published chart
     shows beside a planet/cusp -- small and muted, pushed to the row's own
     right edge so degree and lords never compete for the same space. */
  .wcell-row .pl-lords{ margin-left:auto; font-weight:600; font-size:.68rem; color:#9096ad; letter-spacing:.01em; white-space:nowrap; }
  .pl-retro{ color:#dc2626; font-size:.65em; margin-left:1px; }
  .sign-glyph{ font-size:1.05em; }
  /* Sign name + glyph (left) and the sign's fixed ruler (right), pinned to
     the bottom of the box, below every cusp/planet row above it. */
  /* No border-top here: the sign-name footer used to be separated from the
     rows above by a light rule, which read as clutter inside an already-
     bordered chart box. The margin/padding alone give it enough separation. */
  .wcell-foot{ flex:none; display:flex; align-items:center; justify-content:space-between; gap:6px;
    margin-top:4px; padding-top:4px; }
  .wcell-foot .sn{ color:var(--muted); font-weight:700; font-size:.78rem; }
  .wcell-foot .ruler{ color:#b4b8c8; font-size:.76rem; font-weight:600; }
  .p-benefic{ color:#7c3aed; } .p-malefic{ color:#dc2626; } .p-neutral{ color:#374151; }
  .wcenter{ grid-row:2/4; grid-column:2/4; background:var(--sunk-soft); border:1px solid var(--line-strong); border-radius:8px; display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; padding:10px; font-size:1.02rem; color:var(--muted); gap:2px; }
  .wcenter b{ color:var(--heading); font-size:1.2rem; display:block; margin-bottom:3px; }
  /* Clicked-in house/planet detail replacing the birth-info panel */
  .wcenter.showing-detail{ justify-content:flex-start; align-items:stretch; text-align:left; padding:0; overflow-y:auto; }
  /* Floats top-right, over the card's own title row, instead of sitting in
     a full-width row above it — a row of its own was almost entirely empty
     space beside the short "HOUSE 1" / "MERCURY" title, and stretched the
     whole box taller than the content needed. */
  .wcenter-back{ position:absolute; top:8px; right:8px; background:var(--card); border:1px solid var(--line-strong);
    border-radius:7px; padding:3px 10px; font-size:.74rem; font-weight:700; color:var(--primary); cursor:pointer; z-index:1; }
  .wcenter-back:hover{ background:var(--primary-soft); }
  /* Capped and centered, same width discipline as the floating #tip card
     (272px) this content was designed for — full-bleeding it across a wide
     wcenter box is what left the value column mostly blank on the right. */
  .wcenter-detail{ position:relative; padding:0 14px 12px; max-width:300px; margin:0 auto; width:100%;
    font-size:.8rem; }
  .wcenter-detail .tip-row{ display:flex; gap:4px; }
  /* Wider than the floating tooltip's 46% AND its own explicit font-size
     (.wcenter's 1.02rem base was leaking in here, wrapping "Sub Lord (CSL)"
     and "Sub-Sub Lord" onto two lines at the tooltip's original column
     width) -- both together are what keeps every label on one line. */
  .wcenter-detail .tip-row .l{ flex:0 0 122px; white-space:nowrap; }
  .wcenter-detail .tip-row .v{ flex:1; min-width:0; word-break:break-word; }
  .wcenter-detail .tip-head{ border-radius:0; padding-right:78px; }
  /* North Indian diamond wheel */
  .nwheel{ position:relative; width:100%; max-width:720px; aspect-ratio:1; margin:4px auto;
    container-type:inline-size; container-name:nwheel; }
  .nwheel svg{ position:absolute; inset:0; width:100%; height:100%; }
  .nwheel svg line, .nwheel svg polygon{ stroke:var(--line-strong); stroke-width:.5; fill:none; }
  /* The CLIPPING box, and deliberately never the scaled one. It used to carry
     scale(var(--pl-scale)) itself, and that could not work: an element's own
     transform does not change its own clientHeight/scrollHeight, so the JS
     measured the same overflow at every scale step, ran to its floor every
     time, and .ncell still clipped at its untransformed layout box. Nine
     planets in a corner house lost five of them, silently.
     There is also no transition here any more. fitWheelCells measures and sets
     in one synchronous pass, and a transition would hand it geometry still
     animating towards the value it just wrote. */
  .ncell{ position:absolute; transform:translate(-50%,-50%); transform-origin:center;
    text-align:center; overflow:hidden; font-size:.88rem; line-height:1.3;
    display:flex; flex-direction:column; align-items:center; justify-content:center; }
  /* The SCALED box. Splitting it out from .ncell is the whole fix: the element
     being measured (.ncell, untransformed, so its clientHeight is a stable
     target) is no longer the element being transformed. flex:none keeps the
     column from squashing this instead of letting it overflow, so the natural
     height JS reads is the real one. */
  .ncell-in{ --pl-scale:1; transform:scale(var(--pl-scale)); transform-origin:center; flex:none;
    display:flex; flex-direction:column; align-items:center; gap:1px; }
  .ncell .nhead{ display:flex; align-items:baseline; gap:4px; flex:none; }
  .ncell .nhn{ color:#a5a9bd; font-size:.74rem; font-weight:700; }
  .ncell .nsn{ color:var(--heading); font-weight:700; font-size:.86rem; }
  .ncell .nasc{ display:inline-block; color:#fff; background:#dc2626; border-radius:5px; padding:1px 7px; font-size:.72rem; font-weight:900; letter-spacing:.04em; }
  .ncell .npls{ display:flex; flex-direction:column; align-items:center; gap:1px; }
  .ncell .pl{ cursor:help; font-weight:700; white-space:nowrap; }
  @container nwheel (max-width:480px){
    /* Same idea as the .wheel container query below, for the diamond layout —
       triggers on the wheel's own rendered width (phone OR a squeezed
       desktop column), not just the viewport. */
    .ncell{ font-size:.82rem; }
  }
  @media (max-width:640px){
    /* The diamond layout's 12 fixed-position cells need real room per cell —
       shrinking the whole wheel to the phone's width crams multi-planet
       houses into overlapping text. Give it a horizontal-scroll "window"
       instead of squeezing, same trick used for wide tables. */
    .nwheel-scroll{ overflow-x:auto; -webkit-overflow-scrolling:touch; margin:4px -4px; padding:4px; }
    .nwheel{ min-width:480px; max-width:480px; }
  }
  /* tooltip — matches the desktop 'card' popup (accent header + striped table) */
  #tip{ position:fixed; z-index:1000; width:272px; background:var(--card); border:1px solid var(--line); border-radius:10px;
        box-shadow:0 6px 20px rgba(31,35,48,.10); overflow:hidden; font-size:.78rem; pointer-events:auto; display:none; user-select:text; }
  /* The header was a solid slate slab (#3a4d7a) with white text -- the darkest
     surface on the page, floating over a light chart. It now reads as a tinted
     header: same hierarchy, none of the weight. */
  .tip-head{ background:var(--primary-soft); color:var(--primary); padding:8px 13px;
             border-bottom:1px solid var(--line); }
  .tip-name{ font-size:.9rem; font-weight:700; letter-spacing:.03em; }
  .tip-deg{ font-size:.74rem; font-weight:600; color:var(--muted); margin-top:1px; }
  .tip-chip{ display:inline-block; background:var(--accent); color:var(--on-status); font-size:.62rem; font-weight:700; padding:1px 7px; border-radius:8px; margin-left:7px; vertical-align:middle; }
  .tip-chip.asc{ background:var(--accent); }
  .tip-row{ display:flex; }
  .tip-row .l{ flex:0 0 46%; padding:5px 9px 5px 13px; font-weight:600; color:var(--muted); }
  .tip-row .v{ flex:1; padding:5px 12px 5px 0; font-weight:600; color:var(--text); }
  .tip-row.s{ background:var(--sunk-soft); }
  .tip-row .v b{ color:var(--primary); }
  /* On a phone a fixed 272px box is most of the screen width and could not
     shrink -- it had no mobile rule at all. It now sizes to the viewport and
     drops a step in type. */
  @media (max-width:640px){
    #tip{ width:auto; max-width:calc(100vw - 20px); font-size:.72rem; border-radius:9px; }
    .tip-head{ padding:7px 11px; }
    .tip-name{ font-size:.82rem; }
    .tip-deg{ font-size:.68rem; }
    .tip-row .l{ padding:4px 8px 4px 11px; }
    .tip-row .v{ padding:4px 10px 4px 0; }
  }
  @media (max-width:560px){ .grid{ grid-template-columns:1fr; } }
  @media (max-width:640px){
    /* Phone sizing. The planets are in normal flow at every width now, so
       this no longer has to undo an absolute overlay — all it does is drop a
       step in type and pin the block just under the sign/cusp header instead
       of centring it in the cell's leftover room. */
    .wheel{ gap:4px; }
    .wcell{ min-height:0; font-size:.68rem; padding:5px 5px 16px; }
    .wcell .hn{ font-size:.62rem; }
    .wcell .hn-asc{ font-size:.66rem; padding:1px 6px; }
    .wcell .sn{ font-size:.78rem; }
    .wcell .cusp{ font-size:.6rem; }
    .wcell-mid{ margin:2px 0 0; }
    .wcell .pl{ font-size:.66rem; line-height:1.2; }
    .wcell .ruler{ position:static; display:block; margin-top:2px; left:auto; bottom:auto; font-size:.6rem; }
    .wcenter{ font-size:.72rem; padding:6px; }
    .wcenter b{ font-size:.86rem; }
  }
  @container wheel (max-width:460px){
    /* Same narrow-chart sizing as the phone @media block above, triggered
       instead by the wheel's own rendered width — this is what actually
       fires when the chart is squeezed into a narrow desktop column (e.g. the
       two-column results layout) rather than a small viewport. */
    .wheel{ gap:4px; }
    /* padding-bottom is 6px, not 18px: the 18px reserved room for .ruler while
       it is absolutely positioned, but THIS block makes it static (below), so
       it flows normally and the reserve was dead space at the foot of every
       cell. Measured before/after on a 457px-wide wheel.
       justify-content:center distributes what slack remains evenly instead of
       stacking it all under the content — grid stretches every cell to its
       row's tallest, so a sparse house next to a busy one was showing a 77px
       gap below its sign name. */
    .wcell{ min-height:0; font-size:.78rem; padding:6px 6px 6px; justify-content:center; }
    .wcell .hn{ font-size:.66rem; }
    .wcell .hn-asc{ font-size:.7rem; padding:1px 6px; }
    .wcell .sn{ font-size:.86rem; }
    .wcell .cusp{ font-size:.66rem; }
    .wcell-mid{ margin:2px 0 0; }
    .wcell .pl{ font-size:.76rem; line-height:1.2; }
    .wcell .ruler{ position:static; display:block; margin-top:2px; left:auto; bottom:auto; font-size:.64rem; }
    .wcenter{ font-size:.8rem; padding:8px; }
    .wcenter b{ font-size:.94rem; }
  }
  /* ---- account bar ---- */
  /* ---- chart result toolbar: Save chart / KP Quick Summary, pinned top-right ---- */
  .acctbar{ display:flex; flex-wrap:wrap; align-items:center; justify-content:flex-end; gap:8px; font-size:.82rem; }
  .acctbar .who{ color:var(--muted); font-weight:600; max-width:140px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  @media (max-width:640px){
    .acctbar{ justify-content:center; }
    .abtn, .credits, .badge-admin{ font-size:.76rem; padding:5px 9px; }
  }
  .credits{ display:inline-flex; align-items:center; gap:5px; background:var(--primary-soft); color:var(--primary-dark);
            border:1px solid #d4dbeb; border-radius:20px; padding:3px 11px; font-weight:700; }
  /* display:inline-flex + a fixed line-height keep every pill button the
     same box height regardless of what glyph is inside it — some emoji
     (📂 in particular) carry more natural line-box height than plain text
     or simpler glyphs (＋, 💾), which without this made "Load Saved Chart"
     render visibly taller than "New Chart"/"Save Chart" next to it even
     though the CSS padding was identical on all three. */
  .abtn{ display:inline-flex; align-items:center; justify-content:center; line-height:1;
         height:var(--ctl-h); padding:0 16px; border-radius:20px; border:1px solid var(--line-strong); background:var(--card); color:var(--primary);
         font-weight:600; cursor:pointer; font-size:.82rem; transition:background .1s ease,border-color .1s ease; }
  .abtn:hover{ border-color:var(--line-strong); background:var(--sunk-soft); }
  .abtn.solid{ background:var(--primary); color:var(--on-primary); border-color:var(--primary); }
  .abtn.solid:hover{ background:var(--primary-dark); border-color:var(--primary-dark); }
  .abtn.ghost{ border:none; background:none; color:var(--muted); }
  .abtn.ghost:hover{ color:var(--primary); background:none; }
  .abtn:disabled{ opacity:.45; cursor:not-allowed; box-shadow:none; transform:none; background:var(--card); color:var(--muted); border-color:var(--line-strong); }
  .abtn:disabled:hover{ background:var(--card); border-color:var(--line-strong); transform:none; }
  /* ---- unified chart action bar: Load Saved / New / Save together, one
     professional row instead of scattered floating buttons ---- */
  .action-toolbar{ display:flex; align-items:center; justify-content:space-between; gap:10px; flex-wrap:wrap;
    margin:-2px -2px 16px; padding:10px 12px; background:var(--primary-soft); border:1px solid var(--line);
    border-radius:12px; }
  .action-toolbar-group{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
  /* .loadsaved-wrap normally carries margin-bottom:14px for its standalone
     (non-toolbar) use on the Rectification panel; inside the shared toolbar
     row that extra margin makes its flex box taller than its sibling group,
     so align-items:center centers the button itself a few px off from
     New/Save Chart. The dropdown is positioned absolute either way, so the
     margin isn't needed for spacing here — drop it in this context only. */
  .action-toolbar-group.loadsaved-wrap{ margin-bottom:0; }
  @media (max-width:640px){ .action-toolbar{ justify-content:stretch; } .action-toolbar-group{ width:100%; justify-content:stretch; }
    .action-toolbar-group .abtn{ flex:1; } }
  /* ---- collapsible entry form (Birth/Horary), after a chart exists ----
     Pure CSS visibility toggle -- collapseEntryCard()/expandEntryCard() in
     app.js only add/remove one class each, no re-render, no network call,
     so this costs nothing at runtime. The toolbar (Load/New/Save Chart)
     stays outside .entry-fields so it is never hidden -- New Chart is the
     one control that must always be reachable to start over. */
  .entry-summary{ display:flex; align-items:center; justify-content:space-between; gap:10px;
    padding:10px 14px; margin:0 0 14px; background:var(--primary-soft); border:1px solid var(--line);
    border-radius:10px; font-size:.88rem; cursor:pointer; }
  .entry-summary:hover{ border-color:var(--line-strong); }
  .entry-summary .es-edit{ color:var(--primary); font-weight:700; font-size:.8rem; flex:none; }
  #b_entry_card.entry-collapsed .entry-fields,
  #h_entry_card.entry-collapsed .entry-fields{ display:none; }
  .badge-admin{ background:#1f2433; color:#fff; border-radius:6px; padding:2px 8px; font-size:.7rem; font-weight:700; }
  /* ---- Google Sign-In button (standard Google brand button shape) ---- */
  .gsi-btn{ width:100%; display:flex; align-items:center; justify-content:center; gap:10px;
            padding:10px 16px; margin-bottom:14px; background:#fff; color:#3c4043;
            border:1px solid #dadce0; border-radius:9px; font-size:.9rem; font-weight:600;
            font-family:inherit; cursor:pointer; transition:.15s; box-shadow:0 1px 2px rgba(0,0,0,.06); }
  .gsi-btn:hover{ background:#f7f8f8; border-color:#c6c8ca; box-shadow:0 1px 3px rgba(0,0,0,.12); }
  .gsi-btn:active{ background:#f1f3f4; }
  .gsi-btn svg{ flex:none; }
  /* ---- profile menu (gear + avatar dropdown, Gmail/Facebook-style) ---- */
  .gearbtn{ width:32px; height:32px; border-radius:50%; border:1px solid var(--line-strong); background:var(--card);
            color:var(--muted); font-size:1rem; cursor:pointer; display:inline-flex; align-items:center; justify-content:center; }
  .gearbtn:hover{ border-color:var(--primary); color:var(--primary); background:var(--primary-soft); }
  .acctmenu{ position:relative; display:inline-block; }
  .who-btn{ display:inline-flex; align-items:center; gap:7px; padding:4px 10px 4px 4px; border-radius:20px;
            border:1px solid var(--line-strong); background:var(--card); cursor:pointer; transition:.15s; }
  .who-btn:hover{ border-color:var(--primary); background:var(--primary-soft); box-shadow:var(--shadow); }
  .who-btn .avatar{ width:26px; height:26px; border-radius:50%; background:linear-gradient(135deg,var(--primary),var(--accent)); color:var(--on-primary);
                     font-weight:700; font-size:.8rem; display:flex; align-items:center; justify-content:center; flex:none; }
  .who-btn .who-label{ max-width:150px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:.82rem;
                        font-weight:600; color:var(--text); }
  .who-btn .caret{ color:var(--muted); font-size:.7rem; }
  .acctmenu-drop{ position:absolute; top:calc(100% + 8px); right:0; min-width:220px; background:var(--card);
                   border:1px solid var(--line-strong); border-radius:12px; box-shadow:var(--shadow); z-index:120;
                   overflow:hidden; padding:6px; }
  .acctmenu-drop.hidden{ display:none; }
  .acctmenu-drop .acctmenu-head{ padding:8px 10px; font-size:.78rem; color:var(--muted); border-bottom:1px solid var(--line); margin-bottom:4px; }
  .acctmenu-drop button{ display:flex; align-items:center; gap:9px; width:100%; padding:9px 10px; border:none; background:none;
                          text-align:left; font-size:.85rem; font-weight:600; color:var(--text); border-radius:8px; cursor:pointer; }
  .acctmenu-drop button:hover{ background:var(--primary-soft); color:var(--primary); }
  .acctmenu-drop .mi{ width:18px; text-align:center; flex:none; }
  /* ---- Admin notification bell (topbar, admin-only) ---- */
  .notifwrap{ position:relative; display:inline-block; }
  .notifwrap.hidden{ display:none; }
  .notifbtn{ position:relative; display:inline-flex; align-items:center; justify-content:center;
             width:34px; height:34px; border-radius:50%; border:1px solid var(--line-strong);
             background:var(--card); cursor:pointer; font-size:1rem; line-height:1; }
  .notifbtn:hover{ border-color:var(--primary); background:var(--primary-soft); }
  /* Count badge. min-width + padding (not a fixed width) so "12" and "99+"
     both stay legible instead of clipping. */
  .notifbadge{ position:absolute; top:-4px; right:-4px; min-width:17px; height:17px; padding:0 4px;
               border-radius:9px; background:var(--red); color:#fff; font-size:.62rem; font-weight:800;
               display:flex; align-items:center; justify-content:center; border:2px solid #fff; }
  .notifbadge.hidden{ display:none; }
  .notifdrop{ position:absolute; top:calc(100% + 8px); right:0; width:340px; max-width:calc(100vw - 24px);
              background:var(--card); border:1px solid var(--line-strong); border-radius:12px;
              box-shadow:var(--shadow-lg); z-index:130; overflow:hidden; }
  .notifdrop.hidden{ display:none; }
  .notifhead{ display:flex; align-items:center; justify-content:space-between; gap:8px;
              padding:10px 12px; border-bottom:1px solid var(--line); }
  .notifhead strong{ font-size:.86rem; }
  .notiflist{ max-height:min(420px, 60vh); overflow-y:auto; }
  .notifitem{ display:flex; gap:9px; width:100%; padding:10px 12px; border:none; background:none;
              text-align:left; cursor:pointer; border-bottom:1px solid var(--line); }
  .notifitem:last-child{ border-bottom:none; }
  .notifitem:hover{ background:var(--primary-soft); }
  .notifitem.unread{ background:#f4f7ff; }
  .notifitem.unread:hover{ background:var(--primary-soft); }
  .notifitem .ni-ico{ flex:none; width:26px; height:26px; border-radius:50%; display:flex;
                      align-items:center; justify-content:center; font-size:.8rem; background:var(--primary-soft); }
  .notifitem .ni-body{ min-width:0; flex:1; }
  .notifitem .ni-label{ font-size:.82rem; font-weight:700; color:var(--text); }
  .notifitem .ni-detail{ font-size:.75rem; color:var(--muted); margin-top:1px;
                         overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .notifitem .ni-when{ font-size:.68rem; color:var(--muted); margin-top:2px; }
  /* align-self:center so the button stays vertically centred against a
     one- or three-line row rather than stretching. */
  .notifitem .ni-approve{ flex:none; align-self:center; padding:4px 10px; border-radius:14px;
                          border:1px solid var(--green); background:var(--card); color:var(--green);
                          font-size:.7rem; font-weight:800; cursor:pointer; white-space:nowrap; }
  .notifitem .ni-approve:hover:not(:disabled){ background:var(--green); color:#fff; }
  .notifitem .ni-approve:disabled{ opacity:.7; cursor:default; }
  .notifitem .ni-approve.done{ border-color:var(--green); background:var(--green); color:#fff; }
  .notifempty{ padding:22px 12px; text-align:center; color:var(--muted); font-size:.82rem; }
  /* ---- "Load Saved" quick-access (Birth/Horary/Financial/Rectification) ---- */
  .loadsaved-wrap{ position:relative; display:inline-block; margin-bottom:14px; }
  .loadsaved-wrap.hidden{ display:none; }
  .loadsaved-btn{ display:inline-flex; align-items:center; gap:7px; }
  .loadsaved-drop{ position:absolute; top:calc(100% + 6px); left:0; z-index:130; width:320px; max-width:min(320px,88vw);
    background:var(--card); border:1px solid var(--line-strong); border-radius:12px; box-shadow:var(--shadow-lg);
    max-height:340px; overflow-y:auto; padding:6px; }
  .loadsaved-drop.hidden{ display:none; }
  .loadsaved-item{ display:flex; flex-direction:column; gap:2px; width:100%; padding:9px 10px; border:none; background:none;
    text-align:left; border-radius:8px; cursor:pointer; }
  .loadsaved-item:hover{ background:var(--primary-soft); }
  .loadsaved-item b{ font-size:.86rem; color:var(--text); }
  .loadsaved-item span{ font-size:.76rem; color:var(--muted); }
  .loadsaved-empty, .loadsaved-loading{ padding:14px 10px; font-size:.82rem; color:var(--muted); text-align:center; }
  /* ---- modal ---- */
  .modal-bg{ position:fixed; inset:0; background:rgba(20,22,34,.5); display:none; align-items:center; justify-content:center; z-index:2000; padding:16px; }
  .modal-bg.show{ display:flex; }
  /* Base modal — capped height + internal scroll BY DEFAULT. Audit found
     only 7 of this app's 29 modals had their own inline max-height/
     overflow-y override; the other 22 (AI Settings, User Lookup, Security
     State, Announcement, Feature Flags, ...) had none at all, so any of
     them with content taller than a phone screen was simply unreachable —
     no way to scroll down to Save/Close, top and bottom both cut off. Fixed
     once here instead of patching each modal individually, so every
     CURRENT and FUTURE modal is safe by default; a modal that also sets its
     own inline max-height (e.g. .pricing-modal) still wins, since inline
     styles beat this class rule — this is just the floor everyone gets. */
  .modal{ background:var(--card); border-radius:16px; box-shadow:0 20px 60px rgba(0,0,0,.3); width:100%; max-width:400px; padding:26px;
    max-height:88vh; overflow-y:auto; -webkit-overflow-scrolling:touch; }
  @media (max-width:480px){
    /* Phones lose more of the viewport to the browser chrome/keyboard —
       give modals a bit more of what's left, and tighten padding so a
       compact form doesn't waste space that could show more content. */
    .modal-bg{ padding:10px; align-items:flex-start; padding-top:5vh; }
    .modal{ max-height:90vh; padding:18px; }
  }
  /* ---- dedicated /sign-in page: same login form, rendered as a full page
     instead of a popup over the app shell (no backdrop, no close button,
     plus a "Continue as guest" way out) ---- */
  /* #loginModal lives INSIDE .app-shell/.main-col in the DOM (shared with
     the rest of the app's modals) — hiding .app-shell itself would hide the
     login form along with it (the blank-page bug this replaced). Hide the
     surrounding chrome by name instead, leaving .app-shell/.main-col (and
     therefore the modal inside them) in the render tree. */
  body.signin-page{ display:flex; align-items:stretch; }
  body.signin-page .sidenav,
  body.signin-page .main-col > .topbar,
  body.signin-page .main-col > .wrap,
  body.signin-page .main-col > .sitefooter{ display:none; }
  body.signin-page .app-shell{ flex:1; min-width:0; }
  body.signin-page #loginModal{ position:static; display:flex; flex-direction:column; min-height:100vh; background:var(--bg); padding:40px 20px; }
  body.signin-page #loginModal .mclose{ display:none; }
  body.signin-page #loginModal .modal{ margin:auto; width:100%; max-width:420px; box-shadow:var(--shadow-lg); border-radius:18px;
    padding:34px 32px; animation:signinPop .35s cubic-bezier(.2,.9,.3,1.2); }
  @keyframes signinPop{ from{ opacity:0; transform:translateY(10px) scale(.98); } to{ opacity:1; transform:none; } }
  .signin-brand{ display:none; align-items:center; gap:10px; justify-content:center; margin-bottom:18px; }
  body.signin-page .signin-brand{ display:flex; }
  .signin-brand img{ width:34px; height:34px; }
  .signin-brand b{ font-size:1.05rem; color:var(--primary); letter-spacing:-.01em; }
  .signin-guest{ display:none; text-align:center; margin-top:16px; padding-top:14px; border-top:1px solid var(--line); font-size:.85rem; color:var(--muted); }
  body.signin-page .signin-guest{ display:block; }
  .signin-guest a{ font-weight:700; }
  /* Consent notice, at the foot of the sign-in dialog. Quiet by default and
     styled AS fine print rather than as an action, because it competes with
     nothing: it is the last thing in the dialog and no control sits past it. */
  .signin-legal{ margin:14px 0 0; text-align:center; font-size:.76rem;
    line-height:1.5; color:var(--muted); }
  .signin-legal a{ color:var(--muted); text-decoration:underline;
    text-underline-offset:2px; }
  .signin-legal a:hover{ color:var(--primary); }
  @media (max-width:640px){
    /* On a touch screen the link becomes its own line with a real tap target
       — 44px is the size a thumb actually hits — instead of a few words
       embedded mid-sentence. Deliberate taps land; drifting ones fall on the
       padding around it rather than on a button. */
    .signin-legal{ margin-top:18px; }
    .signin-legal a{ display:inline-block; min-height:44px; line-height:44px;
      padding:0 12px; }
  }
  /* ===== Auth simplification (AS-4): rules from docs/mockups/kp_auth_simplification/proposed.css, tokens only ===== */
  /* ---- Sign-in card (screenshot layout) ---- */
  .as-alt{ margin:0 0 18px; font-size:.86rem; color:var(--muted); }
  .as-divider{ display:flex; align-items:center; gap:12px; margin:4px 0 2px; color:var(--muted); font-size:.78rem; }
  .as-divider::before, .as-divider::after{ content:""; flex:1; height:1px; background:var(--line); }
  .as-divider.guest{ margin-top:18px; }
  .as-primary{ width:100%; background:var(--primary); color:var(--on-primary); border-color:var(--primary); }
  .as-primary:hover{ background:var(--primary-dark); border-color:var(--primary-dark); }
  .as-links{ display:flex; justify-content:space-between; gap:12px; flex-wrap:wrap; margin:12px 0 0; font-size:.85rem; }
  .as-guest{ width:100%; margin-top:12px; }
  .as-back{ display:inline-block; margin-top:12px; font-size:.85rem; }

  /* ---- "Check your email" and other in-card states ---- */
  .as-state-ico{ width:44px; height:44px; border-radius:50%; display:flex; align-items:center; justify-content:center;
    background:var(--primary-soft); color:var(--primary); margin:0 0 12px; }
  .as-target{ color:var(--heading); font-weight:700; overflow-wrap:anywhere; }
  .as-code{ letter-spacing:.35em; font-variant-numeric:tabular-nums; font-weight:700; text-align:center; }
  .as-help{ font-size:.8rem; color:var(--muted); margin:8px 0 0; line-height:1.5; }
  .as-err{ color:var(--danger); font-size:.82rem; margin:8px 0 0; }
  .as-ok{ color:var(--success); font-size:.86rem; font-weight:600; margin:10px 0 0; }
  .as-row-links{ display:flex; gap:14px; flex-wrap:wrap; margin-top:10px; font-size:.82rem; }
  .as-row-links .off{ color:var(--muted); }

  /* ---- Callout inside the card (account already exists, connect Google, step-up, migration) ---- */
  .as-callout{ background:var(--warning-soft); border:1px solid var(--line-strong); border-radius:12px; padding:12px 14px;
    margin:0 0 12px; font-size:.86rem; line-height:1.5; color:var(--text); }
  .as-callout.info{ background:var(--info-soft); }
  .as-callout b{ color:var(--heading); }
  .as-callout-acts{ display:flex; gap:8px; flex-wrap:wrap; margin-top:10px; }
  .as-callout-acts .btn, .as-callout-acts .abtn{ width:auto; margin-top:0; }
  @media (max-width:480px){ .as-callout-acts .btn, .as-callout-acts .abtn{ flex:1 0 100%; } }

  /* ---- Temporary line under the card during the migration window (outside the card) ---- */
  .as-legacy-line{ max-width:420px; margin:14px auto 0; text-align:center; font-size:.8rem; color:var(--muted); line-height:1.5; }

  /* ---- Settings → Sign-in methods ---- */
  .as-method{ display:flex; align-items:center; gap:14px; padding:14px 0; flex-wrap:wrap; }
  .as-method + .as-method{ border-top:1px solid var(--line); }
  .as-method-ico{ width:36px; height:36px; flex:none; border-radius:10px; display:flex; align-items:center; justify-content:center;
    background:var(--sunk-soft, var(--bg)); border:1px solid var(--line); color:var(--heading); }
  .as-method-main{ flex:1; min-width:180px; }
  .as-method-name{ font-weight:700; }
  .as-method-sub{ font-size:.82rem; color:var(--muted); overflow-wrap:anywhere; }
  .as-method-acts{ display:flex; gap:8px; flex-wrap:wrap; }
  .as-method-acts .abtn{ margin:0; }
  .as-pill{ display:inline-flex; align-items:center; gap:4px; font-size:.72rem; font-weight:700; padding:2px 9px;
    border-radius:99px; background:var(--success-soft); color:var(--success); margin-left:6px; vertical-align:middle; }
  .as-pill.warn{ background:var(--warning-soft); color:var(--heading); }
  .as-pill.muted{ background:var(--sunk-soft, var(--bg)); color:var(--muted); }

  /* ---- Admin User Lookup: sign-in methods group ---- */
  .as-admin-kv{ display:grid; grid-template-columns:minmax(110px,max-content) minmax(0,1fr); gap:6px 14px; font-size:.85rem; margin:4px 0 10px; }
  .as-admin-kv dt{ color:var(--muted); font-weight:700; }
  .as-admin-kv dd{ margin:0; overflow-wrap:anywhere; }
  /* Guest way out: the divider and button show only on the dedicated /sign-in page (as the old text link did). */
  .as-divider.guest, .as-guest{ display:none; }
  body.signin-page .as-divider.guest{ display:flex; }
  body.signin-page .as-guest{ display:flex; justify-content:center; }
  .av{ display:none; } .av.on{ display:block; }
  .u3sg-b, .u3sg-o, .u3k-code > i{ display:none; }   /* UI 3 sign-in sheets: decorative badges, the Back link, the Staff eyebrow and the code boxes exist only in the Sutra look (its kit stylesheet shows them) */
  @media (max-width:480px){ body.signin-page #loginModal .modal{ padding:22px 18px; } }   /* mockup 01-07 at 390 px */
  .as-hidden{ display:none !important; }
  .av-err:empty{ display:none; }
  #loginBox .as-callout .btn, #loginBox .as-callout .gsi-btn{ margin-top:0; }
  #loginBox.no-brand .signin-brand{ display:none !important; }
  /* ---- confirm-modal consequence list ---- */
  .confirm-list{ margin:2px 0 4px 18px; padding:0; font-size:.86rem; line-height:1.5; color:var(--text); }
  .confirm-list li{ margin:0 0 6px; }
  /* Google was refused (mockup 05 step 1): only the guidance, the pre-filled address and the email form remain. */
  #loginBox.conflict #lg_alt, #loginBox.conflict #lg_google, #loginBox.conflict #lg_div1, #loginBox.conflict #lg_passkey_row,
  #loginBox.conflict #lg_div2, #loginBox.conflict #guestContinueBtn, #loginBox.conflict #lg_legal{ display:none !important; }
  #loginBox.conflict #lg_conflict{ display:block !important; }
  #loginBox.conflict #lg_err:empty{ display:none; }
  /* left brand panel — hidden everywhere except the /sign-in page */
  .signin-side{ display:none; }
  body.signin-page .signin-side{
    display:flex; flex:0 0 44%; max-width:560px; min-height:100vh; padding:56px 48px;
    background:radial-gradient(120% 140% at 15% 0%, var(--sky-bg-2) 0%, var(--sky-bg) 100%);
    color:var(--on-sky); position:relative; overflow:hidden;
  }
  body.signin-page .signin-side::before{ content:""; position:absolute; inset:0;
    background:radial-gradient(46% 46% at 88% 12%, rgba(232,181,58,.22), transparent 70%),
               radial-gradient(50% 50% at 10% 95%, rgba(217,79,43,.20), transparent 70%); pointer-events:none; }
  .signin-side-inner{ position:relative; margin:auto; max-width:400px; }
  .signin-side-brand{ display:inline-flex; align-items:center; gap:10px; text-decoration:none; color:var(--on-sky); margin-bottom:38px; }
  .signin-side-brand img{ width:36px; height:36px; }
  .signin-side-brand b{ font-size:1.1rem; letter-spacing:-.01em; }
  .signin-side h2{ margin:0 0 26px; font-size:1.55rem; line-height:1.35; font-weight:700; letter-spacing:-.01em; }
  .signin-features{ list-style:none; margin:0 0 30px; padding:0; display:flex; flex-direction:column; gap:13px; }
  .signin-features li{ display:flex; align-items:flex-start; gap:10px; font-size:.92rem; color:#dbe1f7; line-height:1.4; }
  .sf-ico{ flex:none; width:20px; height:20px; border-radius:50%; background:rgba(232,181,58,.9); color:var(--primary-dark);
           font-weight:800; font-size:.72rem; display:flex; align-items:center; justify-content:center; margin-top:1px; }
  .signin-side-quote{ margin:0; padding-top:22px; border-top:1px solid rgba(255,255,255,.18); font-size:.85rem;
                       font-style:italic; color:#c3cbec; line-height:1.5; }
  @media (max-width:900px){ body.signin-page .signin-side{ display:none; } body.signin-page .app-shell{ flex:1 1 100%; } }
  .modal h3{ margin:0 0 6px; color:var(--heading); font-size:1.2rem; }
  .modal p.sub{ margin:0 0 18px; color:var(--muted); font-size:.85rem; }
  .modal .mclose{ float:right; cursor:pointer; color:var(--muted); font-size:1.3rem; line-height:1; border:none; background:none; }
  .modal .err{ min-height:18px; margin:8px 0 0; font-size:.82rem; }
  /* Pricing modal — grew from one price form into price + a 7-row pack
     editor, which overflowed the base .modal's unbounded height above and
     below the viewport. Fixed shell instead of one scrolling blob: a fixed
     head (title/close) and foot (save/reset packs) stay pinned in view,
     only the middle content area scrolls — same pattern a real settings
     dialog uses, not just capping height and hoping content fits. */
  .pricing-modal{ max-width:600px; width:100%; max-height:min(720px,88vh); padding:0;
    display:flex; flex-direction:column; overflow:hidden; }
  .pricing-modal-head{ flex:none; display:flex; align-items:center; justify-content:space-between;
    padding:20px 26px 14px; border-bottom:1px solid var(--line); }
  .pricing-modal-head h3{ margin:0; }
  .pricing-modal-head .mclose{ float:none; }
  .pricing-modal-body{ flex:1 1 auto; min-height:0; overflow-y:auto; padding:18px 26px; }
  .pricing-modal-foot{ flex:none; display:flex; gap:10px; padding:14px 26px; border-top:1px solid var(--line); background:var(--bg); }
  .pricing-modal-foot .btn, .pricing-modal-foot .abtn{ flex:1; margin:0; }
  .pricing-section{ padding-bottom:20px; margin-bottom:20px; border-bottom:1px solid var(--line); }
  .pricing-section:last-child{ border-bottom:none; margin-bottom:0; padding-bottom:0; }
  /* Pack row editor: a real grid with a column header, not a flex row that
     silently wraps its 4 inputs into an unreadable stack on a 400px modal. */
  .pack-edit-head{ display:grid; grid-template-columns:1fr 1.8fr 64px 78px 30px; gap:8px;
    font-size:.68rem; font-weight:700; letter-spacing:.03em; text-transform:uppercase;
    color:var(--muted); margin:14px 0 4px; padding:0 2px; }
  /* Saved-vs-shipped pricing disagreement. Amber, not red: nothing is broken
     and nothing needs undoing — the saved numbers are live and legitimate.
     It is a difference the admin should know about before the next sale. */
  .drift-note{ border:1px solid #e0b25c; border-left:3px solid #d38b12;
    background:rgba(224,178,92,.10); border-radius:10px; padding:10px 12px; margin:0 0 12px;
    font-size:.88rem; }
  .drift-note ul{ margin:6px 0 0; padding-left:18px; }
  .drift-note li{ margin:2px 0; }
  .drift-note p{ margin:4px 0 0; }
  .pack-edit-row{ display:grid; grid-template-columns:1fr 1.8fr 64px 78px 118px 30px; gap:8px;
    align-items:center; margin-top:6px; }
  .pack-edit-row input, .pack-edit-row select{ padding:7px 9px; font-size:.82rem; }
  .fa-grid{ width:100%; border-collapse:collapse; margin-top:8px; font-size:.85rem; }
  .fa-grid th, .fa-grid td{ padding:8px 6px; border-bottom:1px solid var(--line); }
  .fa-grid th{ font-size:.75rem; color:var(--muted); font-weight:600; }
  .fa-grid td.c, .fa-grid th.c{ text-align:center; }
  .ceil-grid td{ vertical-align:top; min-width:132px; }
  .ceil-grid td.ceil-feat{ min-width:170px; font-weight:600; }
  .ceil-grid input.ceil-in{ width:92px; padding:4px 6px; border:1px solid var(--line); border-radius:6px; background:var(--card); color:var(--text); font:inherit; }
  .ceil-grid input.ceil-in.edited{ border-color:var(--accent); box-shadow:0 0 0 1px var(--accent); }
  .ceil-grid .ceil-line{ font-size:.75rem; margin-top:3px; line-height:1.3; color:var(--muted); }
  .ceil-grid .ceil-line.ok{ color:var(--green); }
  .ceil-grid .ceil-line.tight{ color:var(--amber); }
  .ceil-grid .ceil-line.bad{ color:var(--red); }
  .ceil-grid .ceil-stale{ opacity:.45; }
  .ceil-grid tr.ceil-div td{ font-size:.72rem; font-weight:600; color:var(--muted); text-transform:uppercase; letter-spacing:.04em; }
  .fa-grid input[type=checkbox]{ width:18px; height:18px; accent-color:var(--primary); }
  .pack-edit-row .pk-del{ flex:none; width:30px; height:30px; padding:0; border-radius:8px;
    border:1px solid var(--line-strong); background:var(--card); color:var(--muted); cursor:pointer; font-size:.9rem; line-height:1; }
  .pack-edit-row .pk-del:hover{ color:var(--red,#e0392b); border-color:var(--red,#e0392b); background:#fdecea; }
  @media (max-width:560px){
    /* Below ~560px five columns can't stay legible — switch to a stacked
       card per pack instead of squeezing text illegibly small. */
    .pack-edit-head{ display:none; }
    .pack-edit-row{ grid-template-columns:1fr 1fr; row-gap:6px; padding:10px; margin-top:10px;
      background:var(--bg); border:1px solid var(--line); border-radius:10px; }
    .pack-edit-row .pk_label{ grid-column:1 / -1; }
    .pack-edit-row .pk-del{ grid-column:1 / -1; justify-self:end; width:auto; padding:4px 10px; }
  }
  /* Currency-rate row editor — same grid pattern as the pack editor above,
     own column widths (4 short fields instead of a long label). */
  .currency-edit-head{ display:grid; grid-template-columns:1fr 1.4fr 90px 70px 30px; gap:8px;
    font-size:.68rem; font-weight:700; letter-spacing:.03em; text-transform:uppercase;
    color:var(--muted); margin:14px 0 4px; padding:0 2px; }
  .currency-edit-row{ display:grid; grid-template-columns:1fr 1.4fr 90px 70px 30px; gap:8px;
    align-items:center; margin-top:6px; }
  .currency-edit-row input{ padding:7px 9px; font-size:.82rem; }
  .currency-edit-row .cr-del{ flex:none; width:30px; height:30px; padding:0; border-radius:8px;
    border:1px solid var(--line-strong); background:var(--card); color:var(--muted); cursor:pointer; font-size:.9rem; line-height:1; }
  .currency-edit-row .cr-del:hover{ color:var(--red,#e0392b); border-color:var(--red,#e0392b); background:#fdecea; }
  @media (max-width:560px){
    .currency-edit-head{ display:none; }
    .currency-edit-row{ grid-template-columns:1fr 1fr; row-gap:6px; padding:10px; margin-top:10px;
      background:var(--bg); border:1px solid var(--line); border-radius:10px; }
    .currency-edit-row .cr-del{ grid-column:1 / -1; justify-self:end; width:auto; padding:4px 10px; }
  }
  /* International question packs — one explicit price per currency per
     tier, shown as a table since the column count is dynamic (one per
     configured currency) rather than a fixed grid like the INR pack rows. */
  .intl-pack-table{ border-collapse:collapse; width:100%; font-size:.8rem; }
  .intl-pack-table th{ text-align:left; font-size:.68rem; text-transform:uppercase; letter-spacing:.03em;
    color:var(--muted); padding:4px 6px; white-space:nowrap; }
  .intl-pack-table td{ padding:4px 6px; }
  .intl-pack-table input{ padding:6px 7px; font-size:.8rem; width:100%; box-sizing:border-box; display:block; }
  .intl-pack-table .ipk_price{ margin-bottom:3px; }
  .intl-pack-table .ipk_credits_ov{ background:var(--bg); }
  .intl-pack-table .ipk_id{ width:64px; } .intl-pack-table .ipk_label{ width:130px; }
  .intl-pack-table .ipk_credits{ width:52px; } .intl-pack-table td.ipk-price-cell{ width:82px; }
  .intl-pack-table .pk-del{ width:26px; height:26px; padding:0; border-radius:8px; }
  .intl-pack-table .pk-del:hover{ color:var(--red,#e0392b); border-color:var(--red,#e0392b); background:#fdecea; }
  .modal .step2{ display:none; } .modal.otp-sent .step1{ display:none; } .modal.otp-sent .step2{ display:block; }
  .premium-tag{ display:inline-block; background:linear-gradient(90deg,#b8860b,#e6b800); color:#fff; font-size:.66rem;
                font-weight:800; letter-spacing:.04em; padding:2px 8px; border-radius:6px; vertical-align:middle; margin-left:6px; }
  /* One row for every "ask about this chart" action (Explain with AI /
     Quick Insight / Explain My Life Events) -- same height via .btn/.abtn's
     own var(--ctl-h), wraps as a clean stack on narrow screens instead of
     the buttons crowding or the row scrolling sideways. */
  .ai-action-row{ display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin-top:16px; }
  .ai-action-row .btn{ margin-top:0; }
  @media (max-width:640px){ .ai-action-row{ flex-direction:column; align-items:stretch; }
    .ai-action-row .btn, .ai-action-row .abtn{ width:100%; } }
  /* The quiet hand-off line inside #b_life_events_out, between the card
     stream and where the follow-up chat bubbles begin. */
  .life-events-chat-hint{ text-align:center; font-size:.78rem; color:var(--muted);
    padding:4px 0 2px; border-top:1px dashed var(--line); margin-top:2px; }
  /* "See a real one first" sample inside the Life Events paywall/sign-in
     state — three icon chips, one card expanding in place. Left-aligned
     inside a centered .paywall on purpose: the card is body text, and
     centered bullet lists are unreadable. */
  .le-sample{ text-align:left; margin:12px 0; }
  .le-sample-lead{ text-align:center; margin:0 0 6px; }
  .le-sample-chips{ display:flex; flex-wrap:wrap; gap:8px; justify-content:center; margin-bottom:10px; }
  .le-sample-chip{ cursor:pointer; border:1px solid var(--line-strong); background:var(--card);
    color:var(--text); border-radius:999px; padding:5px 12px; font-size:.82rem; font-weight:600; }
  .le-sample-chip:hover{ border-color:var(--accent); }
  .le-sample-chip.active{ background:var(--accent); border-color:var(--accent); color:var(--on-status); }
  /* Caret flips when the chip's card is open, so "click again to close" is
     visible rather than something you have to guess at. */
  .le-sample-caret{ display:inline-block; margin-left:6px; font-size:.7rem; transition:transform .15s; }
  .le-sample-chip.active .le-sample-caret{ transform:rotate(180deg); }
  .le-sample-card{ position:relative; background:var(--card); border:1px solid var(--line); border-radius:10px;
    padding:12px 34px 12px 14px; margin-bottom:10px; }
  .le-sample-close{ position:absolute; top:6px; right:8px; border:0; background:none; cursor:pointer;
    font-size:1.15rem; line-height:1; color:var(--muted); padding:2px 4px; border-radius:6px; }
  .le-sample-close:hover{ background:var(--sunk-soft); color:var(--text); }
  .le-sample-headline{ font-weight:600; margin:0 0 6px; }
  .le-sample-card ul{ margin:0; padding-left:18px; }
  /* :not(.win-ahead-row): the F3b "ahead" rows (mockup 09) bring their own
     padding/border-top rhythm (.win-ahead-row); this generic li margin was
     stacking an extra 3px on top of it only inside the public sample,
     where lifeEventCardEl() is embedded straight into .le-sample-card. */
  .le-sample-card li:not(.win-ahead-row){ margin:3px 0; font-size:.86rem; }
  .paywall{ text-align:center; padding:18px; background:var(--sunk-soft); border:1px dashed var(--line-strong); border-radius:12px; }
  .paywall .big{ font-size:1.05rem; font-weight:700; color:var(--heading); margin-bottom:4px; }
  /* Session-expiry modal: centered icon badge + copy, matches the brand
     gradient used elsewhere (sidenav active state, primary buttons). */
  .session-ended-modal{ text-align:center; animation:signinPop .3s cubic-bezier(.2,.9,.3,1.2); }
  .session-ended-icon{ width:56px; height:56px; margin:4px auto 16px; border-radius:50%;
    background:linear-gradient(135deg,var(--primary),var(--primary-dark)); color:var(--on-primary);
    display:flex; align-items:center; justify-content:center; box-shadow:var(--shadow-md); }
  .session-ended-modal h3{ text-align:center; }
  .session-ended-modal .sub{ text-align:center; }

  /* ---- utilities, last on purpose --------------------------------------- *
   * .hidden used to sit near the top. 75 class rules further down set their
   * own `display`, and a single class beats a single class only on SOURCE
   * ORDER — so `.kptab.hidden`, `.tp-actions.hidden` and 73 others silently
   * ignored it. That is how an admin kept seeing the end-user Key Time Windows
   * tab, and how a NON-admin could see the "Backtest & broker" tab button:
   * the JS was setting .hidden correctly and the CSS was throwing it away.
   *
   * It had been patched twice before by naming individual classes
   * (.ai-suggest-row.hidden, ...). Moving the rule last fixes all of them at
   * once and keeps working for classes added later.
   *
   * Deliberately NOT !important: seven places set element.style.display
   * directly (the tooltip, the right-rail icons, a couple of buttons), and
   * !important would beat those inline styles too.
   * Rules with HIGHER specificity (.navlist :is(button,a)) still win, which is why
   * nav buttons are hidden by inline style instead — see refreshMe(). */
  /* ------------------------------------------------------------------- *
   * Support Portal — a real ticket system for Premium/Advanced, API and
   * desktop-license accounts. Reuses .card/.btn/.abtn for the chrome, and
   * adds only what a helpdesk actually needs: entitlement badges, a
   * status-coded ticket list, and a two-sided message thread.
   * ------------------------------------------------------------------- */
  .sp-head{ margin-bottom:14px; }
  .sp-badges{ display:flex; gap:8px; flex-wrap:wrap; margin-top:12px; }
  .sp-badge{ display:inline-flex; align-items:center; gap:5px; padding:4px 10px;
    border-radius:999px; font-size:.74rem; font-weight:600; background:var(--primary-soft); color:var(--primary); }
  .sp-empty{ text-align:center; padding:48px 20px; color:var(--muted); }
  .sp-empty .sp-empty-ico{ font-size:2.2rem; margin-bottom:10px; display:block; }
  .sp-list-row{ display:flex; align-items:center; gap:14px; padding:14px 16px; border:1px solid var(--line);
    border-radius:12px; background:var(--card); cursor:pointer; margin-bottom:10px; transition:box-shadow .15s,border-color .15s; }
  .sp-list-row:hover{ box-shadow:var(--shadow); border-color:var(--line-strong); }
  .sp-list-main{ flex:1; min-width:0; }
  .sp-list-subject{ font-weight:600; color:var(--text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .sp-list-preview{ font-size:.82rem; color:var(--muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; margin-top:2px; }
  .sp-list-meta{ flex:none; display:flex; flex-direction:column; align-items:flex-end; gap:6px; }
  .sp-list-time{ font-size:.72rem; color:var(--muted); white-space:nowrap; }
  .sp-status{ display:inline-block; padding:3px 10px; border-radius:999px; font-size:.72rem; font-weight:700;
    text-transform:uppercase; letter-spacing:.02em; white-space:nowrap; }
  .sp-status.open{ background:#eaf1ff; color:#1B2A6B; }
  .sp-status.in_progress{ background:#fff4de; color:#9a6a00; }
  .sp-status.resolved{ background:#e6f7ec; color:#12793d; }
  .sp-status.closed{ background:var(--sunk-soft); color:var(--muted); }
  :root:not([data-theme="light"]) .sp-status.open{ background:#22305c; color:#c6d4ff; }
  :root:not([data-theme="light"]) .sp-status.in_progress{ background:#4a3b12; color:#ffd98a; }
  :root:not([data-theme="light"]) .sp-status.resolved{ background:#12331f; color:#8fe3ab; }
  :root[data-theme="dark"] .sp-status.open{ background:#22305c; color:#c6d4ff; }
  :root[data-theme="dark"] .sp-status.in_progress{ background:#4a3b12; color:#ffd98a; }
  :root[data-theme="dark"] .sp-status.resolved{ background:#12331f; color:#8fe3ab; }
  /* Feature Request board's own status vocabulary (reuses .sp-status'
     shape, adds the two labels support tickets don't have). */
  .sp-status.submitted{ background:var(--sunk-soft); color:var(--muted); }
  .sp-status.acknowledged{ background:#eaf1ff; color:#1B2A6B; }
  .sp-status.shipped{ background:#e6f7ec; color:#12793d; }
  .sp-status.declined{ background:#fdeceb; color:var(--red); }
  :root:not([data-theme="light"]) .sp-status.acknowledged{ background:#22305c; color:#c6d4ff; }
  :root:not([data-theme="light"]) .sp-status.shipped{ background:#12331f; color:#8fe3ab; }
  :root:not([data-theme="light"]) .sp-status.declined{ background:#3a1f1d; color:#f3b3ad; }
  :root[data-theme="dark"] .sp-status.acknowledged{ background:#22305c; color:#c6d4ff; }
  :root[data-theme="dark"] .sp-status.shipped{ background:#12331f; color:#8fe3ab; }
  :root[data-theme="dark"] .sp-status.declined{ background:#3a1f1d; color:#f3b3ad; }
  .fr-queue-tag{ display:inline-block; padding:2px 8px; border-radius:999px; font-size:.68rem;
    font-weight:700; text-transform:uppercase; background:var(--primary-soft); color:var(--primary); margin-left:6px; }
  .sp-thread-head{ display:flex; align-items:flex-start; justify-content:space-between; gap:10px; flex-wrap:wrap;
    padding-bottom:14px; margin-bottom:14px; border-bottom:1px solid var(--line); }
  .sp-thread-meta{ display:flex; gap:8px; align-items:center; margin-top:6px; flex-wrap:wrap; }
  .sp-msgs{ display:flex; flex-direction:column; gap:12px; max-height:52vh; overflow-y:auto; padding:4px 2px; }
  .sp-msg{ max-width:78%; padding:10px 14px; border-radius:14px; font-size:.9rem; line-height:1.45; }
  .sp-msg.user{ align-self:flex-end; background:var(--primary); color:var(--on-primary); border-bottom-right-radius:4px; }
  .sp-msg.admin{ align-self:flex-start; background:var(--sunk-soft); color:var(--text); border-bottom-left-radius:4px; }
  .sp-msg-who{ font-size:.68rem; font-weight:700; letter-spacing:.03em; text-transform:uppercase; opacity:.65; margin-bottom:3px; }
  /* white-space:pre-wrap lives HERE, not on .sp-msg -- that container has
     other block children (sp-msg-who, sp-msg-when), and white-space is an
     INHERITED property: set on .sp-msg it also applied to those children,
     which then rendered the raw newlines/indentation from the JS template
     literal BETWEEN the divs as visible blank lines -- discovered live
     (2026-09-02), the actual message text was pushed out of the visible
     bubble by a wall of blank lines from template-literal whitespace. */
  .sp-msg-text{ white-space:pre-wrap; word-break:break-word; }
  .sp-msg-when{ font-size:.66rem; opacity:.55; margin-top:5px; }
  .sp-reply-row{ display:flex; gap:8px; margin-top:14px; align-items:flex-end; }
  .sp-reply-row textarea{ flex:1; resize:vertical; min-height:44px; }
  @media (max-width:640px){
    .sp-list-row{ flex-wrap:wrap; }
    .sp-list-meta{ flex-direction:row; align-items:center; width:100%; justify-content:space-between; }
    .sp-msg{ max-width:92%; }
  }

  /* Identity & Access console. */
  .idn-summary{ display:flex; gap:10px; flex-wrap:wrap; margin-top:14px; }
  .idn-stat{ flex:1; min-width:110px; padding:10px 12px; border:1px solid var(--line);
    border-radius:10px; background:var(--sunk-soft); }
  .idn-stat b{ display:block; font-size:1.35rem; color:var(--heading); font-variant-numeric:tabular-nums; }
  .idn-stat span{ font-size:.74rem; color:var(--muted); }
  /* A count that needs attention is coloured; zero stays neutral, so the
     console is quiet when nothing is wrong rather than permanently amber. */
  .idn-stat.warn b{ color:var(--amber); }
  .idn-stat.bad b{ color:var(--red); }
  .idn-row{ display:flex; align-items:flex-start; gap:14px; padding:12px 0; border-top:1px solid var(--line); flex-wrap:wrap; }
  .idn-row:first-child{ border-top:0; }
  .idn-main{ flex:1; min-width:220px; }
  .idn-name{ font-weight:600; }
  .idn-caps{ font-size:.76rem; color:var(--muted); margin-top:3px; word-break:break-word; }
  .idn-chips{ display:flex; gap:5px; flex-wrap:wrap; margin-top:5px; }
  .idn-chip{ font-size:.7rem; font-weight:600; padding:2px 8px; border-radius:999px;
    background:var(--primary-soft); color:var(--primary); }
  .idn-chip.role{ background:#e6f7ec; color:#12793d; }
  .idn-chip.warn{ background:#fff4de; color:#9a6a00; }
  .idn-chip.bad{ background:#fdeee9; color:var(--accent); }
  :root:not([data-theme="light"]) .idn-chip.role{ background:#12331f; color:#8fe3ab; }
  :root:not([data-theme="light"]) .idn-chip.warn{ background:#4a3b12; color:#ffd98a; }
  :root[data-theme="dark"] .idn-chip.role{ background:#12331f; color:#8fe3ab; }
  :root[data-theme="dark"] .idn-chip.warn{ background:#4a3b12; color:#ffd98a; }
  .idn-side{ flex:none; text-align:right; display:flex; flex-direction:column; gap:6px; align-items:flex-end; }
  .idn-rev{ font-size:.72rem; color:var(--muted); }
  .idn-hist{ font-size:.82rem; padding:8px 0; border-top:1px solid var(--line); display:flex; gap:10px; flex-wrap:wrap; }
  :where(#idn_policy) .u3a-panel{ border-top:1px solid var(--line); }
  :where(#idn_policy) .u3a-sech{ display:flex; align-items:baseline; justify-content:space-between; gap:12px; margin:0 0 12px; flex-wrap:wrap; }
  :where(#idn_policy) .u3a-sech h2{ margin:0; font:600 .72rem/1.2 inherit; letter-spacing:.12em; text-transform:uppercase; }
  :where(#idn_policy) .u3a-help{ display:inline-grid; place-items:center; width:17px; height:17px; border-radius:50%; border:1px solid var(--line); font-size:.65rem; cursor:help; letter-spacing:0; }
  :where(#idn_policy) .u3a-srow{ display:grid; grid-template-columns:minmax(0,1fr) auto; gap:6px 18px; align-items:center; padding:13px 0; border-bottom:1px solid var(--line); }
  :where(#idn_policy) .u3a-srow[hidden]{ display:none; }
  :where(#idn_policy) .u3a-k{ font-weight:600; }
  :where(#idn_policy) .u3a-d{ grid-column:1; font-size:.85rem; }
  :where(#idn_policy) .u3a-srow>.u3a-ctl{ grid-row:1/span 2; grid-column:2; }
  :where(#idn_policy) .u3a-fld{ display:grid; gap:5px; }
  :where(#idn_policy) .u3a-fld>span{ font-size:.72rem; font-weight:600; letter-spacing:.05em; text-transform:uppercase; }
  :where(#idn_policy) .u3a-filters{ display:flex; flex-wrap:wrap; gap:10px 12px; align-items:end; padding:16px 0 14px; border-bottom:1px solid var(--line); }
  :where(#idn_policy) .u3a-filters .u3a-fld{ flex:2 1 220px; }
  :where(#idn_policy) .u3a-sw{ appearance:none; -webkit-appearance:none; width:42px; height:24px; border-radius:999px; background:var(--line); position:relative; cursor:pointer; margin:0; border:0; padding:0; }
  :where(#idn_policy) .u3a-sw::after{ content:""; position:absolute; top:3px; left:3px; width:18px; height:18px; border-radius:50%; background:#fff; box-shadow:0 1px 2px rgba(0,0,0,.2); transition:transform .25s; }
  :where(#idn_policy) .u3a-sw:checked{ background:#2a7a4b; }
  :where(#idn_policy) .u3a-sw:checked::after{ transform:translateX(18px); }
  :where(#idn_policy) .u3a-hist{ list-style:none; margin:10px 0 0; padding:0; font-size:.82rem; }
  :where(#idn_policy) .u3a-hist li{ display:grid; grid-template-columns:112px minmax(0,1fr); gap:10px; padding:7px 0; border-bottom:1px solid var(--line); }
  :where(#idn_policy) .u3a-hist li span:first-child{ }
  @media (max-width:640px){ .idn-side{ width:100%; text-align:left; align-items:flex-start; } }

  /* "Moved to Reports" banner (Phase 5, U5, plan section 1 option B): shown
     at the top of a soft-retired panel's still-live cards, once that
     panel's 5a flag (GET /api/admin/reports/retired-panels) is set. Copied
     from docs/redesign-mockup/reports_p5_mock.html's .moved (its own tokens
     are the same names as the app's -- primary/primary-soft/line-strong/
     heading -- so no new tokens are needed). Informational, not a warning:
     no data was deleted and every store keeps being written. */
  .moved{ display:flex; gap:10px; align-items:center; flex-wrap:wrap; border:1px solid var(--line-strong);
    border-left:3px solid var(--primary); background:var(--primary-soft); border-radius:10px;
    padding:8px 12px; margin:10px 0 12px; font-size:13px; }
  .moved b{ color:var(--heading); }
  .moved .sp{ flex:1; }

  /* Staff roles in User Lookup. Scope checkboxes dim (not vanish) until the
     role is granted — a picker that disappears reads as a rendering bug,
     whereas a dimmed one shows what granting the role would let you narrow. */
  .ulk-role{ padding:10px 0; border-top:1px solid var(--line); }
  .ulk-role:first-child{ border-top:0; }
  .ulk-role-head{ display:flex; align-items:baseline; gap:8px; flex-wrap:wrap; }
  .ulk-role-on{ display:inline-flex; align-items:center; gap:6px; cursor:pointer; }
  .ulk-scopes{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin:6px 0 0 22px; }
  .ulk-scope{ display:inline-flex; align-items:center; gap:4px; font-size:.8rem; cursor:pointer; }
  .ulk-scope.off{ opacity:.45; cursor:default; }

  /* ==== Redesign 2026-09 ===================================================
     One visual language for the whole app: hairline sheets instead of
     shadows, state carried by a 3px underline PLUS a word (never colour
     alone), serif for titles and statements, mono for KP notation. Written
     as a final layer so every tool inherits it without a layout change; it
     sits ABOVE .hidden so .hidden still wins on order, and no rule here gives
     `display` to an element that can carry .hidden. Colours only ever come
     from the tokens in :root and its two dark blocks. */

  /* -- shared type & helpers -- */
  body{ font-family:var(--sans); }
  .ds-eyebrow{ font-size:.68rem; font-weight:700; letter-spacing:.09em; text-transform:uppercase;
    color:var(--faint-text); margin:0 0 6px; }
  .ds-sec{ display:flex; align-items:baseline; justify-content:space-between; gap:12px; margin:26px 0 10px; }
  .ds-sec h2{ margin:0; font-size:.74rem; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--heading); }
  .ds-sec > span{ font-size:.76rem; color:var(--faint-text); text-align:right; }
  .ds-pill{ display:inline-flex; align-items:center; gap:5px; font-size:.72rem; font-weight:700; padding:2px 9px;
    border-radius:99px; border:1px solid var(--line-strong); color:var(--muted); background:var(--card); white-space:nowrap; }
  .ds-pill.yes{ border-color:var(--green); color:var(--dig-good); }
  .ds-pill.mid{ border-color:var(--gold); color:var(--gold-ink); }
  .ds-pill.no{ border-color:var(--red); color:var(--dig-bad); }

  /* -- sidebar -- */
  .sidenav{ width:232px; padding:18px 14px 14px; }
  .sidenav .brand{ padding:4px 8px 18px; margin-bottom:10px; border-bottom:1px solid var(--line); }
  .sidenav .brand img{ width:30px; height:30px; }
  .sidenav .brand h1{ font-family:var(--serif); font-weight:600; font-size:1.02rem; letter-spacing:0; line-height:1.15; color:var(--heading); }
  .sidenav .brand p{ color:var(--faint-text); letter-spacing:.02em; }
  /* Nav numbers are a CSS counter, not markup: a counter skips display:none,
     so a feature-flagged item that is hidden never leaves a gap in 00, 01… */
  .navlist{ gap:1px; margin-top:0; counter-reset:navnum -1; }
  .navlist :is(button,a){ padding:7px 10px; border:none; border-radius:8px; font-weight:500; font-size:.86rem;
    color:var(--muted); counter-increment:navnum; }
  .navlist :is(button,a):hover{ background:var(--sheet); color:var(--text); transform:none; }
  .navlist :is(button,a).active{ background:var(--sheet); color:var(--text); box-shadow:inset 2px 0 0 var(--accent); }
  .navlist :is(button,a).active::before{ content:none; }
  .navlist .navicon{ width:18px; text-align:left; font-family:var(--mono); font-size:.64rem; color:var(--faint-text); }
  .navlist .navicon svg{ display:none; }
  .navlist .navicon::after{ content:counter(navnum, decimal-leading-zero); }
  .navlist :is(button,a).active .navicon{ color:var(--accent); }
  .navgroup-label{ font-size:.62rem; font-weight:700; letter-spacing:.09em; color:var(--faint-text); padding:14px 10px 4px; margin:0; }
  .sidebar-trust{ color:var(--faint-text); border-top:1px solid var(--line); padding:12px 10px 0; font-size:.7rem; line-height:1.5; }
  .sidenav-settings{ border:none; border-radius:8px; padding:7px 10px; margin-top:4px; font-weight:500; font-size:.86rem; }
  .sidenav-settings:hover{ background:var(--sheet); color:var(--text); }
  .sidenav .sidefoot{ color:var(--faint-text); padding:6px 10px 0; }
  @media (min-width:641px){
    /* The collapsed icon rail keeps its icons: a bare "03" means nothing without its label. */
    body.sidenav-collapsed .navlist .navicon svg{ display:inline-block; }
    body.sidenav-collapsed .navlist .navicon::after{ content:none; }
    body.sidenav-collapsed .navlist .navicon{ width:20px; text-align:center; color:inherit; }
    body.sidenav-collapsed .sidenav .brand{ border-bottom:none; }
    body.sidenav-collapsed .sidenav:hover .navlist .navicon svg{ display:none; }
    body.sidenav-collapsed .sidenav:hover .navlist .navicon::after{ content:counter(navnum, decimal-leading-zero); }
    body.sidenav-collapsed .sidenav:hover .navlist .navicon{ width:18px; text-align:left; color:var(--faint-text); }
    body.sidenav-collapsed .sidenav:hover .navlist :is(button,a).active .navicon{ color:var(--accent); }
    body.sidenav-collapsed .sidenav:hover .navlist :is(button,a){ padding:7px 10px; }
    .sidenav .navchips{ display:none; }
  }
  /* Tablet keeps the sidebar (the drawer is for phones only, as in the approved
     layout); the older 860px drawer rules are undone here for 641–860px. */
  @media (min-width:641px) and (max-width:860px){
    .app-shell{ flex-direction:row; }
    .topbar .navtoggle{ display:none; }
    .sidenav{ position:sticky; top:0; height:100vh; transform:none; box-shadow:none; z-index:auto; }
    body.nav-open .nav-backdrop{ display:none; }
  }
  /* Drawer chips (phones): Live Agent, theme and language are MOVED here by
     placeTopChips() in app.js, so their ids and handlers stay unique. */
  .navchips{ display:flex; flex-wrap:wrap; gap:8px; padding:12px 10px 4px; margin-top:10px; border-top:1px solid var(--line); }
  .navchips > button, .navchips > select{ height:32px; width:auto; padding:0 12px; border:1px solid var(--line-strong);
    border-radius:99px; background:var(--card); color:var(--muted); font:500 .8rem var(--sans); cursor:pointer; margin:0; }
  .navchips > .themetoggle{ width:32px; padding:0; }

  /* -- top bar -- */
  .topbar .topbar-inner{ padding:12px 28px; gap:14px; }
  .topbar .homebtn{ display:none; }
  .crumb{ font-size:.78rem; color:var(--faint-text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0; }
  .crumb b{ color:var(--text); font-weight:600; }
  .topbar .topbar-right{ gap:8px; }
  .topbar .liveagent, .topbar .acctbar .abtn.ghost, .topbar .langswitch{ height:30px; width:auto; padding:0 11px;
    border:1px solid var(--line-strong); border-radius:99px; background:var(--card); color:var(--muted);
    font-size:.78rem; font-weight:500; }
  .topbar .themetoggle{ width:30px; height:30px; border:1px solid var(--line-strong); border-radius:99px; background:var(--card); }
  .topbar .liveagent:hover, .topbar .acctbar .abtn.ghost:hover, .topbar .langswitch:hover, .topbar .themetoggle:hover{
    border-color:var(--primary); color:var(--primary); background:var(--card); }
  .topbar .acctbar .abtn.solid{ height:34px; border-radius:9px; padding:0 16px; font-size:.84rem; }

  /* -- shared components: every tool inherits these -- */
  .card{ box-shadow:none; border-radius:14px; }
  .card h3{ font-family:var(--serif); font-weight:700; font-size:1.12rem; letter-spacing:0; color:var(--text); }
  .btn{ background:var(--primary); color:var(--on-primary); border:1px solid var(--primary); border-radius:9px; font-size:.88rem; }
  .btn:hover{ background:var(--primary-hover); border-color:var(--primary-hover); }
  .btn:active{ background:var(--primary-hover); box-shadow:none; }
  .btn:disabled{ background:var(--sunk-soft); color:var(--muted); border-color:var(--line); }
  .ds-outline{ background:transparent; color:var(--primary); border:1px solid var(--primary); }
  .ds-outline:hover{ background:var(--sheet); color:var(--primary); border-color:var(--primary); }
  .abtn{ border-radius:9px; color:var(--text); }
  .abtn:hover{ border-color:var(--primary); background:var(--card); color:var(--primary); }
  .abtn.solid{ color:var(--on-primary); }
  .abtn.solid:hover{ background:var(--primary-hover); border-color:var(--primary-hover); color:var(--on-primary); }
  .abtn.ghost{ color:var(--muted); }
  .kptabs{ gap:4px; }
  .kptab{ height:auto; padding:9px 12px; margin-bottom:-1px; border-radius:0; color:var(--muted); }
  .kptab:hover{ background:none; color:var(--text); }
  .kptab.active{ color:var(--text); border-bottom-color:var(--accent); }
  .kpsub > .kptabs .kptab{ margin-bottom:0; background:var(--card); border:1px solid var(--line); border-radius:99px; color:var(--muted); }
  .kpsub > .kptabs .kptab:hover{ background:var(--sheet); color:var(--text); }
  .kpsub > .kptabs .kptab.active{ background:var(--sheet); border-color:var(--line-strong); color:var(--text); }
  th{ background:transparent; color:var(--faint-text); font-size:.7rem; font-weight:700; letter-spacing:.06em;
    text-transform:uppercase; border-bottom:1px solid var(--line-strong); }
  details.cardfold > summary.cardfold-head{ align-items:center; }
  details.cardfold > summary.cardfold-head::before{ content:none; }
  details.cardfold > summary.cardfold-head::after{ content:"+"; margin-left:auto; font-family:var(--mono); color:var(--faint-text); }
  details.cardfold[open] > summary.cardfold-head::after{ content:"\2212"; }
  details.cardfold > summary.cardfold-head h3{ font-family:var(--sans); font-size:.9rem; font-weight:600; color:var(--text); }
  .cardfold-hint{ font-size:.76rem; color:var(--faint-text); }
  tbody tr:hover, table tr:hover{ background:var(--sheet); }
  /* Fields: hairline, a touch rounder, the same focus ring everywhere. */
  input, textarea, select{ border-radius:10px; }
  input:focus, textarea:focus, select:focus{ border-color:var(--primary); box-shadow:0 0 0 3px var(--primary-soft); }
  /* Chips are outline pills; state is the border and the word, not a fill. */
  .chip{ background:var(--card); color:var(--muted); border:1px solid var(--line-strong); border-radius:99px; font-weight:600; font-size:.76rem; }
  .chip.ok{ background:var(--card); color:var(--dig-good); border-color:var(--green); }
  .chip.warn{ background:var(--card); color:var(--gold-ink); border-color:var(--gold); }
  .notice{ background:var(--sheet); border:1px solid var(--line); border-radius:12px; color:var(--text); }
  .notice.warn{ background:var(--sheet); border:1px solid var(--line); border-left:3px solid var(--gold); }
  .help-toggle{ font-size:.8rem; color:var(--primary); }
  .help-body{ background:var(--sheet); border:1px solid var(--line); border-radius:12px; color:var(--text); }
  .help-body h4{ font-size:.7rem; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--heading); }
  /* Modals: a hairline sheet over a softened page, not a heavy floating card. */
  .modal-bg{ background:rgba(20,22,34,.42); }
  @supports ((-webkit-backdrop-filter:blur(2px)) or (backdrop-filter:blur(2px))){
    .modal-bg{ -webkit-backdrop-filter:blur(3px); backdrop-filter:blur(3px); }
  }
  .modal{ border:1px solid var(--line); border-radius:16px; box-shadow:0 12px 40px rgba(20,22,34,.18); }
  .modal h3{ font-family:var(--serif); font-weight:600; font-size:1.3rem; line-height:1.2; color:var(--text); text-wrap:balance; }
  .modal p.sub{ color:var(--muted); }
  .modal .mclose{ width:30px; height:30px; margin:-4px -6px 0 8px; border:1px solid var(--line); border-radius:99px;
    font:400 1.05rem/1 var(--sans); color:var(--faint-text); }
  .modal .mclose:hover{ border-color:var(--line-strong); color:var(--text); }
  body.signin-page #loginModal .modal{ border:1px solid var(--line); box-shadow:0 12px 40px rgba(20,22,34,.10); }
  /* The brand panel is the brand navy in both themes: --primary turns pale
     lavender in dark, which washed the gradient out to near-white. */
  body.signin-page .signin-side{ background:radial-gradient(120% 140% at 15% 0%, #26399c 0%, #1B2A6B 45%, #141f52 100%); }
  .signin-side h2{ font-family:var(--serif); font-weight:600; font-size:1.7rem; letter-spacing:0; text-wrap:balance; }
  .signin-side-brand b, body.signin-page .signin-brand b{ font-family:var(--serif); font-weight:600; letter-spacing:0; }
  .pricing-modal-foot{ background:var(--sheet); }
  /* A field + button row (search, lookup, add): wide field, compact button,
     as in the approved ask box, instead of the button taking half the row. */
  @media (min-width:641px){ .row > input + .btn, .row > .btn:last-child:not(:first-child){ flex:0 0 auto; min-width:120px; } }
  /* Tabs scroll sideways on a phone rather than wrapping into a block. Some
     strips (Analytics, Pricing) set flex-wrap inline, which only !important
     can override without editing their markup. */
  @media (max-width:640px){
    .kptabs{ flex-wrap:nowrap !important; overflow-x:auto; -webkit-overflow-scrolling:touch; scrollbar-width:none; }
    .kptabs > .kptab{ flex:0 0 auto; white-space:nowrap; }
  }
  /* A primary button sharing a row with secondary ones sits on their line. */
  .modal div:has(> .btn):has(> .abtn){ align-items:center; }
  .modal div:has(> .btn):has(> .abtn) > .btn{ margin-top:0; }
  @media (max-width:640px){
    /* A wide table scrolls inside its own card, never the page. */
    .card > table, .cardfold-body > table, .modal table{ display:block; max-width:100%; overflow-x:auto; -webkit-overflow-scrolling:touch; }
    .modal .btn, .modal .row .btn{ width:100%; }
  }

  /* -- Home -- */
  #panel-home .askhero{ padding:26px 28px; border:1px solid var(--line); border-radius:16px;
    background:linear-gradient(0deg,var(--card),var(--sheet)); }
  #panel-home .askhero h3{ font-family:var(--serif); font-weight:600; font-size:2rem; line-height:1.15; color:var(--text); text-wrap:balance; }
  #panel-home .askhero .sub{ color:var(--muted); max-width:62ch; margin-bottom:18px; }
  .home-greeting{ color:var(--gold-ink); }
  .ask-row{ gap:10px; margin-top:6px; }
  .ask-row .ask-field{ flex:1; min-width:0; }
  .ask-row #ask_q{ height:48px; padding:0 16px; font-size:1rem; border-radius:11px; }
  .ask-row .btn{ margin-top:0; height:48px; min-width:0; padding:0 22px; font-size:.92rem; }
  /* Wide question field, compact button (the approved layout). */
  @media (min-width:641px){ .ask-row > .ask-field{ flex:1 1 auto; } .ask-row > .btn{ flex:0 0 auto; width:auto; } }
  .ask-examples{ display:flex; flex-wrap:wrap; gap:8px; margin-top:12px; }
  .ask-examples > button{ font:.78rem var(--sans); color:var(--muted); background:none; border:1px dashed var(--line-strong);
    border-radius:99px; padding:3px 10px; cursor:pointer; }
  .ask-examples > button:hover{ color:var(--primary); border-color:var(--primary); }
  #home_personal{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); column-gap:10px; }
  /* .hsg-list spans both columns for the same reason .hw-grid does: it is a
     LIST of full-width rows, not a tile that pairs with another tile. Left
     in one column it renders a narrow strip beside empty space, and each
     row's reason line — the part worth reading — wraps to three lines. */
  #home_personal > .home-rp{ grid-column:1/-1; }
  .home-rp{ margin:0 0 10px; padding:12px 16px; border-radius:12px; border:1px solid var(--line); background:var(--card); }
  .home-rp .hrp-eyebrow{ margin:0; font-size:.74rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--heading); }
  .home-rp .hrp-line{ margin:6px 0 8px; font-size:1.05rem; font-weight:700; color:var(--text); }
  .home-rp .hrp-links{ display:flex; flex-wrap:wrap; align-items:center; gap:12px; font-size:.84rem; }
  .home-rp .hrp-links button{ background:none; border:0; padding:0; color:var(--primary); cursor:pointer; font:inherit; text-decoration:underline; }
  .home-rp .hrp-links a{ color:var(--primary); }
  .home-rp .hrp-edit{ display:flex; gap:8px; align-items:center; margin-top:8px; }
  .home-rp .hrp-edit input{ max-width:220px; margin:0; }
  .home-rp .hrp-status{ margin:6px 0 0; font-size:.8rem; color:var(--muted); }
  #home_personal > .ds-sec, #home_personal > .mute-offer,
  #home_personal > .home-sec:has(.hw-grid), #home_personal > .home-sec:has(.hsg-list){ grid-column:1/-1; }
  #home_personal .home-sec{ margin:0 0 10px; padding:14px 16px; border-radius:12px; }
  #home_personal .home-sec .hs-head h3{ font-family:var(--sans); font-size:.9rem; }
  .hw-grid{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:10px; }
  .hw-grid .hw-row, .hw-grid .hw-row:first-child{ position:relative; padding:12px 14px 15px; border:1px solid var(--line);
    border-radius:12px; background:var(--card); }
  .hw-grid .hw-stripe{ position:absolute; left:-1px; right:-1px; bottom:-1px; width:auto; height:3px; border-radius:0 0 12px 12px; }
  .home-track .htr-big{ font-family:var(--serif); font-weight:600; }
  .home-resume{ border-radius:12px; }
  .home-resume:hover{ transform:none; }
  .alltools-card{ background:none; border:none; box-shadow:none; padding:0; }
  .alltools-toggle{ margin:26px 0 10px; }
  .alltools-toggle > span:first-child{ display:flex; align-items:baseline; justify-content:space-between; gap:12px; }
  .alltools-toggle .at-title{ font-size:.74rem; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--heading); }
  .alltools-toggle .at-sub{ margin:0; font-size:.76rem; color:var(--faint-text); text-align:right; }
  .hometile-grid{ grid-template-columns:repeat(4,minmax(0,1fr)); gap:10px; margin-top:0; counter-reset:tool; }
  .hometile{ gap:3px; min-height:112px; padding:14px 14px 12px; background:var(--card); border:1px solid var(--line);
    border-bottom:3px solid var(--line-strong); border-radius:12px; box-shadow:none; counter-increment:tool; }
  .hometile::before{ content:attr(data-num); font-family:var(--mono); font-size:.66rem; letter-spacing:.04em; color:var(--faint-text); }
  .hometile:hover{ transform:none; box-shadow:none; border-color:var(--line-strong); }
  .hometile .ht-ico{ display:none; }
  .hometile .ht-label{ font-size:.95rem; font-weight:600; color:var(--text); }
  .hometile .ht-desc{ display:block; font-size:.78rem; line-height:1.4; color:var(--muted); }
  .hometile.ht-feature{ background:var(--sheet); border-bottom-color:var(--accent); }
  .hometile.ht-feature:hover{ border-bottom-color:var(--accent); }
  .hometile.ht-feature .ht-label{ font-family:var(--serif); font-size:1.25rem; }
  #hometile-horary, #hometile-horary:hover{ border-bottom-color:var(--primary); }
  .hometile.ht-gold, .hometile.ht-gold:hover{ border-bottom-color:var(--gold); }

  /* -- tablet (≤1024px) -- */
  @media (max-width:1024px){
    .hometile-grid{ grid-template-columns:repeat(2,minmax(0,1fr)); }
    .hometile.ht-feature{ grid-column:span 2; }
    .hw-grid{ grid-template-columns:repeat(2,minmax(0,1fr)); }
  }

  /* -- phones (≤640px) -- */
  .mobnav{ display:none; }
  @media (max-width:640px){
    .topbar .topbar-inner{ padding:10px 14px; gap:8px; }
    .topbar .navtoggle{ width:34px; height:34px; border-radius:9px; }
    .crumb{ flex:1 1 0; font-size:.74rem; }
    .topbar .acctbar .abtn.ghost{ display:none; }
    /* Admin Panel folds to a short "Admin" pill. It must out-rank the solid
       top-bar button rule above, which otherwise restored the full label
       inside a 36px box and overlapped the old wrench emoji. */
    .topbar .acctbar .abtn.solid.adminpanel-btn{ font-size:0; gap:0; width:auto; min-width:0; height:34px; padding:0 11px; border-radius:99px; }
    .topbar .acctbar .abtn.solid.adminpanel-btn::before{ content:"Admin"; font:600 .76rem var(--sans); }
    #panel-home .askhero{ padding:18px 16px; border-radius:14px; }
    #panel-home .askhero h3{ font-size:1.55rem; }
    .ask-row{ flex-direction:column; }
    .ask-row .btn{ width:100%; height:44px; }
    .ask-examples{ flex-wrap:nowrap; overflow-x:auto; padding-bottom:4px; scrollbar-width:none; }
    .ask-examples > button{ white-space:nowrap; flex:none; }
    #home_personal{ grid-template-columns:minmax(0,1fr); }
    .hw-grid{ grid-template-columns:minmax(0,1fr); }
    .ds-sec > span{ display:none; }
    .alltools-toggle .at-sub{ display:none; }
    .hometile-grid{ grid-template-columns:repeat(2,minmax(0,1fr)); gap:8px; }
    .hometile{ min-height:92px; padding:12px; }
    .hometile.ht-feature{ grid-column:span 2; }
    .hometile .ht-desc{ display:none; }
    .hometile.ht-feature .ht-desc{ display:block; }
    /* Fixed bottom bar: four ways in; "More" opens the existing nav drawer. */
    .mobnav{ display:grid; grid-template-columns:repeat(4,1fr); position:fixed; left:0; right:0; bottom:0; z-index:1200;
      background:var(--card); border-top:1px solid var(--line); padding:6px 4px calc(8px + env(safe-area-inset-bottom)); }
    .mobnav > button{ display:flex; flex-direction:column; align-items:center; gap:2px; padding:4px 0; margin:0; width:auto;
      background:none; border:none; font:600 .66rem var(--sans); color:var(--muted); cursor:pointer; }
    .mobnav > button i{ font-style:normal; font-family:var(--mono); font-size:.62rem; color:var(--faint-text); }
    .mobnav > button.on{ color:var(--text); }
    .mobnav > button.on i{ color:var(--accent); }
    body.nav-open .mobnav, body.signin-page .mobnav, body:has(.modal-bg.show) .mobnav{ display:none; }
    .main-col{ padding-bottom:72px; }
    .faqchat{ bottom:80px; }
  }
  /* A disabled solid button keeps readable text on its pale ground. */
  .abtn:disabled, .abtn.solid:disabled{ background:var(--card); color:var(--muted); border-color:var(--line-strong); }
  /* Drawer chips beat .abtn.ghost's borderless reset. */
  .navchips > .abtn.ghost{ border:1px solid var(--line-strong); background:var(--card); color:var(--muted); }
  @media (max-width:1024px){ .crumb-site, .crumb-site + .crumb-sep{ display:none; } }
  @media (max-width:640px){ .ask-row > .ask-field, .ask-row > .btn{ flex:none; } }

  /* -- Birth Chart result --------------------------------------------------
     Only once a chart exists (#panel-birth.bx-on, set by runBirth). Both
     existing columns and the three result containers become display:contents,
     so their children join ONE grid and `order` lays them out top to bottom:
     header, tally, chart + running period, predictions, AI prompt, reference.
     No container moves in the DOM, so nothing that fills them has to change. */
  .bx-ai-h, .aicta-lead{ display:none; }
  #panel-birth.bx-on .chart-2col{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); column-gap:10px; row-gap:0; align-items:start; }
  #panel-birth.bx-on .chart-2col-left, #panel-birth.bx-on .chart-2col-right,
  #panel-birth.bx-on #b_out, #panel-birth.bx-on #b_tables_out, #panel-birth.bx-on #b_av_out{ display:contents; }
  #panel-birth.bx-on :is(#b_entry_card, .bx-ai-h, #b_ai_title, #b_ai_card, #b_ppm_out, .bx-head, .bx-tally, .bx-sec, .bx-two, .bx-pred){ grid-column:1/-1; }
  #panel-birth.bx-on #b_entry_card{ order:0; }
  #panel-birth.bx-on #b_entry_card.entry-collapsed{ display:none; }
  #panel-birth.bx-on .bx-head{ order:1; }
  #panel-birth.bx-on .bx-tally{ order:2; }
  #panel-birth.bx-on .bx-chart-h{ order:3; }
  #panel-birth.bx-on .bx-two{ order:4; }
  #panel-birth.bx-on .bx-pred{ order:5; }
  #panel-birth.bx-on .bx-ai-h{ order:6; }
  #panel-birth.bx-on #b_ai_title{ order:6; }
  #panel-birth.bx-on #b_ai_card{ order:7; }
  #panel-birth.bx-on .bx-ref-h{ order:8; }
  #panel-birth.bx-on #b_out > .bx-ref{ order:9; }
  #panel-birth.bx-on #b_tables_out > *{ order:10; }
  #panel-birth.bx-on #b_av_out > *{ order:11; }
  #panel-birth.bx-on #b_ppm_out{ order:12; }
  #panel-birth.bx-on :is(#b_out > .bx-ref, #b_tables_out > *, #b_av_out > *){ margin:0 0 10px; }
  #panel-birth.bx-on .bx-ai-h:has(+ #b_ai_card:not(.hidden)){ display:flex; }
  #panel-birth.bx-on .aicta-lead{ display:block; margin:0 0 12px; }
  .aicta-q{ margin:0; font-family:var(--serif); font-size:1.15rem; color:var(--text); }
  .aicta-sub{ margin:4px 0 0; font-size:.88rem; color:var(--muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  @media (max-width:640px){ .aicta-sub{ white-space:normal; overflow:visible; text-overflow:clip; } }
  .bx-ai-title{ margin:18px 0 10px; color:var(--heading); font-size:1.05rem; font-weight:700; }
  .bx-ai-title .premium-tag{ vertical-align:middle; }

  .bx-head{ display:grid; grid-template-columns:minmax(0,1fr) auto; gap:18px; align-items:end;
    padding-bottom:18px; border-bottom:1px solid var(--line); }
  .bx-head h2{ margin:0 0 6px; font-family:var(--serif); font-weight:600; font-size:2rem; line-height:1.15; color:var(--text); text-wrap:balance; }
  .bx-meta{ font-family:var(--mono); font-size:.78rem; color:var(--muted); overflow-wrap:anywhere; }
  .bx-actions{ display:flex; gap:8px; align-items:center; }
  .bx-actions .btn{ margin-top:0; min-width:0; height:36px; padding:0 16px; }
  .bx-export{ position:relative; }
  .bx-export > summary{ list-style:none; }
  .bx-export > summary::-webkit-details-marker{ display:none; }
  .bx-export-menu{ position:absolute; right:0; top:calc(100% + 6px); z-index:60; display:flex; flex-direction:column;
    min-width:150px; padding:4px; background:var(--card); border:1px solid var(--line-strong); border-radius:10px; }
  .bx-export-menu > button{ padding:8px 10px; text-align:left; background:none; border:none; border-radius:7px;
    font:500 .84rem var(--sans); color:var(--text); cursor:pointer; }
  .bx-export-menu > button:hover{ background:var(--sheet); }
  .bx-tally{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:10px; margin-top:18px; }
  .bx-tile{ padding:12px 16px; background:var(--card); border:1px solid var(--line); border-bottom:3px solid var(--line-strong); border-radius:12px; }
  .bx-tile b{ display:block; font-family:var(--serif); font-weight:500; font-size:1.9rem; line-height:1; font-variant-numeric:tabular-nums; color:var(--text); }
  .bx-tile span{ display:block; margin-top:4px; font-size:.72rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--muted); }
  .bx-tile.yes{ border-bottom-color:var(--green); }
  .bx-tile.mid{ border-bottom-color:var(--gold); }
  .bx-tile.no{ border-bottom-color:var(--red); }
  .bx-two{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1.15fr); gap:16px; align-items:start; }
  .bx-two > .card, .bx-two > .bx-run{ margin:0; }
  .bx-two .card-head{ display:none; }
  .bx-run{ padding:16px; }
  .bx-chain{ display:flex; flex-wrap:wrap; gap:6px; align-items:center; margin:4px 0 6px; }
  .bx-lv{ padding:4px 10px; font-family:var(--mono); font-size:.8rem; color:var(--text); background:var(--sheet); border:1px solid var(--line); border-radius:8px; }
  .bx-lv small{ display:block; font-family:var(--sans); font-size:.6rem; letter-spacing:.06em; text-transform:uppercase; color:var(--faint-text); }
  .bx-arrow{ color:var(--faint-text); }
  .bx-span{ margin:4px 0 6px; font-family:var(--mono); font-size:.8rem; color:var(--muted); }
  .bx-track{ position:relative; height:44px; margin:18px 2px 0; }
  .bx-rail{ position:absolute; left:0; right:0; top:18px; height:10px; background:var(--sunk-soft); border:1px solid var(--line); border-radius:4px; }
  .bx-md{ position:absolute; top:12px; height:22px; display:flex; align-items:center; justify-content:center; overflow:hidden; white-space:nowrap;
    font:600 .62rem var(--mono); color:var(--muted); background:var(--card); border:1px solid var(--line-strong); border-radius:5px; }
  .bx-md.now{ background:var(--accent); border-color:var(--accent); color:var(--on-primary); }
  .bx-today{ position:absolute; top:2px; bottom:0; width:2px; background:var(--text); }
  .bx-today span{ position:absolute; top:-12px; left:5px; white-space:nowrap; font:700 .58rem var(--sans); letter-spacing:.05em; text-transform:uppercase; color:var(--text); }
  .bx-axis{ display:flex; justify-content:space-between; margin:2px 2px 0; font-size:.66rem; font-variant-numeric:tabular-nums; color:var(--faint-text); }
  .bx-note{ margin:14px 0 0; padding-top:12px; border-top:1px solid var(--line); font-family:var(--serif); font-size:1.02rem; line-height:1.5; color:var(--text); }
  #panel-birth .bx-pred{ background:none; border:none; padding:0; margin:0; }
  #panel-birth .bx-pred > summary.cardfold-head{ margin:26px 0 10px; }
  #panel-birth .bx-pred > summary.cardfold-head h3{ font-family:var(--sans); font-size:.74rem; font-weight:700; letter-spacing:.08em;
    text-transform:uppercase; color:var(--heading); }
  #panel-birth .bx-pred > .cardfold-body{ margin-top:0; padding:0 16px 10px; background:var(--card); border:1px solid var(--line); border-radius:14px; }
  #panel-birth .bx-pred > .cardfold-body > .kptabs{ margin:0 -16px 12px; padding:10px 12px 0; }
  .bx-domtable, .bx-domtable > tbody{ display:block; }
  .bx-domtable tr:has(> th){ display:none; }
  .bx-domtable tr.pexpand{ display:grid; grid-template-columns:minmax(0,190px) auto minmax(0,1fr) auto; gap:14px; align-items:center;
    padding:11px 0; border-top:1px solid var(--line); cursor:pointer; }
  .bx-domtable tr.pdetail:not(.hidden){ display:block; }
  .bx-domtable td{ padding:0; border:none; }
  .bx-domtable td.pt{ font-size:.88rem; font-weight:600; }
  .bx-domtable .ptwist, .bx-domtable .pw-all{ display:none; }
  .bx-domtable td.verdict{ justify-self:start; padding:2px 9px; white-space:nowrap; font-size:.72rem; font-weight:700;
    color:var(--gold-ink); background:var(--card); border:1px solid var(--gold); border-radius:99px; }
  .bx-domtable td.verdict.v-promised{ color:var(--dig-good); border-color:var(--green); }
  .bx-domtable td.verdict.v-not{ color:var(--dig-bad); border-color:var(--red); }
  .bx-domtable td.windows{ order:3; font-family:var(--mono); font-size:.74rem; color:var(--muted); }
  .bx-domtable td.ptrk{ order:4; }
  .pw-next em{ font-style:normal; font-weight:600; color:var(--primary); }
  /* The KP counts + "next good period" line: a quiet sheet under the tabs,
     not a tinted banner competing with the tally tiles above. */
  .pred-headline{ margin:0 0 12px; padding:10px 14px; background:var(--sheet); border:1px solid var(--line); border-radius:12px; box-shadow:none; }
  .pred-headline .ph-counts{ font-size:.9rem; color:var(--text); }
  .pred-headline .ph-next{ margin-top:3px; font-size:.8rem; }
  /* U2B F1: the shared TimingFacts.rows periods list (KP detail, mockup
     01 A "Your timeline" / 07 A "<Domain> · periods"). */
  .tw-rows{ list-style:none; margin:10px 0 0; padding:0; }
  .tw-item{ display:flex; align-items:center; gap:10px; padding:9px 0; border-top:1px solid var(--line); }
  .tw-item:first-child{ border-top:none; }
  .tw-n{ flex:none; width:22px; height:22px; display:flex; align-items:center; justify-content:center;
    border-radius:50%; background:var(--sheet); font-size:.72rem; font-weight:700; color:var(--muted); }
  .tw-main{ flex:1 1 auto; min-width:0; }
  .tw-main b{ display:block; font-size:.88rem; }
  .tw-main span{ display:block; font-size:.76rem; color:var(--muted); margin-top:2px; }
  .tw-reliability{ margin-top:8px; font-size:.76rem; }
  .tw-empty{ margin-top:10px; }
  /* U2B F2 parity spec section 8: the word chip becomes a FILLED soft pill
     (tw-* and tw-*-soft tokens, 2.1/2.2), matching mockups 07 A/09's own filled
     chips -- closing an F1 parity gap rather than opening one. "Likely"
     changes from navy (--heading) to the mockup's own blue (--tw-likely).
     The chip/dot rules themselves are defined once, in the [A] answer-card
     block above (2.2) -- F1's twChip/twSummary/twRowList and F3b's Life
     Events rows share those same classes, no separate copy here. */
  @media (max-width:1024px){
    .bx-two{ grid-template-columns:minmax(0,1fr); }
    .bx-domtable tr.pexpand{ grid-template-columns:minmax(0,1fr) auto auto; row-gap:4px; }
    .bx-domtable td.ptrk{ order:3; }
    .bx-domtable td.windows{ order:4; grid-column:1/-1; }
  }
  @media (max-width:640px){
    #panel-birth.bx-on .chart-2col{ grid-template-columns:minmax(0,1fr); }
    .bx-head{ grid-template-columns:minmax(0,1fr); }
    .bx-head h2{ font-size:1.55rem; }
    .bx-meta{ font-size:.72rem; }
    .bx-actions > *{ flex:1 1 0; min-width:0; }
    .bx-actions .btn{ width:100%; height:auto; min-height:40px; padding:6px 8px; font-size:.8rem; }
    .bx-tally{ gap:8px; }
    .bx-tile{ padding:10px 12px; }
    .bx-tile b{ font-size:1.5rem; }
    .bx-tile span{ font-size:.62rem; }
    .bx-note{ font-size:.95rem; }
    /* Domain rows: name + verdict on top, timing (and Track) underneath. */
    .bx-domtable tr.pexpand{ grid-template-columns:minmax(0,1fr) auto; column-gap:10px; }
    .bx-domtable td.verdict{ justify-self:end; }
    .bx-domtable td.windows{ order:4; grid-column:1; }
    .bx-domtable td.ptrk{ order:5; grid-column:2; justify-self:end; }
  }

  /* -- Tool results: Horary, Compatibility, Panchang (redesign phase 3) ----
     Approved-style layouts from docs/redesign-mockup/tools_mock.html. Shared
     head (.tr-head) with the Birth result; verdict and score bands carry
     their state as a 3px underline plus a word, never colour alone. */
  .tr-head{ display:grid; grid-template-columns:minmax(0,1fr) auto; gap:18px; align-items:end;
    margin:6px 0 14px; padding-bottom:16px; border-bottom:1px solid var(--line); }
  .tr-head h2{ margin:0 0 6px; font-family:var(--serif); font-weight:600; font-size:1.9rem; line-height:1.15; color:var(--text); text-wrap:balance; }
  .tr-meta{ font-family:var(--mono); font-size:.76rem; color:var(--muted); overflow-wrap:anywhere; }
  /* U4b S3 (docs/u4b_free_question_cap_plan.md, mockup 01): the lifetime
     "N of 3 free questions" line -- information, not a warning, so it sits
     in the same muted style as .tr-meta rather than a coloured box. */
  .tr-freeleft{ margin-top:8px; font-size:.8rem; color:var(--muted); }
  .tr-freeleft b{ color:var(--text); }
  .tr-freeleft a{ color:var(--primary); font-weight:600; text-decoration:none; }
  .tr-export{ flex-wrap:wrap; justify-content:flex-end; gap:6px; }
  .tr-verdict, .cm-score{ border-bottom:3px solid var(--gold); }
  .tr-verdict.yes, .cm-score.yes{ border-bottom-color:var(--green); }
  .tr-verdict.no, .cm-score.no{ border-bottom-color:var(--red); }
  .tr-vtop{ display:flex; flex-wrap:wrap; align-items:baseline; gap:8px 14px; }
  .tr-vword{ font-family:var(--serif); font-size:1.6rem; font-weight:600; line-height:1.15; }
  .tr-why{ margin:10px 0 0; max-width:80ch; line-height:1.55; color:var(--muted); }
  .tr-ai-note{ margin:10px 0 0; font-size:.82rem; }
  /* Cusp derivation (D2-W): the "Read as" line in the verdict card's notice slot -- docs/mockups/kp_cusp_derivation
     (states C1-C3b; the "That's not my question" link and the picker are follow-ups). Theme tokens only. */
  .cd-read{ margin:0 0 10px; padding:6px 0 6px 10px; border-left:3px solid var(--primary);
    font-size:.86rem; line-height:1.5; color:var(--text); max-width:75ch; }
  .cd-read b{ font-weight:600; }
  .cd-read.exact{ border-left-color:var(--line-strong); color:var(--muted); }
  .cd-read.approx{ border-left-color:var(--gold); }
  .cd-read .cd-why{ display:block; margin-top:2px; font-size:.82rem; color:var(--muted); }
  /* F1 (docs/plans/kp_cd_f1_picker_plan.md section 8): the "That's not my question" link and picker, states C4-C8. */
  .cd-read .abtn.ghost{ height:auto; padding:0 0 0 6px; font-size:.82rem; text-decoration:underline; text-underline-offset:2px; vertical-align:baseline; }
  .cd-pick{ margin:0 0 12px; padding:12px 14px; border:1px solid var(--tile-line); border-left:4px solid var(--primary);
    border-radius:8px; background:var(--tile-tint); }
  .cd-pick h3{ margin:0 0 4px; font-family:var(--serif); font-size:1.1rem; font-weight:600; line-height:1.25; color:var(--heading); }
  .cd-pick p{ margin:0; font-size:.84rem; line-height:1.5; color:var(--muted); max-width:70ch; }
  .cd-opts{ display:flex; flex-wrap:wrap; gap:8px; margin:10px 0 0; }
  .cd-opts .abtn{ height:auto; min-height:32px; padding:6px 14px; line-height:1.25; text-align:left; white-space:normal; }
  .cd-links{ display:flex; flex-wrap:wrap; gap:2px 18px; margin:8px 0 0; }
  .cd-links .abtn.ghost{ height:auto; padding:3px 0; font-size:.8rem; text-decoration:underline; text-underline-offset:2px; }
  .cd-pick p.cd-err{ margin:8px 0 0; color:var(--dig-bad); }
  /* Same as the RP-1 block below: the mockup draws the plain .abtn (pill, inherited font, primary-coloured text) and does
     not include the redesign override ".abtn{border-radius:9px;color:var(--text)}" further down this stylesheet. */
  .cd-opts .abtn, .cd-links .abtn, .cd-read .abtn{ border-radius:20px; font-family:inherit; }
  .cd-opts .abtn:not(.solid):not(.ghost):not(:disabled){ color:var(--primary); }
  .cd-opts .abtn:not(.solid):not(.ghost):not(:disabled):hover{ border-color:var(--line-strong); background:var(--sunk-soft); color:var(--primary); }
  .cd-opts .abtn.solid:not(:disabled){ color:var(--on-primary); }
  .cd-links .abtn.ghost, .cd-read .abtn.ghost{ color:var(--muted); }
  .cd-links .abtn.ghost:not(:disabled):hover, .cd-read .abtn.ghost:not(:disabled):hover{ color:var(--primary); background:none; }
  /* Relation picker (RP-1): "Who is <Name> to you?" -- docs/mockups/kp_relation_picker. Theme tokens only. */
  .rp-card{ margin:0 0 12px; padding:12px 14px; border:1px solid var(--tile-line); border-left:4px solid var(--primary);
    border-radius:8px; background:var(--tile-tint); }
  .rp-card h3{ margin:0 0 4px; font-family:var(--serif); font-size:1.15rem; font-weight:600; line-height:1.25; color:var(--heading); }
  .rp-card p{ margin:0; font-size:.86rem; line-height:1.5; color:var(--text); max-width:70ch; }
  .rp-rels{ display:flex; flex-wrap:wrap; gap:8px; margin:10px 0 0; }
  .rp-rels .abtn{ height:32px; padding:0 14px; }
  .rp-rels .abtn.solid:disabled{ opacity:1; background:var(--primary); color:var(--on-primary); border-color:var(--primary); }
  /* The mockup draws the plain .abtn (pill, primary-coloured text) and does not include the redesign override
     ".abtn{border-radius:9px;color:var(--text)}" further down this stylesheet. These rules reproduce the mockup's
     buttons exactly inside the picker only; delete this block to make the picker buttons match the rest of the app. */
  .rp-rels .abtn, .rp-save .abtn, .rp-links .abtn{ border-radius:20px; font-family:inherit; }
  .rp-save .abtn{ font-size:.82rem; white-space:normal; }  /* beats the app's ".row button" (.88rem, nowrap) */
  .rp-rels .abtn:not(.solid):not(.ghost):not(:disabled), .rp-save .abtn:not(.solid):not(.ghost):not(:disabled){ color:var(--primary); }
  .rp-rels .abtn:not(.solid):not(.ghost):not(:disabled):hover, .rp-save .abtn:not(.solid):not(.ghost):not(:disabled):hover{ border-color:var(--line-strong); background:var(--sunk-soft); color:var(--primary); }
  .rp-rels .abtn.solid:not(:disabled), .rp-save .abtn.solid:not(:disabled){ color:var(--on-primary); }
  .rp-rels .abtn.ghost, .rp-save .abtn.ghost, .rp-links .abtn.ghost{ color:var(--muted); }
  .rp-rels .abtn.ghost:not(:disabled):hover, .rp-save .abtn.ghost:not(:disabled):hover, .rp-links .abtn.ghost:not(:disabled):hover{ color:var(--primary); background:none; }
  .rp-links{ display:flex; flex-wrap:wrap; gap:2px 18px; margin:8px 0 0; }
  .rp-links .abtn.ghost{ height:auto; padding:3px 0; font-size:.8rem; text-decoration:underline; text-underline-offset:2px; }
  .rp-card p.rp-note, .uv-card p.rp-note{ margin:8px 0 0; font-size:.8rem; color:var(--muted); }
  .rp-card p.rp-err, .uv-card p.rp-err{ margin:8px 0 0; font-size:.82rem; color:var(--dig-bad); }
  .bn-nudge{ display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap; }
  .bn-nudge p{ margin:0; flex:1 1 260px; }
  .bn-nudge .bn-actions{ display:flex; gap:8px; flex-wrap:wrap; }
  .bn-nudge .abtn, .bn-nudge .btn{ width:auto; margin-top:0; }
  .rp-save{ margin:0 0 12px; padding:10px 12px; border:1px dashed var(--line-strong); border-radius:8px; font-size:.86rem; line-height:1.5; }
  .rp-save .row{ margin-top:8px; flex-wrap:wrap; }
  .rp-save .abtn{ height:32px; padding:0 14px; }
  .rp-save p.rp-err{ margin:8px 0 0; font-size:.82rem; color:var(--dig-bad); }
  .rp-save.done{ border-style:solid; color:var(--muted); }
  .tr-rows, .cm-rows{ padding-top:0; padding-bottom:0; }
  .tr-wrow{ display:grid; grid-template-columns:minmax(0,210px) minmax(0,1fr) auto; gap:14px; align-items:center;
    padding:11px 0; border-top:1px solid var(--line); }
  .tr-wrow:first-child, .cm-krow:first-child{ border-top:none; }
  .tr-when{ font-family:var(--mono); font-size:.8rem; font-weight:600; color:var(--text); }
  .tr-tag{ margin-right:6px; font:700 .64rem var(--sans); letter-spacing:.06em; text-transform:uppercase; color:var(--accent); }
  /* NEW (U5f results): band rows on the horary result page (mockup docs/mockups/kp_u5f_horary_results). */
  .tr-wrow > .chip{ margin:0; justify-self:end; }
  .tr-wrow.is-empty .tr-when{ font-family:var(--sans); font-size:.84rem; font-weight:600; color:var(--muted); }
  .ds-sec.is-bands{ flex-wrap:wrap; row-gap:2px; }
  .ds-sec.is-bands > span{ text-align:left; }
  .tr-foot{ margin:0; padding:10px 0 12px; border-top:1px solid var(--line); font-size:.8rem; color:var(--muted); }
  .tr-chain{ font-family:var(--mono); font-size:.74rem; color:var(--muted); overflow-wrap:anywhere; }
  .tr-kp > h3{ display:none; }
  .cm-score{ display:grid; grid-template-columns:auto minmax(0,1fr); gap:22px; align-items:center; }
  .cm-big{ font-family:var(--serif); font-size:2.5rem; line-height:1; font-variant-numeric:tabular-nums; color:var(--text); }
  .cm-big small{ font-size:1.05rem; color:var(--faint-text); }
  .cm-meter{ position:relative; height:10px; margin:6px 0 22px; border-radius:5px; background:var(--sunk-soft); border:1px solid var(--line); }
  .cm-meter > i{ position:absolute; left:0; top:-1px; bottom:-1px; border-radius:5px; background:var(--primary); }
  .cm-th{ position:absolute; top:-6px; bottom:-6px; width:2px; background:var(--text); }
  .cm-th > span{ position:absolute; top:15px; left:-14px; font:600 .6rem var(--mono); color:var(--muted); white-space:nowrap; }
  .cm-verdict{ margin:0; font-size:.9rem; color:var(--text); }
  .cm-people{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:10px; }
  .cm-people > .card{ margin:0; padding:12px 16px; }
  .cm-person b{ display:block; font-weight:600; }
  .cm-person span{ font-family:var(--mono); font-size:.78rem; color:var(--muted); }
  .cm-krow{ display:grid; grid-template-columns:minmax(0,150px) 118px minmax(0,1fr); gap:14px; align-items:center;
    padding:10px 0; border-top:1px solid var(--line); }
  .cm-kn{ font-size:.88rem; font-weight:600; }
  .cm-kp{ font-family:var(--mono); font-size:.8rem; font-variant-numeric:tabular-nums; }
  .cm-kbar{ height:4px; margin-top:4px; overflow:hidden; border-radius:2px; background:var(--sunk-soft); }
  .cm-kbar > i{ display:block; height:100%; background:var(--primary); }
  .cm-kd{ font-size:.8rem; color:var(--muted); }
  .cm-caution{ margin:10px 0 0; padding:12px 16px; background:var(--sheet); border:1px solid var(--line);
    border-left:3px solid var(--gold); border-radius:12px; }
  .cm-caution b{ font-weight:600; }
  .cm-caution p{ margin:2px 0 0; font-size:.86rem; color:var(--muted); }
  .cm-fine{ margin:12px 0 0; max-width:80ch; font-size:.8rem; color:var(--muted); }
  .cm-export{ flex-wrap:wrap; gap:8px; margin-top:12px; }
  .pg-result{ margin-top:18px; }
  .pg-day{ padding:16px 18px 12px; }
  .pg-track{ position:relative; height:76px; margin:22px 0 4px; }
  .pg-rail{ position:absolute; left:0; right:0; top:22px; height:12px; border-radius:6px; background:var(--sunk-soft); border:1px solid var(--line); }
  .pg-seg{ position:absolute; top:16px; height:24px; display:flex; align-items:center; justify-content:center; overflow:hidden;
    white-space:nowrap; border-radius:6px; background:var(--card); font:600 .6rem var(--mono); }
  .pg-seg.bad{ border:1px solid var(--red); color:var(--dig-bad); }
  .pg-seg.good{ top:46px; border:1px solid var(--green); color:var(--dig-good); }
  .pg-now{ position:absolute; top:6px; bottom:0; width:2px; background:var(--text); }
  .pg-now > span{ position:absolute; top:-14px; left:5px; white-space:nowrap; font:700 .58rem var(--sans); letter-spacing:.05em; text-transform:uppercase; color:var(--text); }
  .pg-axis{ display:flex; justify-content:space-between; font-family:var(--mono); font-size:.68rem; color:var(--faint-text); }
  .pg-limbs{ display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:10px; }
  .pg-limbs > .card{ margin:0; padding:12px 14px; border-bottom:3px solid var(--line-strong); }
  .pg-limb small{ display:block; font-size:.64rem; font-weight:700; letter-spacing:.07em; text-transform:uppercase; color:var(--faint-text); }
  .pg-limb b{ display:block; margin-top:4px; font-family:var(--serif); font-size:1.2rem; font-weight:600; line-height:1.2; color:var(--text); }
  .pg-limb span{ display:block; margin-top:2px; font-size:.76rem; color:var(--muted); }
  .pg-wins{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:10px; }
  .pg-wins > .card{ margin:0; padding:12px 16px; display:flex; align-items:center; justify-content:space-between; gap:12px; }
  .pg-win b{ font-weight:600; }
  .pg-win .d{ display:block; font-size:.74rem; color:var(--muted); }
  .pg-wt{ white-space:nowrap; }
  .pg-wt .t{ font-family:var(--mono); font-size:.86rem; font-weight:600; font-variant-numeric:tabular-nums; }
  /* The Horary result sits in the right-hand column beside its form, so its
     head and window rows stack there rather than squeezing three columns. */
  .chart-2col-right .tr-head{ grid-template-columns:minmax(0,1fr); gap:10px; }
  .chart-2col-right .tr-export{ justify-content:flex-start; }
  .chart-2col-right .tr-wrow{ grid-template-columns:minmax(0,1fr) auto; row-gap:3px; }
  .chart-2col-right .tr-chain{ grid-column:1/-1; order:3; }
  /* Limbs and windows fill whatever width the panel has. */
  .pg-limbs{ grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); }
  .pg-wins{ grid-template-columns:repeat(auto-fit,minmax(min(100%,330px),1fr)); }
  /* A short window still shows its name; its true span is in the tooltip
     and in the Day windows tiles below. */
  .pg-seg{ min-width:46px; padding:0 4px; }
  .pg-axis{ position:relative; display:block; height:16px; }
  .pg-axis > span{ position:absolute; top:0; transform:translateX(-50%); white-space:nowrap; }
  .pg-axis > span:first-child{ transform:none; }
  .pg-axis > span:last-child{ transform:translateX(-100%); }
  @media (max-width:640px){
    .tr-head{ grid-template-columns:minmax(0,1fr); }
    .tr-head h2{ font-size:1.45rem; }
    .tr-export{ justify-content:flex-start; }
    .tr-vword{ font-size:1.35rem; }
    .tr-wrow{ grid-template-columns:minmax(0,1fr) auto; row-gap:3px; }
    .tr-chain{ grid-column:1/-1; order:3; }
    .cm-score{ grid-template-columns:minmax(0,1fr); gap:10px; }
    .cm-big{ font-size:2.1rem; }
    .cm-people{ grid-template-columns:minmax(0,1fr); }
    .cm-krow{ grid-template-columns:minmax(0,1fr) auto; row-gap:3px; }
    .cm-kd{ grid-column:1/-1; }
    .pg-limbs{ grid-template-columns:repeat(2,minmax(0,1fr)); }
    .pg-limbs > .card:first-child{ grid-column:1/-1; }
    .pg-wins{ grid-template-columns:minmax(0,1fr); }
    .pg-wins > .card{ flex-wrap:wrap; }
    .pg-seg{ font-size:0; }
  }

  /* ---- PWA install card / toast (static/pwa_install.js) -------------------
     The module only builds the DOM; all visual treatment lives here so it
     matches the rest of the /app design language and both themes. Bottom
     sheet on mobile, small anchored card on desktop. Placed BEFORE .hidden
     below -- test_html_quoting.py enforces that no single-class display
     rule comes after .hidden{display:none}, since equal-specificity +
     later-source-order would silently win over .hidden on any element that
     carried both classes. */
  .kpsa-pwa-card{ position:fixed; inset:0; z-index:9000; display:flex; align-items:flex-end;
                  justify-content:center; background:rgba(15,18,38,.42);
                  animation:kpsaPwaFadeIn .18s ease-out; }
  @media (prefers-reduced-motion:reduce){ .kpsa-pwa-card{ animation:none; } }
  @keyframes kpsaPwaFadeIn{ from{ opacity:0; } to{ opacity:1; } }
  .kpsa-pwa-card-inner{ background:var(--card); color:var(--text); width:100%; max-width:400px;
                         border-radius:16px 16px 0 0; padding:20px 20px calc(20px + env(safe-area-inset-bottom,0px));
                         box-shadow:0 -8px 30px rgba(15,18,38,.22); text-align:center;
                         border:1px solid var(--line); border-bottom:none; }
  @media (min-width:641px){
    .kpsa-pwa-card{ align-items:flex-end; justify-content:flex-end; padding:0 20px 20px 0; background:transparent; }
    .kpsa-pwa-card-inner{ max-width:320px; border-radius:16px; border-bottom:1px solid var(--line);
                           box-shadow:0 12px 32px rgba(15,18,38,.22); }
  }
  .kpsa-pwa-card-inner img{ border-radius:12px; margin-bottom:10px; }
  .kpsa-pwa-card-inner h2{ margin:0 0 10px; font-size:1.05rem; color:var(--heading); }
  .kpsa-pwa-card-inner ul{ list-style:none; margin:0 0 16px; padding:0; text-align:left;
                            display:flex; flex-direction:column; gap:8px; font-size:.88rem; color:var(--text); }
  .kpsa-pwa-card-inner ul li{ padding-left:22px; position:relative; }
  .kpsa-pwa-card-inner ul li::before{ content:"✓"; position:absolute; left:0; color:var(--accent); font-weight:700; }
  .kpsa-pwa-card-inner p{ margin:0 0 16px; font-size:.92rem; color:var(--text); display:flex;
                           align-items:center; justify-content:center; gap:6px; flex-wrap:wrap; }
  .kpsa-pwa-card-inner p svg{ fill:var(--accent); vertical-align:middle; }
  .kpsa-pwa-card-actions{ display:flex; gap:10px; }
  .kpsa-pwa-card-actions .btn{ margin-top:0; flex:1; }
  .kpsa-pwa-card-actions .btn-ghost{ margin-top:0; flex:1; height:var(--ctl-h); border-radius:9px;
                                      background:transparent; color:var(--muted); border:1px solid var(--line-strong);
                                      font-weight:600; font-size:.92rem; cursor:pointer; }
  .kpsa-pwa-card-actions .btn-ghost:hover{ background:var(--primary-soft); color:var(--text); }
  .kpsa-pwa-card *:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
  .kpsa-pwa-toast{ position:fixed; left:50%; bottom:24px; transform:translateX(-50%); z-index:9100;
                    background:var(--primary); color:var(--on-primary); padding:12px 18px; border-radius:10px;
                    font-size:.88rem; max-width:90vw; text-align:center; box-shadow:0 8px 24px rgba(15,18,38,.3);
                    animation:kpsaPwaFadeIn .2s ease-out; }

  .hidden{ display:none; }

  /* Admin -> AI models -> Model prices (docs/plans/kp_auto_model_pricing_plan.md, 8.3): the table scrolls inside its own box. */
  .ai-prices-scroll{ max-width:100%; }
  .ai-prices-table th, .ai-prices-table td{ text-align:left; padding:5px 8px; border-bottom:1px solid var(--border,#eee); white-space:nowrap; vertical-align:top; }
  .ai-prices-table th{ font-weight:600; font-size:.74rem; color:var(--muted,#667); }
  .ai-prices-table .ai-price-form td{ white-space:normal; background:var(--card-alt,rgba(27,42,107,.04)); }
