/* removed for the shell build: Corporate S comes from assets/fonts via tokens.css */
/* ===================== NPB 3D — light institutional theme ===================== */
/* removed for the shell build: design tokens are tokens.css, and they already agree */
/* removed for the shell build: box model, body and .tnum belong to the shell */
/* removed for the shell build: brandmark, wordmark and the upload landing are not used here */
/* ===================== app shell ===================== */
/* removed for the shell build: the shell draws the topbar; .app/.topbar would fight it */
.btn{font:inherit;font-size:.8rem;font-weight:550;border:1px solid var(--line);background:#fff;color:var(--ink);
  padding:.46rem .8rem;border-radius:9px;cursor:pointer;display:inline-flex;align-items:center;gap:.4rem;
  transition:.14s all}
.btn:hover{border-color:#c7ccd8;background:#fbfcfe}
.btn svg{width:15px;height:15px}
.btn.primary{background:var(--npb-red);border-color:var(--npb-red);color:#fff}
.btn.primary:hover{background:var(--npb-red-deep);border-color:var(--npb-red-deep)}
.actions{display:flex;gap:.5rem}

/* removed for the shell build: the shell owns .nav; the app tab strip is restyled below */
.npb3d .tab{appearance:none;border:0;background:none;font:inherit;font-size:.84rem;font-weight:550;color:var(--ink-3);
  padding:.75rem .95rem;cursor:pointer;border-bottom:2.5px solid transparent;margin-bottom:-1px;white-space:nowrap;
  display:flex;align-items:center;gap:.45rem}
.npb3d .tab .n{font-size:.66rem;font-weight:700;color:#b9becb;background:var(--plane-2);border-radius:5px;
  padding:.05rem .32rem;min-width:1.1rem;text-align:center}
.npb3d .tab:hover{color:var(--ink)}
.npb3d .tab.active{color:var(--npb-red);border-bottom-color:var(--npb-red)}
.npb3d .tab.active .n{color:var(--npb-red);background:rgba(202,5,56,.09)}

/* ===================== pages ===================== */
.npb3d .pages{max-width:1500px;margin:0 auto;padding:clamp(1rem,3vw,2rem) clamp(1rem,4vw,2.4rem) 4rem}
.page{display:none}
.page.active{display:block;animation:fade .25s ease}
@keyframes fade{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}

.page-head{display:flex;align-items:flex-end;justify-content:space-between;margin-bottom:1.1rem;
  padding-bottom:.7rem;border-bottom:2px solid var(--navy);gap:1rem;flex-wrap:wrap}
.page-head h2{margin:0;font-size:1.32rem;font-weight:680;letter-spacing:-.015em}
.page-head .kick{font-size:.7rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--npb-red);
  margin-bottom:.18rem}

/* card / panel */
.npb3d .panel{background:var(--surface);border:1px solid var(--line);border-radius:12px;overflow:hidden;
  box-shadow:0 1px 2px rgba(18,19,26,.04);break-inside:avoid}
.npb3d .panel-h{display:flex;align-items:center;justify-content:space-between;gap:.5rem;
  padding:.55rem .85rem;background:linear-gradient(#fbfbfd,#f6f7fa);border-bottom:1px solid var(--line)}
.npb3d .panel-h .ttl{font-size:.74rem;font-weight:700;letter-spacing:.075em;text-transform:uppercase;color:var(--navy)}
.panel-h .side{font-size:.68rem;color:var(--ink-3);font-weight:600;letter-spacing:.02em}

/* dashboard grid */
.grid{display:grid;gap:.95rem}
.dash-grid{grid-template-columns:repeat(12,1fr)}
.col-4{grid-column:span 4}.col-5{grid-column:span 5}.col-6{grid-column:span 6}
.col-7{grid-column:span 7}.col-8{grid-column:span 8}.col-3{grid-column:span 3}.col-12{grid-column:span 12}
@media(max-width:1080px){.dash-grid{grid-template-columns:repeat(6,1fr)}
  .col-4,.col-5,.col-6,.col-7,.col-8,.col-3{grid-column:span 6}}
@media(max-width:640px){.dash-grid{grid-template-columns:1fr}
  .col-4,.col-5,.col-6,.col-7,.col-8,.col-3,.col-12{grid-column:span 1}}

/* tables */
table.tbl{width:100%;border-collapse:collapse;font-size:.8rem}
.tbl th{font-size:.62rem;font-weight:650;letter-spacing:.05em;text-transform:uppercase;color:var(--ink-3);
  text-align:right;padding:.4rem .7rem .35rem;border-bottom:1px solid var(--line);white-space:nowrap;background:#fff}
.tbl th.l{text-align:left}
.tbl td{padding:.32rem .7rem;border-bottom:1px solid var(--line-2);text-align:right;white-space:nowrap}
.tbl tr:last-child td{border-bottom:0}
.tbl td.l{text-align:left}
.tbl tbody tr:hover{background:#fafbfd}
.tbl .name{font-weight:550;color:var(--ink);max-width:15rem;overflow:hidden;text-overflow:ellipsis}
.tbl .tk{font-size:.66rem;color:var(--ink-3);font-weight:500}
.tbl .px{font-weight:600}
.sector-tag{font-size:.64rem;color:var(--ink-3);font-weight:500}

/* deltas */
.delta{font-variant-numeric:tabular-nums;font-weight:600}
.delta.pos{color:var(--pos)}
.delta.neg{color:var(--neg)}
.delta.flat{color:var(--ink-3)}
.chip{display:inline-flex;align-items:center;gap:.2rem;padding:.08rem .38rem;border-radius:6px;font-size:.72rem;
  font-weight:650;font-variant-numeric:tabular-nums}
.chip.pos{color:var(--pos);background:var(--pos-bg)}
.chip.neg{color:var(--neg);background:var(--neg-bg)}
.chip.flat{color:var(--ink-3);background:var(--plane-2)}

/* trend arrows ST/MT/LT */
.trend{display:inline-flex;gap:.14rem;justify-content:flex-end}
.tr{width:15px;height:15px;border-radius:4px;display:inline-flex;align-items:center;justify-content:center;
  font-size:9px;font-weight:800}
.tr.up{color:var(--pos);background:var(--pos-bg)}
.tr.down{color:var(--neg);background:var(--neg-bg)}
.tr.flat{color:var(--ink-3);background:var(--plane-2)}
.trend-head{display:inline-flex;gap:.14rem;justify-content:flex-end}
.trend-head span{width:15px;text-align:center;font-size:.56rem;color:var(--ink-3);font-weight:700}

/* diverging mini-bar (sectors) */
.dbar{position:relative;height:16px;width:74px;display:inline-block;vertical-align:middle}
.dbar .mid{position:absolute;left:50%;top:0;bottom:0;width:1px;background:var(--line)}
.dbar .fill{position:absolute;top:3px;height:10px;border-radius:2px}
.dbar .fill.pos{left:50%;background:var(--pos)}
.dbar .fill.neg{right:50%;background:var(--neg)}

/* RSI / OBOS chip (sectors) */

/* market-wide OBOS gauge (breadth tiles) */
.obos{display:flex;align-items:center;gap:.35rem;margin-top:.45rem}
.obos-lab{font-size:.58rem;font-weight:700;letter-spacing:.02em;white-space:nowrap}
.obos-lab.os{color:#0a6b8a}.obos-lab.ob{color:#9a6a00}
.obos-track{flex:1;height:6px;border-radius:4px;background:var(--plane-2);position:relative;display:flex;
  justify-content:center;overflow:hidden}
.obos-track .seg{height:100%}
.obos-track .seg.os{background:#1592b8;border-radius:4px 0 0 4px}
.obos-track .seg.ob{background:#c68a1e;border-radius:0 4px 4px 0}

/* stat tiles (breadth) */
.tiles{display:grid;grid-template-columns:repeat(2,1fr);gap:.7rem;padding:.85rem}
.tile{border:1px solid var(--line);border-radius:9px;padding:.6rem .7rem;background:#fff}
.tile .lab{font-size:.6rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-3)}
.tile .val{font-size:1.15rem;font-weight:700;margin-top:.15rem;font-variant-numeric:tabular-nums}
.tile .sub{font-size:.66rem;color:var(--ink-3);margin-top:.05rem}
.adbar{height:7px;border-radius:4px;background:var(--neg);overflow:hidden;margin-top:.45rem;display:flex}
.adbar .adv{background:var(--pos);height:100%}
.adrow{display:flex;justify-content:space-between;font-size:.66rem;font-weight:600;margin-top:.28rem}
.adrow .a{color:var(--pos)}.adrow .d{color:var(--neg)}

/* movers */
.movers{display:grid;grid-template-columns:1fr 1fr;gap:0}
.movers .mv{padding:.55rem .8rem}
.movers .mv+.mv{border-left:1px solid var(--line-2)}
.mv h4{margin:0 0 .35rem;font-size:.62rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-3);
  display:flex;align-items:center;gap:.3rem}
.mv-row{display:flex;align-items:center;gap:.5rem;padding:.2rem 0;font-size:.74rem}
.mv-row .nm{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:550}
.mv-row .tk{color:var(--ink-3);font-size:.64rem;font-weight:600;width:3.4rem;flex-shrink:0}

/* focus list 2x2 */
.focus-grid{display:grid;grid-template-columns:1fr 1fr;gap:1rem}
@media(max-width:820px){.focus-grid{grid-template-columns:1fr}}
.region-flag{width:8px;height:8px;border-radius:50%;background:var(--npb-red);display:inline-block}

/* hilo two-col */
.hilo-grid{display:grid;grid-template-columns:1fr 1fr;gap:1rem;align-items:start}
@media(max-width:820px){.hilo-grid{grid-template-columns:1fr}}
.hilo-h{display:flex;align-items:center;gap:.5rem;padding:.6rem .9rem;border-bottom:1px solid var(--line)}
.hilo-h .dot{width:9px;height:9px;border-radius:50%}
.hilo-h.hi .dot{background:var(--pos)}.hilo-h.lo .dot{background:var(--neg)}
.hilo-h .ttl{font-size:.8rem;font-weight:700;letter-spacing:.03em}
.hilo-h.hi .ttl{color:var(--pos)}.hilo-h.lo .ttl{color:var(--neg)}
.hilo-h .cnt{margin-left:auto;font-size:.72rem;font-weight:700;padding:.1rem .45rem;border-radius:20px}
.hilo-h.hi .cnt{color:var(--pos);background:var(--pos-bg)}.hilo-h.lo .cnt{color:var(--neg);background:var(--neg-bg)}

/* removed for the shell build: the site shell draws the footer, and a bare
   .foot rule here would have restyled it — app.css loads after shell.css. */

/* ===================== site-tool states ===================== */
/* removed for the shell build: freshness moved into the app bar */

.btn.busy{opacity:.6;pointer-events:none}
.btn[hidden]{display:none}
.statebox{display:none;align-items:center;justify-content:center;min-height:62vh;padding:2rem}
.statebox-in{text-align:center;max-width:30rem}
.statebox-in svg{width:38px;height:38px;color:var(--ink-3);margin-bottom:1rem}
.statebox-msg{font-size:.98rem;color:var(--ink-2);line-height:1.5}
.statebox-msg b{color:var(--npb-red)}
.spin{width:34px;height:34px;border-radius:50%;border:3px solid var(--plane-2);border-top-color:var(--npb-red);
  margin:0 auto 1.1rem;animation:spin .8s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.spin{animation-duration:2s}}
.toast{position:fixed;left:50%;bottom:1.4rem;transform:translate(-50%,140%);z-index:60;
  background:var(--navy);color:#fff;font-size:.84rem;font-weight:500;padding:.7rem 1.1rem;border-radius:10px;
  box-shadow:var(--shadow);transition:transform .28s cubic-bezier(.2,.8,.2,1);max-width:min(90vw,32rem)}
.toast.show{transform:translate(-50%,0)}
/* removed for the shell build: the drop-to-publish overlay. There is no
   publish path on a public page, and the prompt would be a lie. */

/* ===================== CORE HOLDINGS ===================== */
.hold-wrap{display:flex;flex-direction:column;gap:.95rem}
.hold-panel .tbl td{padding:.4rem .7rem}
/* Internal G-codes: off by default, revealed by the checkbox that precedes the
   panels. Pure CSS so the choice survives a tab switch with no event wiring. */
.hold-cb{position:absolute;opacity:0;pointer-events:none}
.hold-tbl .gcode{display:none}
.hold-cb:checked ~ .hold-panel .gcode{display:table-cell}
.hold-bar{display:flex;align-items:baseline;gap:1rem;flex-wrap:wrap;margin-bottom:-.2rem}
.hold-bar label{display:inline-flex;align-items:center;gap:.4rem;cursor:pointer;
  font-size:.68rem;font-weight:600;letter-spacing:.03em;color:var(--ink-3);user-select:none}
.hold-bar label .box{width:12px;height:12px;border:1px solid var(--line);border-radius:3px;
  background:var(--surface);display:inline-block;transition:background .15s,border-color .15s}
.hold-cb:checked ~ .hold-bar label .box{background:var(--npb-red);border-color:var(--npb-red);
  box-shadow:inset 0 0 0 2px var(--surface)}
.hold-bar label:hover{color:var(--ink)}
.hold-bar .note{font-size:.66rem;color:var(--ink-3)}
.hold-tbl .fund{max-width:18rem}
.hold-tbl .fund .name{display:block;max-width:none;white-space:normal}
.hold-tbl .fund .isin{display:block;font-size:.6rem;color:var(--ink-3);opacity:.75;
  font-variant-numeric:tabular-nums;letter-spacing:.02em;margin-top:.05rem}
.hold-tbl .gcode{font-size:.66rem;color:var(--ink-3);font-weight:600}
.hold-tbl .ccy{font-size:.66rem;color:var(--ink-3);font-weight:600;text-align:center}
.hold-tbl .desc-col{font-size:.66rem;color:var(--ink-3);line-height:1.3;
  white-space:normal;max-width:20rem;min-width:11rem}
.hold-tbl .none{color:var(--ink-3);opacity:.5}
/* YTD carries the most weight of the six, so it gets a hairline-bounded column
   and a slightly heavier figure — separation, not another colour, which would
   collide with the pos/neg semantics the numbers already use. */
.hold-tbl th.em,.hold-tbl td.em{background:rgba(16,32,64,.035);
  box-shadow:inset 1px 0 0 var(--line-2),inset -1px 0 0 var(--line-2)}
.hold-tbl td.em .delta{font-weight:700}
.hold-tbl th.em{color:var(--ink)}
.hold-tbl tr.sub td{background:#f7f8fb;border-bottom:1px solid var(--line);
  font-size:.62rem;font-weight:700;letter-spacing:.075em;text-transform:uppercase;
  color:var(--ink-3);padding:.28rem .7rem;text-align:left}
/* The header must sit over the plot, not over the cell's right edge — .tbl th
   defaults to right-aligned to match the numeric columns. */
.hold-tbl .trend,.hold-tbl th.trend-h{text-align:center;width:96px}
/* One series, so the line carries no identity — it wears a de-emphasis ink and
   the numeric columns beside it carry polarity. Only the end-dot is semantic. */
.spark{display:block;margin:0 auto;overflow:visible}
.spark path{stroke:var(--ink-3);opacity:.65}
.spark circle.pos{fill:var(--pos)}
.spark circle.neg{fill:var(--neg)}
.spark circle.flat{fill:var(--ink-3)}
.spark-na{color:var(--ink-3);opacity:.5}
.hold-empty{color:var(--ink-3);padding:1.5rem;text-align:center}

/* ===================== PRINT DOCUMENT (explicit pagination) =====================
   The old print path let content free-flow onto A4 and hoped. That failed twice:
   the 1062px sheet is narrower than the 1080px breakpoint, so the dashboard's
   12-col grid collapsed to 6 AND every .col-* span was rewritten to 6 — every
   panel went full-width and stacked, which is why printed columns never lined up.
   Now we build our own sheets at a fixed design width and scale them to the page,
   so print layout == screen layout, and we can number the sheets ourselves
   (Chrome has never implemented @page margin boxes, so counter(page) is out).
   ============================================================================= */
:root{
  /* NO zoom. Blink paginates on UNZOOMED geometry, so a zoomed sheet that looks
     like it fits still breaks across pages — that was the 22-pages-for-10-sheets
     bug. Instead the sheet is the real page box, and density comes from scaling
     the ROOT FONT SIZE: virtually every size in this stylesheet is in rem, so
     shrinking the root shrinks type, padding and gaps together, exactly as zoom
     did, while every box keeps honest layout dimensions.
     12px root ≈ 6.5pt table type (André, 05.08.2026: smaller is fine, applied
     document-wide rather than per-page). */
  --print-w:1062px;      /* 297mm - 16mm side margins @ 96 css px/in */
  --sheet-h:735px;       /* 210mm - 14mm, less a little slack so nothing spills */
  /* Portrait pages for the long Hi/Lo lists — André's Excel report used portrait
     for exactly those and it read well. Same area, but the extra height suits a
     list far better than the extra width does. Verified: Blink honours CSS named
     pages, so one document can mix both orientations. */
  --print-w-p:740px;     /* 210mm - 14mm */
  --sheet-h-p:1050px;    /* 297mm - 16mm, less slack */
  /* Root font-size while printing — the density lever. 11px ≈ 5.9pt table type.
     Chosen because it is the point at which Top Movers joins Sectors on sheet 2
     (André asked for that, and accepted a smaller face document-wide to get it);
     at 11.5px it spills to a third sheet. */
  --print-root:11px;
}
#printdoc{display:none}
/* Measuring pass: the packer must lay blocks out under the SAME rules that will
   print, or it packs against the wrong heights. Rendered off-screen, not hidden —
   display:none has no geometry. */
/* The width MATTERS: blocks are measured as direct children of #printdoc before
   being placed in a sheet, and position:fixed without a width shrink-wraps —
   tables then wrap at the wrong width and every measured height is wrong. Pin it
   to the sheet width so measuring geometry equals printing geometry. */
#printdoc.measuring{display:block;position:fixed;left:-99999px;top:0;z-index:-1;
  width:var(--print-w);font-size:var(--print-root)}
.sheet{width:var(--print-w);height:var(--sheet-h);display:flex;flex-direction:column;
  overflow:hidden;background:#fff}
.sheet.portrait{width:var(--print-w-p);height:var(--sheet-h-p)}
/* Layout pins scoped to .sheet so they hold on screen (while measuring) and in
   print alike. .sheet only ever exists inside #printdoc, so nothing else moves. */
.sheet .dash-grid{grid-template-columns:repeat(12,1fr)!important}
.sheet .col-3{grid-column:span 3!important}.sheet .col-4{grid-column:span 4!important}
.sheet .col-5{grid-column:span 5!important}.sheet .col-6{grid-column:span 6!important}
.sheet .col-7{grid-column:span 7!important}.sheet .col-8{grid-column:span 8!important}
.sheet .col-12{grid-column:span 12!important}
.sheet .focus-grid,.sheet .hilo-grid{grid-template-columns:1fr 1fr!important}
/* Purpose-built containers for split output. Cloning .hilo-grid dragged its
   !important column rule along, which then had to be fought with an inline
   !important — these carry no such baggage. */
.sheet .np-grid{display:grid;grid-template-columns:1fr 1fr;gap:.7rem;align-items:start}
.sheet .np-full{display:block}
/* Portrait sheets are 740px wide: two side-by-side tables leave ~360px each,
   which clips the YTD column. Stack them instead — the whole point of portrait
   here is height, not width.
   Scoped to .portrait rather than .sheet.portrait so it ALSO applies while the
   packer measures: blocks are measured as direct children of #printdoc, which
   carries the same class for the duration of a portrait section. Without that
   the packer measures a 2-column layout and prints a 1-column one. */
.portrait .hilo-grid,.portrait .focus-grid,
.portrait .np-grid{grid-template-columns:1fr!important}
.sheet .grid,.sheet .focus-grid,.sheet .hilo-grid{gap:.7rem}
.sheet-body{flex:1 1 auto;overflow:hidden;min-height:0}
.sheet-head{display:flex;align-items:center;justify-content:space-between;gap:1.2rem;
  border-bottom:1.8px solid var(--npb-red);padding-bottom:5px;margin-bottom:8px;flex:0 0 auto}
/* The date block and page number must never be squeezed off the edge: the sheet
   clips its overflow, so a nowrap title in the middle would silently eat them. */
.sheet-head .brandmark25{height:26px;width:auto;display:block;flex:0 0 auto}
.sheet-head .pmeta{text-align:right;font-size:.62rem;color:var(--ink-3);line-height:1.3;
  white-space:nowrap;flex:0 0 auto}
.sheet-head .pmeta b{display:block;font-size:.7rem;color:var(--ink);font-weight:650}
.sheet-title{display:flex;align-items:baseline;gap:.5rem;flex:1 1 auto;min-width:0;overflow:hidden}
.sheet-title .kick{font-size:.58rem;font-weight:700;letter-spacing:.11em;text-transform:uppercase;
  color:var(--npb-red);white-space:nowrap}
.sheet-title h2{font-size:1.05rem;font-weight:700;letter-spacing:-.005em;color:var(--ink);margin:0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
.sheet-title .cont{font-size:.6rem;color:var(--ink-3);letter-spacing:.04em}
.sheet-foot{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;
  border-top:1px solid var(--line);margin-top:8px;padding-top:4px;
  font-size:.6rem;color:var(--ink-3);letter-spacing:.03em}
.sheet-foot .src{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;font-weight:600}
.sheet-foot .asof{flex:1 1 auto;text-align:center;white-space:nowrap;padding:0 1rem}
.sheet-foot .pg{font-variant-numeric:tabular-nums;font-weight:600;flex:0 0 auto;padding-left:1rem}

@media print{
  @page{size:A4 landscape;margin:7mm 8mm}
  @page portraitpg{size:A4 portrait;margin:8mm 7mm}
  .sheet.portrait{page:portraitpg}
  html,body{background:#fff}
  html{font-size:var(--print-root)}   /* the density lever — see :root above */
  body{margin:0;padding:0}
  body>*:not(#printdoc){display:none!important}
  #printdoc{display:block}
  /* Break BEFORE each sheet after the first, never after. break-after on the
     last sheet leaves a trailing blank page, and :last-child only reliably
     cancels it if nothing else is ever appended to #printdoc. */
  .sheet + .sheet{break-before:page;page-break-before:always}
  /* Keep the fills. Without this Blink drops every background unless the user
     ticks "Background graphics" — the reason the PDF looked washed out. */
  *{-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}
  .panel,.mv,.tile{break-inside:avoid;page-break-inside:avoid}
  /* Print the FULL OBOS list regardless of the on-screen filter, so a filtered
     screen can never produce a quietly partial report. */
  .ob-row{display:table-row!important}
  .ob-panel{display:block!important}
}

/* ===================== OBOS GAUGE (sectors) =====================
   Diverging RSI scale, 0-100 mapped across the track, neutral at 50.
   Green = oversold, amber = overbought: the Excel's convention, and NOT the
   red/green used for price deltas — these mean opposite things, so they must
   not share a palette. */
.obos-cell{text-align:center;width:130px}
.obos-head{display:flex;justify-content:space-between;width:112px;margin:0 auto;
  font-style:normal;font-size:.52rem;letter-spacing:.06em}
.obos-head i{font-style:normal}
.obos-head .os{color:#2f7d32}
.obos-head .n{color:var(--ink-3);opacity:.7}
.obos-head .ob{color:#a85806}
.obosg{position:relative;display:inline-block;width:112px;height:13px;vertical-align:middle;
  background:var(--surface);border-radius:2px;overflow:hidden}
.obosg .zone{position:absolute;top:0;bottom:0}
.obosg .mid{position:absolute;left:50%;top:0;bottom:0;width:1px;background:rgba(16,24,40,.45)}
.obosg .bar{position:absolute;top:6px;height:1.5px;background:rgba(16,24,40,.55)}
.obosg .ghost{position:absolute;top:3.5px;width:1.5px;height:6px;margin-left:-.75px;
  background:rgba(16,24,40,.32)}                                   /* RSI one week ago */
.obosg .dot{position:absolute;top:3.5px;width:6px;height:6px;margin-left:-3px;
  transform:rotate(45deg);background:#4a4a48}                      /* diamond, as the Excel */
.obosg .dot.ob{background:#a85806}
.obosg .dot.os{background:#2f7d32}
.obosg.empty{color:var(--ink-3);opacity:.5;background:none}

/* ===================== OBOS TAB (Bollinger %B) =====================
   Same gauge as the sectors one, rescaled: the axis is %B from -0.25 to 1.25,
   so 0 and 1 are the Bollinger bands themselves and 0.5 sits dead centre.
   Zone widths follow from that: 0.25/1.5 = 16.67% up to the lower band, etc. */
.obosg.obosb .b-os2{left:0;width:16.67%;background:rgba(47,125,50,.30)}      /* <= 0   below lower band */
.obosg.obosb .b-os1{left:16.67%;width:13.33%;background:rgba(47,125,50,.13)} /* 0-0.2  approaching      */
.obosg.obosb .b-ob1{left:70%;width:13.33%;background:rgba(168,88,6,.13)}     /* 0.8-1  approaching      */
.obosg.obosb .b-ob2{left:83.33%;width:16.67%;background:rgba(168,88,6,.30)}  /* >= 1   above upper band */
.obosg .dot.ob-near{background:#c98a3a}
.obosg .dot.os-near{background:#4f9a58}

.ob-wrap{display:flex;flex-direction:column;gap:.95rem}
.obv{font-variant-numeric:tabular-nums;font-weight:650;font-size:.72rem}
.obv.ob{color:#a85806}.obv.os{color:#2f7d32}
.obv.ob-near{color:#c07f28}.obv.os-near{color:#41864a}
.obv.mid{color:var(--ink-3)}.obv.none{color:var(--ink-3);opacity:.5}
.obmove{font-variant-numeric:tabular-nums;font-size:.68rem;font-weight:600}
.obmove.up{color:var(--pos)}.obmove.dn{color:var(--neg)}.obmove.flat{color:var(--ink-3);opacity:.7}

.ob-xwrap{display:grid;grid-template-columns:1fr 1fr;gap:0}
.ob-xcol{padding:.55rem .8rem .7rem}
.ob-xcol + .ob-xcol{border-left:1px solid var(--line-2)}
.ob-xcol h5{margin:0 0 .4rem;font-size:.62rem;font-weight:700;letter-spacing:.075em;text-transform:uppercase}
.ob-xcol h5.ob{color:#a85806}.ob-xcol h5.os{color:#2f7d32}
.ob-x{display:flex;align-items:center;gap:.55rem;font-size:.7rem;padding:.1rem 0}
.ob-x .n{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ink);min-width:0}
.ob-x .n i{font-style:normal;color:var(--ink-3);font-size:.63rem;margin-left:.35rem}
.ob-x .g{flex:0 0 auto;display:flex;align-items:center}
.ob-x .obv{flex:0 0 2rem;text-align:right}
.ob-x .obmove{flex:0 0 2.2rem;text-align:right}

.ob-tally{display:flex;flex-wrap:wrap;gap:1.1rem;padding:.5rem .8rem .65rem;
  border-top:1px solid var(--line-2);font-size:.68rem;color:var(--ink-3)}
.ob-tally .t b{font-weight:700;font-variant-numeric:tabular-nums}
.ob-tally .t.ob b{color:#a85806}.ob-tally .t.mid b{color:var(--ink)}
.ob-tally .t.os b{color:#2f7d32}.ob-tally .t.none b{color:var(--ink-3)}

.ob-bar{display:flex;align-items:flex-end;gap:.8rem;flex-wrap:wrap;
  padding:.15rem 0 .1rem}
.ob-f{display:flex;flex-direction:column;gap:.18rem}
.ob-f span{font-size:.6rem;letter-spacing:.07em;text-transform:uppercase;color:var(--ink-3);font-weight:650}
.ob-f select{font:inherit;font-size:.72rem;padding:.24rem .5rem;border:1px solid var(--line);
  border-radius:6px;background:var(--surface);color:var(--ink);min-width:9rem;cursor:pointer}
.ob-f select:focus-visible{outline:2px solid var(--npb-red);outline-offset:1px}
.obos-reset{font:inherit;font-size:.7rem;padding:.28rem .7rem;border:1px solid var(--line);
  border-radius:6px;background:var(--surface);color:var(--ink-3);cursor:pointer}
.obos-reset:hover{color:var(--ink);border-color:var(--ink-3)}
.ob-count{font-size:.68rem;color:var(--ink-3);padding-bottom:.35rem}
.ob-tbl .ob-meta{font-size:.66rem;color:var(--ink-3)}
.ob-tbl .name{max-width:16rem}

/* ============ HI/LO SECTOR BREAKDOWN ============ */
.hilo-sum{margin-bottom:.95rem}
.tally-wrap{display:grid;grid-template-columns:1fr 1fr;gap:0}
.tally{padding:.55rem .8rem .7rem}
.tally + .tally{border-left:1px solid var(--line-2)}
.tally h5{margin:0 0 .4rem;font-size:.62rem;font-weight:700;letter-spacing:.075em;
  text-transform:uppercase;color:var(--ink-3)}
.tally h5 b{color:var(--ink);font-size:.72rem;margin-left:.2rem}
.tally h5.hi{color:var(--pos)}
.tally h5.lo{color:var(--neg)}
.tally-row{display:flex;align-items:center;gap:.5rem;font-size:.66rem;padding:.08rem 0}
.tally-row .s{flex:0 0 8.6rem;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tally-row .track{flex:1 1 auto;height:6px;background:var(--line-2);border-radius:3px;overflow:hidden}
.tally-row .fill{display:block;height:100%;border-radius:3px}
.tally-row .fill.hi{background:var(--pos)}
.tally-row .fill.lo{background:var(--neg)}
.tally-row .n{flex:0 0 1.6rem;text-align:right;font-variant-numeric:tabular-nums;font-weight:600}
.tally-row .p{flex:0 0 2.1rem;text-align:right;font-variant-numeric:tabular-nums;color:var(--ink-3)}
.tally-none{font-size:.66rem;color:var(--ink-3);opacity:.7;padding:.2rem 0}

/* pre-print chooser */
.pdlg-ov{position:fixed;inset:0;z-index:900;background:rgba(16,24,40,.42);
  display:flex;align-items:center;justify-content:center;padding:1.5rem}
.pdlg{background:var(--surface);border-radius:14px;box-shadow:0 24px 60px rgba(16,24,40,.28);
  width:min(26rem,94vw);padding:1.4rem 1.5rem 1.2rem}
.pdlg h3{font-size:1.25rem;font-weight:700;letter-spacing:-.01em;margin:0 0 .25rem;color:var(--ink)}
.pdlg-sub{font-size:.74rem;color:var(--ink-3);margin:0 0 1rem}
.pdlg-list{display:flex;flex-direction:column;gap:.1rem;margin-bottom:1.2rem}
.pdlg-list label{display:flex;align-items:center;gap:.6rem;padding:.4rem .5rem;border-radius:7px;
  cursor:pointer;font-size:.82rem;color:var(--ink)}
.pdlg-list label:hover{background:var(--line-2)}
.pdlg-list input{width:15px;height:15px;accent-color:var(--npb-red);cursor:pointer;flex:0 0 auto}
.pdlg-act{display:flex;justify-content:flex-end;gap:.5rem}

/* print-only page furniture, hidden on screen */
.print-only{display:none}

/* The old "shrink everything to 5-6pt and hope it fits" print block lived here.
   Superseded by the explicit sheet paginator above: content now renders at its
   on-screen proportions and the whole sheet is scaled via zoom instead. */

/* ============================================================================
   Shell integration — npb-insights only
   ============================================================================
   Everything above came from the standalone NPB 3D tool, the gold standard, which
   draws its own topbar, nav and footer. Here the site shell draws those, so
   this block replaces the chrome that was cut and neutralises the app's own
   page-level padding.

   .npb3d sits on BOTH #npb-app and #printdoc. The print sheets are clones that
   live outside the app container — scoping to #npb-app alone would strip every
   style off every printed sheet. See ARCHITECTURE.md §13.
   ========================================================================= */

/* The app's page wrapper is inside the shell's .main, which already carries the
   site gutter and max-width. Undo both, or the content is inset twice. */
.npb3d .pages{max-width:none;margin:0;padding:0}

/* App bar: as-of, freshness, and the actions that used to sit in the app's own
   topbar. This is app content, not site chrome — it belongs to 3D and scrolls
   with it. */
.npb3d-bar{
  display:flex;align-items:baseline;gap:.6rem 1rem;flex-wrap:wrap;
  padding-bottom:.7rem;margin-bottom:.9rem;border-bottom:1px solid var(--line);
}
.npb3d-bar .asof{font-size:.95rem;font-weight:650;letter-spacing:-.01em}
.npb3d-bar .sub{font-size:.7rem;color:var(--ink-3);letter-spacing:.02em}
.npb3d-bar .sub.stale{color:var(--neg);font-weight:600}
.npb3d-bar .actions{margin-left:auto}
@media(max-width:520px){
  .npb3d-bar .actions{margin-left:0;width:100%}
  .npb3d-bar .btn{flex:1 1 auto;justify-content:center}
}

/* The app's own page tabs. Deliberately not .nav/.nav-in: those are the site
   shell's section tabs and the two were the same class name. Renamed here
   rather than scoped, because the shell's sticky nav sits directly above and
   two sticky strips fighting for the same offset is its own bug. */
.npb3d-tabs{border-bottom:1px solid var(--line);margin-bottom:1.1rem}
.npb3d .tabrow{display:flex;gap:.2rem;overflow-x:auto;-webkit-overflow-scrolling:touch;
  scrollbar-width:none}
.npb3d .tabrow::-webkit-scrollbar{display:none}

/* Print: the shell's chrome is hidden by body>*:not(#printdoc) already, and
   #printdoc is a body-level sibling, so nothing here needs to change for it.
   This only stops the app bar and tabs printing on screen-to-paper fallback. */
@media print{.npb3d-bar,.npb3d-tabs{display:none}}

/* At 390px the four page tabs do not fit and the strip scrolls, but the
   scrollbar is hidden — so the last tab is simply invisible with nothing to
   suggest it exists. A soft right edge is the affordance. */
@media(max-width:640px){
  .npb3d-tabs{position:relative}
  .npb3d-tabs::after{
    content:"";position:absolute;right:0;top:0;bottom:1px;width:2.2rem;pointer-events:none;
    background:linear-gradient(90deg,rgba(244,245,248,0),var(--plane) 75%);
  }
}

/* Shown only once the Inside model has actually been fetched, so it reports a
   fact rather than an intention: this reader is seeing the staff pages. */
.npb3d-bar .tiertag{
  font-size:.6rem;font-weight:700;letter-spacing:.09em;text-transform:uppercase;
  color:var(--npb-red);background:var(--neg-bg);border-radius:5px;padding:.12rem .4rem;
}
.npb3d-bar .tiertag[hidden]{display:none}
