/* ============================================================================
   npb-insights.ch — site shell
   ============================================================================
   Chrome only: topbar, section nav, app rail, section index, stub state,
   footer. App content styles live in the app's own folder.

   Every colour here comes from tokens.css. If you find yourself typing a hex
   value in this file, the token is missing — add it there instead.

   MOBILE FIRST (ARCHITECTURE.md §11). The base rules are the 390px layout and
   every media query adds to it, never subtracts. All three tiers are held to
   390 / 768 / 1280, and nothing may overflow the viewport horizontally at any
   width — tools/check-responsive.mjs measures exactly that.
   ========================================================================= */

*{box-sizing:border-box}
html,body{margin:0;padding:0}
html{scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

body{
  font-family:var(--sans);
  color:var(--ink);
  background:var(--plane);
  -webkit-font-smoothing:antialiased;
  font-feature-settings:"tnum" 0;
  line-height:1.35;
  min-height:100vh;
  display:flex;
  flex-direction:column;
}

a{color:inherit}
img{max-width:100%}
.tnum{font-variant-numeric:tabular-nums}

/* Screen-reader-only, for skip links and nav landmarks. */
.sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}
.skip:focus{position:fixed;left:1rem;top:1rem;z-index:100;width:auto;height:auto;
  clip:auto;margin:0;padding:.55rem .9rem;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-btn);box-shadow:var(--shadow);font-size:.82rem;font-weight:600}

:focus-visible{outline:2px solid var(--npb-red);outline-offset:2px}

/* ---------- kicker ---------------------------------------------------------
   Small uppercase red label above a heading. Used throughout NPB 3D; adopted
   site-wide (ARCHITECTURE.md §6). */
.kicker{
  font-size:.7rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;
  color:var(--npb-red);margin:0 0 .5rem;
}
.kicker.muted{color:var(--ink-3)}

/* ---------- lockup ---------------------------------------------------------
   Sized by HEIGHT, never width — the viewBox widens with the app name, so a
   fixed width silently shrinks the mark on longer names (ARCHITECTURE.md §7).

   max-width is a guard, not a size. A long app name on a narrow screen would
   otherwise push the mark off the edge; clamped, the whole lockup scales down
   proportionally, which is exactly what sizing by height protects. */
.lockup{height:var(--lockup-h);width:auto;max-width:100%;display:block;flex:0 1 auto}
/* The stub's large mark. Sets the token rather than the height, so the
   fallback's gap and font-size scale with it too. */
.lg{--lockup-h:44px}
a.lockup-link{display:block;text-decoration:none;border-radius:var(--r-btn);min-width:0}
a.lockup-link:focus-visible{outline:2px solid var(--npb-red);outline-offset:4px}

/* The static fallback, which is what a visitor with scripting off sees and
   what every visitor sees for the first frame. The red mark is the corporate
   artwork itself (npb-mark.svg, unmodified) — only the name slot differs from
   the measured SVG, where the baseline is exact in user units rather than
   approximated from a font's metrics.

   The ratios are lockup.js's, expressed against the height:
     gap        34.3 / 121  = .283
     font-size  41.17 / 121 = .340
   The nudge below lands the name near the corporate baseline (y 288.3, which
   is .597 of the way down the mark) from a centred start. It is close, not
   exact; shell.js replaces the whole thing on load. If a swap ever becomes
   visible, these three numbers have drifted from lockup.js. */
.lockup-fb{
  display:inline-flex;align-items:center;min-width:0;max-width:100%;
  height:var(--lockup-h);gap:calc(var(--lockup-h) * .283);
}
.lockup-fb .mk{height:100%;width:auto;flex:0 0 auto}
.lockup-fb .nm{
  font-size:calc(var(--lockup-h) * .34);line-height:1;font-weight:400;
  color:var(--lockup-name);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;min-width:0;
  transform:translateY(calc(var(--lockup-h) * .012));
}

/* ---------- sticky chrome ---------------------------------------------------
   Topbar and nav scroll as one block, so neither has to know the other's
   height. That pair of magic offsets was wrong at every width except the one
   it was measured at. The rail still needs a number — shell.js measures it
   into --chrome-h, and the default below carries the no-script case. */
.chrome{position:sticky;top:0;z-index:40;background:var(--plane)}

/* ---------- topbar ---------------------------------------------------------- */
.topbar{
  background:rgba(255,255,255,.9);backdrop-filter:blur(12px);
  border-bottom:1px solid var(--line);
  padding:.62rem var(--gutter);
}
.topbar-in{max-width:var(--wrap);margin:0 auto;display:flex;align-items:center;gap:1rem}
.topbar .meta{margin-left:auto;text-align:right;line-height:1.25;min-width:0}
/* A signed-out visitor gets no slot at all — not an empty one, and not a
   placeholder that hints an account exists. */
.topbar .meta:empty{display:none}
.topbar .who{font-size:.76rem;font-weight:600;color:var(--ink-2);
  overflow:hidden;text-overflow:ellipsis;max-width:min(52vw,22rem);white-space:nowrap}
.topbar .signout{font-size:.7rem;color:var(--ink-3);letter-spacing:.02em;text-decoration:none}
.topbar .signout:hover{color:var(--npb-red)}

/* ---------- section nav -----------------------------------------------------
   Three tabs fit at 390px. The horizontal scroll is the safety valve for a
   fourth pillar or a long rename, not the normal state. */
.nav{background:var(--plane);border-bottom:1px solid var(--line);padding:0 var(--gutter)}
.nav-in{max-width:var(--wrap);margin:0 auto;display:flex;gap:.1rem;
  overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}
.nav-in::-webkit-scrollbar{display:none}
.tab{
  font:inherit;font-size:.8rem;font-weight:550;color:var(--ink-3);text-decoration:none;
  padding:.7rem .55rem;border-bottom:2.5px solid transparent;margin-bottom:-1px;
  white-space:nowrap;display:flex;align-items:center;gap:.35rem;flex:0 0 auto;
}
.tab .n{
  font-size:.64rem;font-weight:700;color:#b9becb;background:var(--plane-2);border-radius:5px;
  padding:.05rem .3rem;min-width:1.05rem;text-align:center;
}
.tab:hover{color:var(--ink)}
.tab.active{color:var(--npb-red);border-bottom-color:var(--npb-red)}
.tab.active .n{color:var(--npb-red);background:var(--neg-bg)}

/* Padlock on a section behind Access. Deliberately quiet — it marks a door,
   it does not warn about one. */
.lock{width:11px;height:11px;flex:0 0 auto;color:var(--ink-3);opacity:.75}
.tab.active .lock{color:var(--npb-red);opacity:1}

/* ---------- body layout -----------------------------------------------------
   One column by default. The rail becomes a second column only on a wide
   viewport AND only when it has something in it — an empty rail would
   otherwise reserve 15.5rem of blank gutter. */
.layout{
  flex:1 0 auto;width:100%;max-width:var(--wrap);margin:0 auto;
  padding:clamp(1rem,3vw,2rem) var(--gutter) 3rem;
  display:grid;grid-template-columns:minmax(0,1fr);gap:1.3rem;
  align-items:start;
}

/* ---------- app rail --------------------------------------------------------
   On a narrow screen a section index would show the rail and the card grid one
   above the other, listing the same apps twice. The cards win there; the rail
   stays for app pages, where it is the only way sideways. */
[data-render="home"] .rail,
[data-render="section"] .rail{display:none}

.rail h2{
  font-size:.62rem;font-weight:700;letter-spacing:.09em;text-transform:uppercase;
  color:var(--ink-3);margin:0 0 .55rem;padding:0 .1rem;
}
.rail ul{list-style:none;margin:0;padding:0}
.rail ul ul{margin-top:1px}
.rail li + li{margin-top:1px}
.rail .item{
  display:flex;flex-wrap:wrap;align-items:baseline;gap:.35rem;
  padding:.42rem .6rem;border-radius:var(--r-btn);
  font-size:.85rem;font-weight:550;color:var(--ink-2);text-decoration:none;
  border-left:2px solid transparent;
}
.rail .item .t{min-width:0;overflow-wrap:anywhere}
.rail a.item:hover{background:var(--surface);color:var(--ink)}
.rail .item.active{background:var(--surface);color:var(--npb-red);
  border-left-color:var(--npb-red);box-shadow:var(--shadow-panel)}
.rail .item.disabled{color:var(--ink-3);cursor:default}
.rail .child{margin-left:1.05rem}
/* Same word as the card and the stub — the vocabulary is the registry's, and
   there is exactly one of it (ARCHITECTURE.md §4). */
.rail .item .state{
  margin-left:auto;font-size:.58rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  color:var(--ink-3);background:var(--plane-2);border-radius:5px;padding:.1rem .34rem;
  flex:0 0 auto;align-self:center;
}
.rail .empty{font-size:.78rem;color:var(--ink-3);padding:.42rem .6rem}

/* ---------- main ------------------------------------------------------------ */
.main{min-width:0}
.page-h{margin:0 0 1.4rem}
.page-h h1{font-size:1.4rem;font-weight:650;letter-spacing:-.012em;margin:0}

/* A RAISED LETTER IN A NAME, not a footnote reference. An app's mark may end in
   one — VaMoS with a superscript S, against the VaMoS X to come — and the
   browser's default <sup> is built for citations: it sits too high, too large,
   and it pushes the line box open, which moves everything under a heading down
   by a couple of pixels. These three values are the fix, and the line-height:0
   is the one that stops the shove.

   It is markup rather than a character on purpose: Corporate S carries no
   superscript-letter glyph (U+02E2 and U+02E3 were both read out of the font
   and are absent), so ˢ would be drawn by whatever font the browser reached
   for next — one letter of a brand mark, in the wrong face. */
h1 sup,h2 sup,.nm sup,.t sup,.lockup-fb sup{
  font-size:.62em;line-height:0;vertical-align:.45em;letter-spacing:0;font-weight:inherit;
}
.page-h .lede{color:var(--ink-3);font-size:.9rem;margin:.45rem 0 0;max-width:46rem}

/* ---------- panels ----------------------------------------------------------- */
.panel{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-panel);
  box-shadow:var(--shadow-panel);overflow:hidden;
}
.panel-h{
  display:flex;align-items:center;justify-content:space-between;gap:.5rem;
  padding:.7rem .95rem;border-bottom:1px solid var(--line);
}
.panel-h .ttl{font-size:.74rem;font-weight:700;letter-spacing:.075em;text-transform:uppercase;
  color:var(--navy)}
.panel-b{padding:1rem .95rem}
.sections{margin-top:2rem}

/* ---------- cards ------------------------------------------------------------
   Flex, not grid, and the column count comes from data-count.

   A fixed track count cannot balance the item counts this site actually has: at
   four items a four-column grid leaves one orphan, and at five it leaves a hole
   in the last row. Flex items that grow have neither problem — the last row
   always fills — and data-count picks a basis that makes the split a sensible
   one rather than whatever the width happens to allow.

     3 items -> 3        4 items -> 2 + 2        5 items -> 3 + 2

   The count is the registry's, written in by tools/gen-pages.mjs. */
.cards{
  --card-gap:.9rem;
  display:flex;flex-wrap:wrap;gap:var(--card-gap);
  list-style:none;margin:0;padding:0;
}
.cards > li{display:flex;min-width:0;flex:1 1 100%}

.card{
  display:flex;flex-direction:column;gap:.3rem;width:100%;
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-card);
  box-shadow:var(--shadow-panel);padding:1rem 1.1rem 1.1rem;text-decoration:none;
  transition:border-color .14s,box-shadow .14s,transform .14s;
}
a.card:hover{border-color:#c7ccd8;box-shadow:var(--shadow);transform:translateY(-1px)}
.card .nm{font-size:1rem;font-weight:650;letter-spacing:-.005em;color:var(--ink);
  display:flex;align-items:center;gap:.42rem;overflow-wrap:anywhere}
.card .nm .lock{width:12px;height:12px}
.card .sub{font-size:.8rem;color:var(--ink-3);overflow-wrap:anywhere}
.card.is-planned .nm{color:var(--ink-2)}
.card > .state{
  margin-top:auto;padding-top:.7rem;
  font-size:.6rem;font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3);
}

/* Who a gated pillar is for, in one neutral line, and then — only for a
   visitor who already holds it — how it is actually entered. The second is
   fetched from the pillar's protected gate.json and starts hidden. It is never
   in this bundle. */
.card .gate{
  margin-top:.6rem;padding:.4rem .55rem;border-radius:var(--r-btn);
  background:var(--plane-2);color:var(--ink-2);
  font-size:.72rem;font-weight:550;line-height:1.4;
}

/* ---------- child apps in the grid -------------------------------------------
   A child keeps its own route, its own lockup and its own publication date. It
   does not keep its own card: at equal size it read as a sibling of the thing
   it belongs to. It renders as an indented row inside the parent's card, which
   is what the sidebar does with an indent — a row, not a nested card, because
   cards inside cards are out (CLAUDE.md). */
.card .kids{
  list-style:none;margin:.85rem 0 0;padding:.7rem 0 0;
  border-top:1px solid var(--line-2);
}
.card .kids li + li{margin-top:.1rem}
.kid{
  display:flex;flex-wrap:wrap;align-items:baseline;gap:.2rem .5rem;
  padding:.32rem .5rem .32rem .7rem;
  border-left:2px solid var(--line);border-radius:0 var(--r-btn) var(--r-btn) 0;
  font-size:.85rem;font-weight:600;color:var(--ink-2);text-decoration:none;
}
a.kid:hover{background:var(--plane);border-left-color:var(--npb-red);color:var(--ink)}
.kid .t{min-width:0;overflow-wrap:anywhere}
.kid .cs{font-size:.72rem;font-weight:400;color:var(--ink-3)}
.kid .state{
  margin-left:auto;font-size:.55rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  color:var(--ink-3);background:var(--plane-2);border-radius:5px;padding:.1rem .32rem;
  flex:0 0 auto;align-self:center;
}

/* ---------- stub state --------------------------------------------------------
   Milestone 1 ships every app as a page carrying its lockup and a coming-soon
   state. Nothing here describes what an app does — that copy does not exist
   yet and must not be invented. */
.stub{
  background:var(--surface);border:1px solid var(--line);border-radius:var(--r-card);
  box-shadow:var(--shadow-panel);
  padding:clamp(1.6rem,6vw,3.4rem);text-align:center;
}
.stub .lockup,.stub .lockup-fb{margin:0 auto 1.5rem}
.stub h1{font-size:1.3rem;font-weight:650;letter-spacing:-.01em;margin:0;overflow-wrap:anywhere}
.stub .long{color:var(--ink-3);font-size:.88rem;margin:.4rem 0 0}
.stub .state{
  display:inline-block;margin:1.4rem 0 0;
  font-size:.66rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-3);background:var(--plane-2);border-radius:20px;padding:.3rem .8rem;
}
.stub .rule{height:1px;background:var(--line);margin:1.6rem auto 0;max-width:9rem}
.stub .back{display:inline-block;margin-top:1.3rem;font-size:.8rem;font-weight:550;
  color:var(--ink-3);text-decoration:none}
.stub .back:hover{color:var(--npb-red)}

/* ---------- dense content ------------------------------------------------------
   For apps, not for the shell. A wide table scrolls inside its own box; it must
   never be the reason the page itself scrolls sideways (ARCHITECTURE.md §11).
   Wrap the table, do not style the page around it. */
.scroll-x{overflow-x:auto;-webkit-overflow-scrolling:touch;max-width:100%}
.scroll-x > table{min-width:max-content}
table{border-collapse:collapse}
th,td{text-align:left}
td.num,th.num{text-align:right;font-variant-numeric:tabular-nums}

/* ---------- footer -------------------------------------------------------------- */
.foot{
  flex:0 0 auto;
  max-width:var(--wrap);width:100%;margin:0 auto;
  padding:1.3rem var(--gutter) 2rem;
  border-top:1px solid var(--line);
  color:var(--ink-3);font-size:.68rem;
  display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;flex-wrap:wrap;
}
/* The header mark drops "Neue Privat Bank", so the full corporate logo lives
   here and the bank name is always present on the page. */
.foot .corp{height:28px;width:auto;display:block;opacity:.9}
.foot .legal{line-height:1.5}

/* ============================================================================
   768px — tablet
   ========================================================================= */
@media (min-width:600px){
  .cards > li{flex-basis:calc((100% - var(--card-gap)) / 2)}
  .cards[data-count="1"] > li{max-width:26rem}
  .foot{align-items:center}
  .foot .legal{text-align:right}
}

@media (min-width:768px){
  .page-h h1{font-size:1.5rem}
  .tab{font-size:.84rem;padding:.75rem .9rem;gap:.45rem}
  .lg{--lockup-h:64px}
}

/* ============================================================================
   Wide — the rail becomes a column and the grid gets its third track
   ========================================================================= */
@media (min-width:900px){
  body.has-rail .layout{
    grid-template-columns:15.5rem minmax(0,1fr);
    gap:clamp(1.4rem,4vw,3rem);
  }
  [data-render="home"] .rail,
  [data-render="section"] .rail{display:block}
  body.has-rail .rail{position:sticky;top:calc(var(--chrome-h,110px) + 1rem)}
}

@media (min-width:1024px){
  /* Default three across; the counts that do not divide by three get their own
     basis so the last row is never short. See the note above .cards. */
  .cards > li{flex-basis:calc((100% - var(--card-gap) * 2) / 3)}
  .cards[data-count="1"] > li,
  .cards[data-count="2"] > li,
  .cards[data-count="4"] > li{flex-basis:calc((100% - var(--card-gap)) / 2)}
  .cards[data-count="7"] > li,
  .cards[data-count="8"] > li{flex-basis:calc((100% - var(--card-gap) * 3) / 4)}
  .layout{padding-bottom:3.5rem}
}

/* ---------- print ---------------------------------------------------------------
   Chrome only. The hard-won A4 pagination block belongs to apps that produce a
   report (ARCHITECTURE.md §9) and is not duplicated here. */
@media print{
  .chrome,.rail,.skip{display:none}
  body{background:#fff}
  .layout{display:block;padding:0}
  .stub,.panel,.card{box-shadow:none}
  *{print-color-adjust:exact;-webkit-print-color-adjust:exact}
}

/* ---------- a card whose title carries the link -------------------------------
   A card with child rows cannot itself be an anchor, so its NAME is the link.
   Stretching that anchor over the whole card makes it behave like the childless
   cards, which are one big link — otherwise the only clickable thing in the
   card is the child row, and the card's own subject is dead.

   The child rows sit above it so they stay independently clickable. */
.card.has-kids{position:relative}
.card a.nm{text-decoration:none;color:inherit}
.card a.nm::after{content:"";position:absolute;inset:0;border-radius:var(--r-card)}
.card.has-kids:hover{border-color:#c7ccd8;box-shadow:var(--shadow)}
.card.has-kids:hover a.nm{color:var(--npb-red)}
.card a.nm:focus-visible{outline:none}
.card.has-kids:focus-within{border-color:var(--npb-red)}
/* Above the stretched link, or they cannot be clicked at all. */
.card .kids{position:relative;z-index:1}
