/*
  Site shell — global header, left sidebar, page layout.
  Pages opt in by adding the .site-shell wrapper and the two placeholder
  elements; site.js injects the partials. Sonia-fwd-curve aesthetic carried
  through (cards/KPIs/grids live in sonia-fwd-curve.css).
*/

/* Design tokens (the --pt-* palette, shell aliases, layout, shadows) now
   live in tokens.css — the single source of truth, loaded before this file
   on every page. Edit values there; this file just consumes them. */

html, body {
  background: var(--shell-bg);
  color: var(--shell-fg);
  margin: 0;
  padding: 0;
}

body.site-shell-ready { /* hook for any post-load reveal animations */ }

/* ---------- top header ----------
   Full-width white header bar sitting above both sidebar and content.
   The navy chrome of the sidebar (below) lines up with the top of the
   content area on the right, so the page has a clean two-row layout:
   white header / navy nav + light content. */
header#site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  height: var(--shell-header-h);
  background: #ffffff;
  /* Subtle navy delineation between header and body — translucent
     brand navy reads as a quiet line on the white header without
     fighting the orange brand mark. */
  border-bottom: 1px solid rgba(10, 37, 64, 0.18);
  /* The as-of strip is already contained by .site-header-context (overflow:
     hidden) + its own max-height:40px, so the header itself must NOT clip.
     An overflow:hidden here also clipped the account and "more" dropdown
     menus (they open below the header), hiding them — which broke Sign out
     on desktop. */
  overflow: visible;
}

.site-header-inner {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0 1rem;
}

.site-brand {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  text-decoration: none;
  color: var(--shell-fg);
  /* Reserve the sidebar's horizontal space — minus the header's own
   * left padding — so anything inside .site-header-context starts at
   * the same x-coordinate as the page's main content. */
  min-width: calc(var(--shell-sidebar-w) - 1rem);
}

.site-brand-logo {
  height: 32px;
  width: auto;
  display: block;
}

/* Compact chart mark (the logo's bars+arrow, via apple-touch-icon.png) shown
   in place of the wordmark on mobile tool pages (see body.has-header-title
   rules in the ≤900px block). Hidden otherwise. */
.site-brand-mark {
  display: none;
  height: 30px;
  width: auto;
}

.site-brand-text {
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.site-brand-text-accent {
  color: var(--shell-accent);
  font-weight: 600;
}

/* === "Who we are" trust touchpoint =================================
 * A quiet trigger injected by site.js into each academy strip, plus a
 * shared modal (aims / people / goal + CTA). */
/* Framed like the academy question pills (same font + pill shape + padding)
   so it sits consistently in the row, but navy-outlined with a people icon
   so it reads as a distinct "about us" action, not a course question. */
.meet-trigger {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: inherit; font-size: 0.82rem; font-weight: 600;
  color: var(--pt-navy, #0a2540);
  background: #ffffff;
  border: 1px solid rgba(10, 37, 64, 0.18);
  border-radius: 999px;
  padding: 5px 13px;
  cursor: pointer; white-space: nowrap;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.meet-trigger:hover {
  background: rgba(10, 37, 64, 0.04);
  border-color: rgba(10, 37, 64, 0.35);
  color: var(--pt-navy, #0a2540);
}
.meet-trigger .bi { color: var(--pt-orange, #fd7e14); font-size: 0.9rem; }

.meet-modal {
  position: fixed; inset: 0; z-index: 1100;
  display: flex; align-items: center; justify-content: center; padding: 20px;
}
.meet-modal[hidden] { display: none; }
.meet-modal-backdrop {
  position: absolute; inset: 0;
  background: rgba(10, 37, 64, 0.55); backdrop-filter: blur(2px);
}
.meet-modal-card {
  position: relative; background: #fff; border-radius: 14px;
  width: 100%; max-width: 540px; max-height: calc(100vh - 40px); overflow-y: auto;
  padding: 32px 32px 28px; box-shadow: 0 20px 60px rgba(10, 37, 64, 0.30);
  animation: meet-in 0.18s ease-out;
}
@keyframes meet-in {
  from { opacity: 0; transform: translateY(8px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}
.meet-modal-close {
  position: absolute; top: 14px; right: 14px; width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: none; cursor: pointer;
  color: var(--pt-muted, #6c757d); border-radius: 8px;
  transition: background 0.12s, color 0.12s;
}
.meet-modal-close:hover { background: rgba(10, 37, 64, 0.06); color: var(--pt-navy, #0a2540); }
.meet-modal-close .bi { font-size: 1.05rem; }
.meet-modal-eyebrow {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.95rem; font-weight: 700; color: var(--pt-navy, #0a2540); margin-bottom: 10px;
}
.meet-modal-eyebrow .bi-mortarboard-fill { color: var(--pt-orange, #fd7e14); font-size: 1.05rem; }
.meet-brand .accent { color: var(--pt-orange, #fd7e14); }
.meet-modal-title {
  font-size: 1.5rem; font-weight: 800; color: var(--pt-navy, #0a2540);
  margin: 0 0 8px; letter-spacing: -0.005em;
}
.meet-modal-intro { font-size: 0.95rem; line-height: 1.55; color: var(--pt-text, #243240); margin: 0 0 18px; }
.meet-section { margin-bottom: 14px; }
.meet-section-head {
  font-size: 0.72rem; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--pt-orange, #fd7e14); margin-bottom: 4px;
}
.meet-section p { font-size: 0.9rem; line-height: 1.55; color: var(--pt-text, #243240); margin: 0; }
/* LinkedIn signifiers in the people section */
.meet-linkedin {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 12px;
}
.meet-linkedin-label { font-size: 0.8rem; font-weight: 600; color: var(--pt-muted, #6c757d); }
.meet-linkedin-row { display: inline-flex; gap: 6px; }
.meet-li {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 50%;
  background: rgba(10, 102, 194, 0.10); color: #0a66c2;   /* LinkedIn blue */
  text-decoration: none; transition: background 0.12s, color 0.12s, transform 0.12s;
}
.meet-li:hover { background: #0a66c2; color: #fff; transform: translateY(-1px); }
.meet-li .bi { font-size: 1rem; }

/* Dual CTA row — Explore (primary) + Contact us (secondary). */
.meet-modal-actions { display: flex; gap: 10px; margin-top: 20px; flex-wrap: wrap; }
.meet-modal-cta {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  flex: 2 1 200px;
  background: var(--pt-orange, #fd7e14); color: #fff; font-size: 1rem; font-weight: 700;
  border-radius: 10px; padding: 13px 22px; text-decoration: none;
  box-shadow: 0 6px 18px rgba(253, 126, 20, 0.28);
  transition: background 0.15s, transform 0.12s, box-shadow 0.15s;
}
.meet-modal-cta .accent { color: #fff; }
.meet-modal-cta:hover {
  background: #e06b00; color: #fff; text-decoration: none;
  transform: translateY(-1px); box-shadow: 0 8px 22px rgba(253, 126, 20, 0.38);
}
.meet-modal-cta .bi { font-size: 1.05rem; }
.meet-modal-cta-secondary {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  flex: 1 1 140px;
  background: transparent; color: var(--pt-navy, #0a2540);
  border: 1px solid rgba(10, 37, 64, 0.20);
  font-size: 1rem; font-weight: 700; text-decoration: none;
  border-radius: 10px; padding: 13px 18px; white-space: nowrap;
  transition: background 0.12s, border-color 0.12s;
}
.meet-modal-cta-secondary:hover {
  background: rgba(10, 37, 64, 0.04); border-color: rgba(10, 37, 64, 0.35);
  color: var(--pt-navy, #0a2540); text-decoration: none;
}
.meet-modal-cta-secondary .bi { color: var(--pt-orange, #fd7e14); font-size: 0.95rem; }
body.meet-modal-open { overflow: hidden; }
@media (max-width: 540px) {
  .meet-modal-card { padding: 26px 22px 22px; }
  .meet-modal-title { font-size: 1.3rem; }
}

/* Header context slot — host for the page title (and historically for
 * the predictor's AS OF strip). Takes the flexible space between the
 * brand and the right-side action pills, content LEFT-aligned. */
.site-header-context {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: baseline;               /* strapline sits on the title's text baseline, inline — not vertically centred against it */
  justify-content: flex-start;         /* left-aligned, was centred */
  gap: 8px;
  padding: 0 18px;
  font-size: 12px;
  color: #6c757d;
  overflow: hidden;
}
.site-header-context:empty { display: none; }

/* Page title in the header bar — matches the sidebar nav text.
 * Two-tone wordmark (PWLB + orange accent) for branded pages,
 * plain text for the rest. Left-aligned with the page content via
 * the .site-brand min-width above. */
.site-page-title-bar {
  margin: 0;
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--pt-navy, #0a2540);
  line-height: 1;
  white-space: nowrap;
  /* Never shrink or wrap — the title always stays full-size, on the same
   * row as the logo and header pills. Any space pressure is absorbed by
   * the as-of strip beside it (which shrinks/clips instead). */
  flex: 0 0 auto;
  /* Always vertically centred on the header row itself, independent of
   * whatever sits beside it (a one-line strap on PWLBpredict, a taller
   * 2-line as-of strip on the data pages) — so the title sits at the
   * same vertical position on every page. */
  align-self: center;
}
.site-page-title-bar .site-brand-text-accent {
  color: var(--pt-orange, #fd7e14);
  font-weight: 700;
}

/* Optional muted strapline to the right of the title — says what the
 * page is (value-led). Thin vertical rule separates it from the title.
 * Subordinate weight/colour so it never competes with the heading.
 * Hidden below 900px so it doesn't crowd the title on narrow screens. */
.site-page-strap {
  display: inline-flex;
  align-items: center;
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--pt-muted, #6c757d);
  line-height: 1.2;
  white-space: nowrap;
  padding-left: 12px;
  margin-left: 2px;
  border-left: 1px solid var(--pt-line, #e9ecef);
  /* Match the title's centring (align-self: center) rather than the
   * parent's baseline default — otherwise, with the title opted out of
   * baseline alignment, this ends up aligning to its own lone baseline
   * and visibly drifts below the title. */
  align-self: center;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
@media (max-width: 900px) {
  .site-page-strap { display: none; }
}

/* Anti-layout-shift: hide the freshness strip in its in-page position and
 * reveal it only once site.js relocates it into the header — prevents the
 * content→header jump on load. !important beats Bootstrap's .d-flex on the
 * PWLB strip. (If site.js fails to run, the strip stays hidden — acceptable;
 * it's only freshness metadata.) */
[data-header-asof] { display: none !important; }
.site-header-context [data-header-asof] { display: flex !important; }

/* As-of freshness strip relocated into the header (PWLB Rates + SONIA +
 * PWLB Activity + Economic Calendar) by site.js's relocateHeaderAsof —
 * it sits where the predictor's strapline sits, so it doesn't eat a
 * content row. Compact, baseline-aligned, divider from the title; the
 * source flows inline (its in-page right-push is cancelled).
 *
 * The title itself never shares this box or shrinks (flex: 0 0 auto
 * above) — it's always fully visible alongside the logo and pills.
 * This strip gets whatever space is left and wraps by whole item (each
 * child stays on one line — see the `> *` rule below — so it never
 * breaks mid-phrase into a vertical word list), capped at 2 wrapped
 * lines; anything beyond that is clipped rather than pushing the
 * header taller or spilling under the pills. */
.site-header-context [data-header-asof] {
  margin: 0 0 0 2px !important;          /* kill the in-content margin-bottom */
  padding-left: 12px;
  border-left: 1px solid var(--pt-line, #e9ecef);
  flex: 1 1 auto;
  flex-wrap: wrap !important;
  align-content: flex-start;
  align-self: center;                    /* match the title's centring, not the parent's baseline */
  row-gap: 3px;
  line-height: 18px;
  min-width: 0;
  /* Cap at 2 wrapped lines (18px + 3px gap + 18px, rounded up) — now
   * that the "Standard Rate" badge has moved out of this strip onto its
   * own pages, every remaining child is plain text/links, so this only
   * needs enough room for ordinary text at this font-size, not a
   * padded/bordered badge. Anything beyond 2 lines (e.g. a very narrow
   * desktop width) is simply hidden rather than adding a 3rd line. */
  max-height: 40px;
  overflow: hidden;
}
/* Each direct child (the "as of" group, the separator, the "Nd ago"
 * note, the Source: link) wraps as one unbreakable unit onto the next
 * line rather than breaking word-by-word within itself. */
.site-header-context [data-header-asof] > * {
  white-space: nowrap;
}
.site-header-context [data-header-asof] .ms-auto,
.site-header-context [data-header-asof] .sfc-asof-trail,
.site-header-context [data-header-asof] .pa-asof-trail {
  margin-left: 8px !important;           /* was auto — flow source inline, not far-right */
}
/* Hidden below 900px (like the predictor strapline) so it never crowds
 * the title on narrow screens. */
@media (max-width: 900px) {
  .site-header-context [data-header-asof] { display: none !important; }
}

/* (Legacy rules from when the strip lived in the header — kept but no
 * longer load-bearing. The strip is now moved into .pt-commentary by
 * the predictor's inline script.) */
.site-header-context .pt-asof-strip {
  margin: 0;
  flex-wrap: nowrap;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
}
.site-header-context .pt-asof-dot { display: none; }

.site-header-actions {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
}

.site-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.4rem 1rem;
  border-radius: 999px;
  font-size: 0.82rem;
  font-weight: 600;
  text-decoration: none;
  transition: filter 0.12s ease, transform 0.08s ease;
}

.site-btn:active { transform: translateY(1px); }
.site-btn:hover { filter: brightness(1.06); }

/* About us — ghost variant (lightest weight: informational, not a CTA). */
.site-btn-about {
  background: transparent;
  color: var(--shell-fg);
  border: 1px solid rgba(10, 37, 64, 0.20);
}
.site-btn-about:hover {
  background: rgba(10, 37, 64, 0.04);
  border-color: rgba(10, 37, 64, 0.35);
  color: var(--shell-fg);
  filter: none;
}

/* Contact us — ghost variant, same weight as About us (both
   informational/company links). Routes to contact.html. */
.site-btn-contactus {
  background: transparent;
  color: var(--shell-fg);
  border: 1px solid rgba(10, 37, 64, 0.20);
}
.site-btn-contactus:hover {
  background: rgba(10, 37, 64, 0.04);
  border-color: rgba(10, 37, 64, 0.35);
  color: var(--shell-fg);
  filter: none;
}

/* Pricing — ghost variant, same weight as Contact. Moved from the sidebar
   tool-rail into the header (commercial page, not a data tool). */
.site-btn-pricing {
  background: transparent;
  color: var(--shell-fg);
  border: 1px solid rgba(10, 37, 64, 0.20);
}
.site-btn-pricing:hover {
  background: rgba(10, 37, 64, 0.04);
  border-color: rgba(10, 37, 64, 0.35);
  color: var(--shell-fg);
  filter: none;
}

/* Briefings — secondary CTA: orange OUTLINE. Sits between the ghost info
   pills (About/Contact) and the solid-orange primary CTA (Sign in), so it
   draws the eye without competing for "the action." Fills solid on hover.
   Placeholder action for now. */
.site-btn-briefings {
  background: transparent;
  color: var(--pt-orange, #fd7e14);
  border: 1px solid var(--pt-orange, #fd7e14);
  gap: 0.45rem;
  cursor: pointer;
  font-family: inherit;
}
.site-btn-briefings:hover {
  background: var(--pt-orange, #fd7e14);
  color: #ffffff;
  border-color: var(--pt-orange, #fd7e14);
  filter: none;
}
/* "PWLB" portion of the PWLBbrief label — neutral on the default outline
   button, flips to white (matching "brief") on hover with the solid fill. */
.site-btn-briefings-brand {
  color: var(--pt-text);
}
.site-btn-briefings:hover .site-btn-briefings-brand,
.site-btn-briefings:hover .site-brand-text-accent {
  color: #ffffff;
}
/* Freshness dot — green pulsing dot to the LEFT of the label, signalling
   new content. Same live cue used across the data pages. Stays green on
   the orange hover fill (reads as a live accent). */
.site-btn-briefings-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #28a745;
  flex-shrink: 0;
  box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.5);
  animation: site-briefings-pulse 2.4s ease-out infinite;
}
@keyframes site-briefings-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(40, 167, 69, 0.5); }
  70%  { box-shadow: 0 0 0 6px rgba(40, 167, 69, 0); }
  100% { box-shadow: 0 0 0 0 rgba(40, 167, 69, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .site-btn-briefings-dot { animation: none; }
}

/* Sign in — ghost (returning users; subordinate to the solid Sign up CTA). */
.site-btn-signin {
  background: transparent;
  color: var(--shell-fg);
  border: 1px solid rgba(10, 37, 64, 0.20);
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}
.site-btn-signin i { font-size: 0.95rem; line-height: 1; }
.site-btn-signin:hover {
  background: rgba(10, 37, 64, 0.04);
  border-color: rgba(10, 37, 64, 0.35);
  color: var(--shell-fg);
  filter: none;
}

/* Sign up — solid orange (primary CTA, heaviest weight). The newcomer's
   action: this is the loudest pill in the header on every page. */
.site-btn-signup {
  background: var(--pt-orange, #fd7e14);
  color: #ffffff;
  border: 1px solid var(--pt-orange, #fd7e14);
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  white-space: nowrap;
}
.site-btn-signup i { font-size: 0.95rem; line-height: 1; }
.site-btn-signup:hover {
  color: #ffffff;
  filter: brightness(1.06);
}

/* Hide BOTH auth pills immediately for returning signed-in users.
 * body.pwlb-signed-in is set synchronously from the persisted auth flag
 * before the header partial is injected (see site.js applySignedInState),
 * so this applies the moment the pills exist — no flash while
 * auth-header.js/academy.js's async user check resolves and swaps them for
 * the account chip. */
body.pwlb-signed-in #siteSignInPill,
body.pwlb-signed-in #siteSignUpPill { display: none; }

/* Mobile "more" menu (top-right burger) — surfaces the header info/nav pills
   (Pricing, Contact, PWLBbrief, Sign in) as a normal dropdown list on phones,
   where those pills are hidden. Hidden on desktop, which shows the pills. */
.site-hdr-menu { display: none; position: relative; }
.site-hdr-menu-btn {
  /* Match the left nav toggle (.site-mobile-nav-toggle) exactly so the two
     header burgers are the same size and style. */
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; padding: 0; cursor: pointer;
  background: transparent; color: var(--shell-fg);
  border: 1px solid var(--shell-border); border-radius: 8px;
  font-size: 18px; line-height: 1;
}
.site-hdr-menu-btn:hover { background: rgba(10, 37, 64, 0.04); }
.site-hdr-menu-panel {
  /* position:fixed (not absolute) so the dropdown escapes the header's
     overflow:hidden clip — the header hard-clips at its own height, which
     would otherwise hide this panel that hangs below it. Anchored to the
     viewport just under the fixed-height header, near the right edge. */
  position: fixed; top: calc(var(--shell-header-h) + 8px); right: 0.75rem; z-index: 1200;
  min-width: 200px; padding: 6px; display: flex; flex-direction: column; gap: 2px;
  background: #fff; border: 1px solid var(--pt-line, #dde3ec);
  border-radius: 10px; box-shadow: 0 10px 26px rgba(10, 37, 64, 0.16);
}
/* Respect the `hidden` attribute — the base `display: flex` above would
   otherwise override the UA `[hidden] { display: none }` and leave the menu
   permanently open. */
.site-hdr-menu-panel[hidden] { display: none; }
.site-hdr-menu-panel a,
.site-hdr-menu-panel button {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 10px 12px; border-radius: 7px; border: none; background: transparent;
  font-family: inherit; font-size: 0.95rem; font-weight: 600; text-align: left;
  color: var(--pt-navy, #0a2540); text-decoration: none; cursor: pointer;
}
.site-hdr-menu-panel a:hover,
.site-hdr-menu-panel button:hover { background: rgba(10, 37, 64, 0.05); }
.site-hdr-menu-panel i {
  font-size: 1.05rem; color: var(--pt-orange, #fd7e14);
  width: 20px; text-align: center; flex: none;
}
/* The Sign-up item is the primary CTA — solid orange, like the header pill. */
.site-hdr-menu-panel a.site-hdr-menu-cta { background: var(--pt-orange, #fd7e14); color: #fff; }
.site-hdr-menu-panel a.site-hdr-menu-cta:hover { background: #e06b00; }
.site-hdr-menu-panel a.site-hdr-menu-cta i { color: #fff; }
/* Signed-in users don't need the menu's auth links (account chip covers them). */
body.pwlb-signed-in #siteHdrMenuSignup,
body.pwlb-signed-in #siteHdrMenuSignin { display: none; }

/* Uniform width for the top-right header pills. The min-width exceeds the
   widest label, so each pill renders at the same width; .site-btn already
   centres its content. */
.site-header-actions .site-btn-about,
.site-header-actions .site-btn-pricing,
.site-header-actions .site-btn-contactus,
.site-header-actions .site-btn-briefings,
.site-header-actions .site-btn-signin,
.site-header-actions .site-btn-signup {
  min-width: 116px;   /* exceeds the widest (Briefings + its dot) so all pills match */
}

/* Legacy class kept for backwards-compat — was the Contact pill that
   we retired and is now repurposed by some pages. */
.site-btn-contact {
  background: var(--shell-contact);
  color: #fff;
}

.site-btn-academy {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.4rem 0.85rem;
  background: var(--pt-orange-soft);
  color: var(--pt-orange);
  border: 1px solid rgba(253, 126, 20, 0.30);
  animation: site-btn-academy-pulse 2.4s ease-out infinite;
}

.site-btn-academy i { font-size: 0.95rem; line-height: 1; }

.site-btn-academy:hover {
  background: var(--pt-orange);
  color: #ffffff;
  border-color: var(--pt-orange);
  filter: none;
  animation: none;
}

@keyframes site-btn-academy-pulse {
  0%   { box-shadow: 0 0 0 0   rgba(253, 126, 20, 0.45); }
  70%  { box-shadow: 0 0 0 8px rgba(253, 126, 20, 0);    }
  100% { box-shadow: 0 0 0 0   rgba(253, 126, 20, 0);    }
}

/* Honour reduced-motion preference */
@media (prefers-reduced-motion: reduce) {
  .site-btn-academy { animation: none; }
}

/* ---------- layout ---------- */
.site-layout {
  display: grid;
  grid-template-columns: var(--shell-sidebar-w) 1fr;
  min-height: calc(100vh - var(--shell-header-h));
}

/* ---------- sidebar — navy chrome, compact, collapsible ----------
   Width is driven by --shell-sidebar-w. When body.sidebar-collapsed is set,
   the sidebar shrinks to icon-only. State persisted in localStorage by site.js.
*/

/* --shell-sidebar-w-collapsed lives in tokens.css */

aside#site-sidebar {
  background: linear-gradient(180deg, #0a2540 0%, #143659 60%, #1a4775 100%);
  border-right: none;
  padding: 0.1rem 0.55rem 1rem;     /* tighter top — was 0.5rem, collapse toolbar removed */
  overflow-y: auto;
  overflow-x: hidden;
  color: #ffffff;
  /* Sit below the sticky header. Sticky so it scrolls with the page
     while keeping its top aligned with the bottom of the header bar. */
  position: sticky;
  top: var(--shell-header-h);
  align-self: start;
  height: calc(100vh - var(--shell-header-h));
}

/* Subtle radial highlight at the top — gives the navy a touch of depth */
aside#site-sidebar::before {
  content: "";
  position: absolute;
  top: -50px;
  right: -30px;
  width: 240px;
  height: 200px;
  background: radial-gradient(circle, rgba(253, 126, 20, 0.10) 0%, transparent 70%);
  pointer-events: none;
}

aside#site-sidebar > * { position: relative; z-index: 1; }

/* Collapse toolbar — sits above the first nav group */
.site-nav-toolbar {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  padding: 0.25rem 0.35rem 0.5rem;
  margin-bottom: 0.25rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.site-nav-collapse-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: none;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.04);
  color: rgba(255, 255, 255, 0.65);
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}

.site-nav-collapse-btn:hover {
  background: rgba(255, 255, 255, 0.10);
  color: #ffffff;
}

.site-nav-collapse-btn i {
  font-size: 1rem;
  line-height: 1;
}

.site-nav-group-label {
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.42);
  padding: 0.7rem 0.75rem 0.35rem;
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
}
/* Extra breathing room above the first group label (Market data) so it
 * isn't hard up against the top of the sidebar / header divider. */
.site-nav > .site-nav-group-label:first-child {
  padding-top: 1.4rem;
}

/* Admin nav item — hidden unless the signed-in user is an admin. site.js
 * adds body.pwlb-admin from the cached user's is_admin flag, revealing it. */
.site-nav-admin-item { display: none; }
body.pwlb-admin .site-nav-admin-item { display: block; }
/* Org (training) admin nav item — top of the sidebar, shown only to granted
 * org-admins. body-scoped so it overrides .site-nav-list's display. */
body .site-nav-orgadmin-item { display: none; }
body.pwlb-org-admin .site-nav-orgadmin-item { display: block; }
/* Org members already have access via their org's subscription — hide the
 * Pricing pill for them (body.pwlb-in-org from the user's org). */
body.pwlb-in-org .site-btn-pricing { display: none; }

.site-nav-list {
  list-style: none;
  margin: 0 0 0.4rem;
  padding: 0;
}

.site-nav-list li { margin: 0; }

/* Academy category sub-nav — mirrors the in-app pill bar under PWLBacademy.
   Desktop only: hidden on the mobile drawer and when the rail is collapsed. */
/* Collapsed by default — the academy category sub-nav only expands when the
   user is actually in the academy (the app/concierge, data-page="academy-chat",
   or the hub, data-page="academy-start"). On every other page it stays
   collapsed so the rail isn't cluttered with academy views. */
.site-nav-sublist {
  display: none;
  list-style: none;
  margin: 1px 0 8px 1.55rem;
  padding-left: 0.55rem;
  border-left: 1px solid rgba(255, 255, 255, 0.12);
}
body[data-page="academy-chat"] .site-nav-sublist,
body[data-page="academy-start"] .site-nav-sublist { display: block; }
.site-nav-sublist a {
  display: block;
  padding: 0.28rem 0.55rem;
  border-radius: 6px;
  font-size: 0.76rem;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.60);
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background 0.12s ease, color 0.12s ease;
}
.site-nav-sublist a:hover { background: rgba(255, 255, 255, 0.06); color: #ffffff; }
/* Active view — orange highlight mirroring the top-level nav (.site-nav-list a.active). */
.site-nav-sublist a.active {
  background: rgba(253, 126, 20, 0.14);
  color: #ffffff;
  font-weight: 600;
  box-shadow: inset 3px 0 0 var(--pt-orange);
}
@media (max-width: 900px) { .site-nav-sublist { display: none !important; } }
body.sidebar-collapsed .site-nav-sublist { display: none !important; }

.site-nav-list a {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.42rem 0.7rem;
  margin: 0.08rem 0;
  border-radius: 7px;
  font-size: 0.82rem;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.78);
  text-decoration: none;
  white-space: nowrap;
  /* No overflow:hidden here — the inner span handles ellipsis truncation,
     and removing it lets the collapsed-state tooltip (::after) render
     outside the link bounds. */
  transition: background 0.12s ease, color 0.12s ease;
}

.site-nav-list a i {
  color: rgba(255, 255, 255, 0.55);
  font-size: 1rem;
  width: 1.1rem;
  flex: 0 0 1.1rem;
  text-align: center;
  transition: color 0.12s ease;
}

.site-nav-list a > span:not(.new-badge):not(.soon-badge) {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.site-nav-list a:hover {
  background: rgba(255, 255, 255, 0.06);
  color: #ffffff;
}
.site-nav-list a:hover i {
  color: rgba(255, 255, 255, 0.85);
}

/* Button variant of nav items (e.g. PWLBbrief modal trigger) — mirrors <a> style exactly */
.site-nav-list .site-nav-btn {
  width: 100%; background: none; border: none; cursor: pointer; text-align: left;
  font-family: inherit;
  position: relative;
  display: flex; align-items: center; gap: 0.7rem;
  padding: 0.42rem 0.7rem; margin: 0.08rem 0;
  border-radius: 7px; font-size: 0.82rem; font-weight: 500;
  color: rgba(255, 255, 255, 0.78); white-space: nowrap;
  transition: background 0.12s ease, color 0.12s ease;
}
.site-nav-list .site-nav-btn i {
  color: rgba(255, 255, 255, 0.55); font-size: 1rem;
  width: 1.1rem; flex: 0 0 1.1rem; text-align: center;
  transition: color 0.12s ease;
}
.site-nav-list .site-nav-btn > span { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.site-nav-list .site-nav-btn:hover { background: rgba(255, 255, 255, 0.06); color: #ffffff; }
.site-nav-list .site-nav-btn:hover i { color: rgba(255, 255, 255, 0.85); }
.site-nav-list .site-nav-btn .site-brand-text-accent { color: var(--pt-orange, #fd7e14); }
/* Hide label when sidebar is collapsed */
body.sidebar-collapsed .site-nav-list .site-nav-btn > span { display: none; }
body.sidebar-collapsed .site-nav-list .site-nav-btn { justify-content: center; padding: 0.55rem; }

.site-nav-list a.active {
  background: rgba(253, 126, 20, 0.14);
  color: #ffffff;
  font-weight: 600;
  box-shadow: inset 3px 0 0 var(--pt-orange);
}
.site-nav-list a.active i {
  color: var(--pt-orange);
}

/* NEW badge — small orange pill */
.site-nav-list a .new-badge,
.site-nav-list .site-nav-btn .new-badge {
  display: inline-block;
  margin-left: auto;
  padding: 1px 6px;
  font-size: 12px;
  line-height: 1.5;
  font-weight: 700;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: #ffffff;
  background: var(--pt-orange);
  border-radius: 3px;
  flex: 0 0 auto;
}

/* SOON badge — mint pill, signals preview / coming-soon (Curve) */
.site-nav-list a .soon-badge {
  display: inline-block;
  margin-left: auto;
  padding: 1px 6px;
  font-size: 12px;
  line-height: 1.5;
  font-weight: 700;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: #0f172a;
  background: #2dd4a8;
  border-radius: 3px;
  flex: 0 0 auto;
}

/* ---------- collapsed state ---------- */
body.sidebar-collapsed {
  --shell-sidebar-w: var(--shell-sidebar-w-collapsed);
}

body.sidebar-collapsed .site-nav-group-label,
body.sidebar-collapsed .site-nav-list a > span,
body.sidebar-collapsed .site-nav-list a .new-badge,
body.sidebar-collapsed .site-nav-list .site-nav-btn .new-badge {
  display: none;
}

body.sidebar-collapsed .site-nav-list a {
  justify-content: center;
  padding: 0.55rem 0.5rem;
}

body.sidebar-collapsed .site-nav-toolbar {
  justify-content: center;
  padding: 0.25rem 0;
}

body.sidebar-collapsed .site-nav-toolbar .site-nav-collapse-btn i {
  transform: rotate(180deg);
}

/* Tooltip on hover when collapsed — show full link label from data-tip.
   When the sidebar is collapsed it's icon-only and short, so drop its
   own overflow:hidden so the tooltip ::after can render to the right of
   each icon. (Spec quirk: overflow-x:visible only takes effect when
   overflow-y is also non-clipping, hence both set to visible here.)
   Lift the sidebar's stacking context so its tooltips render OVER the
   main column's cards — without this, cards flush against the sidebar
   would clip the tooltip behind them. */
body.sidebar-collapsed aside#site-sidebar {
  overflow: visible;
  z-index: 50;
}

body.sidebar-collapsed .site-nav-list a[data-tip]::after {
  content: attr(data-tip);
  position: absolute;
  left: calc(100% + 10px);
  top: 50%;
  transform: translateY(-50%);
  background: var(--pt-navy);
  color: #ffffff;
  padding: 5px 10px;
  border-radius: 5px;
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  z-index: 200;
  box-shadow: 0 6px 18px rgba(10, 37, 64, 0.35);
  border: 1px solid rgba(255, 255, 255, 0.08);
  transition: opacity 0.12s ease, transform 0.12s ease;
}

body.sidebar-collapsed .site-nav-list a:hover::after {
  opacity: 1;
}

/* Canonical content wrap — every page shares the same gap above its
   first card so the navigator-to-content edge feels consistent.
   Pages used to set their own .main-content padding (predictor's
   body-scoped rule, dashboard's inline rule, the chat pages zeroing
   the wrap entirely) — all of those have been neutralised here. */
main.site-main,
.site-main-wrap {
  min-width: 0;
  padding: 16px 20px 20px;
}

/* Inner main element MUST NOT add its own padding. !important nukes
   per-page overrides (predictor.css, dashboard inline, etc.) so the
   top-of-content gap stays a single source of truth. */
.site-main-wrap > main,
.site-main-wrap > main.main-content,
.site-main-wrap > main.curve-main { padding: 0 !important; }
.site-main-wrap > main > .container-fluid,
.site-main-wrap > main > .container { padding-left: 0; padding-right: 0; }

/* ── "Add to Home Screen" hint toast — desktop only ──────────────
   Shown once per browser, dismissable, slides in from bottom-right
   after a few seconds of dwell time. Hidden entirely on mobile
   (where the suggestion would be redundant). */
.a2hs-hint {
  position: fixed;
  bottom: 20px;
  right: 20px;
  z-index: 200;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  width: min(360px, calc(100vw - 40px));
  padding: 14px 36px 14px 14px;
  background: #ffffff;
  border: 1px solid rgba(10, 37, 64, 0.10);
  border-left: 3px solid #fd7e14;
  border-radius: 10px;
  box-shadow: 0 12px 28px rgba(10, 37, 64, 0.18);
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.25s ease, transform 0.25s ease;
  pointer-events: none;
}
.a2hs-hint.is-visible {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
.a2hs-icon {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: 8px;
  background: linear-gradient(135deg, #fd7e14 0%, #c95800 100%);
  color: #ffffff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
}
.a2hs-body { min-width: 0; }
.a2hs-title {
  font-size: 0.88rem;
  font-weight: 700;
  color: #0a2540;
  margin-bottom: 2px;
}
.a2hs-text {
  font-size: 0.78rem;
  color: #495057;
  line-height: 1.4;
}
.a2hs-close {
  position: absolute;
  top: 6px;
  right: 8px;
  width: 22px;
  height: 22px;
  border: none;
  background: transparent;
  color: #6c757d;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  border-radius: 4px;
  transition: background 0.12s, color 0.12s;
}
.a2hs-close:hover { background: rgba(10, 37, 64, 0.06); color: #0a2540; }
@media (max-width: 900px) {
  /* Mobile variant — different copy ("Add to home screen") plus an
     actionable Install button on Android (via beforeinstallprompt)
     or Share-sheet instructions on iOS. Positioned centred at the
     bottom for thumb reach. */
  .a2hs-hint.a2hs-mobile {
    left: 12px;
    right: 12px;
    bottom: 12px;
    width: auto;
    padding: 14px 36px 14px 14px;
    box-shadow: 0 -2px 18px rgba(10, 37, 64, 0.18), 0 12px 28px rgba(10, 37, 64, 0.22);
  }
  /* Hide the original desktop-only variant on mobile (no .a2hs-mobile
     class) — only the mobile variant should appear here. */
  .a2hs-hint:not(.a2hs-mobile) { display: none !important; }
}
.a2hs-install-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  padding: 8px 16px;
  background: #fd7e14;
  color: #ffffff;
  border: none;
  border-radius: 999px;
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(253, 126, 20, 0.30);
  transition: background 0.12s, transform 0.08s, box-shadow 0.12s;
}
.a2hs-install-btn i { font-size: 14px; line-height: 1; }
.a2hs-install-btn:hover { background: #e6700f; box-shadow: 0 4px 12px rgba(253, 126, 20, 0.40); }
.a2hs-install-btn:active { transform: translateY(1px); }
.a2hs-ios-hint {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  padding: 6px 12px;
  background: rgba(10, 37, 64, 0.06);
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
  color: #0a2540;
}
.a2hs-ios-hint i { font-size: 15px; color: #1f6feb; }
.a2hs-ios-arrow {
  font-size: 14px;
  color: #fd7e14;
  animation: a2hs-bounce 1.4s ease-in-out infinite;
}
@keyframes a2hs-bounce {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(3px); }
}

/* Small "Take the matching course" CTA placed inside the AS-OF strip
   on the live tool pages (PWLB dashboard, SONIA forward curve,
   Predictor). Deep-links to the academy with the relevant flagship
   course preselected via ?course=<id>. Subtle by default; the orange
   accent pops on hover so it reads as a deliberate invitation. */
.pwlb-page-course-cta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  background: rgba(253, 126, 20, 0.08);
  color: #b85a02;
  border: 1px solid rgba(253, 126, 20, 0.30);
  text-decoration: none;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  transition: background .12s, color .12s, border-color .12s, transform .08s;
  white-space: nowrap;
}
.pwlb-page-course-cta i { font-size: 12px; }
.pwlb-page-course-cta:hover {
  background: #fd7e14;
  color: #ffffff;
  border-color: #fd7e14;
  transform: translateY(-1px);
}

.site-shell-error {
  padding: 0.75rem 1rem;
  color: #b91c1c;
  background: #fef2f2;
  border-bottom: 1px solid #fecaca;
  font-size: 0.85rem;
}

/* ---------- mobile-nav toggle (hamburger) ----------
   Hidden on desktop; visible inside the header at narrow widths.
   Sits to the LEFT of the brand so it reads as standard mobile UX. */
.site-mobile-nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  margin-right: 8px;
  border-radius: 8px;
  border: 1px solid var(--shell-border);
  background: transparent;
  color: var(--shell-fg);
  cursor: pointer;
  font-size: 18px;
  flex-shrink: 0;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.site-mobile-nav-toggle:hover {
  background: rgba(10, 37, 64, 0.06);
  border-color: var(--pt-navy);
  color: var(--pt-navy);
}
.site-mobile-nav-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(10, 37, 64, 0.45);
  z-index: 40;
  backdrop-filter: blur(2px);
}

/* ---------- mobile ----------
   At narrow widths the sidebar becomes a slide-in drawer. Hidden by
   default; revealed when body has class `mobile-nav-open` (toggled
   by the hamburger button). Sidebar is vertical (proper nav list,
   not a horizontal pill row), full-height, and dismissible by
   tapping the backdrop or hitting Esc. */
@media (max-width: 900px) {
  .site-layout {
    grid-template-columns: 1fr;
  }
  .site-mobile-nav-toggle {
    display: inline-flex;
  }
  aside#site-sidebar {
    /* Drawer spans the full height from the very top; the sticky header
       (z-index 100, opaque white) covers the strip above the nav, and the
       nav's own padding-top clears the header so its first item is never
       hidden. Anchoring to top:0 (rather than top:<header-h>) keeps this
       robust on iOS Safari, where a fixed element positioned at the header
       height does NOT line up with the sticky header's bottom while the URL
       bar is showing — which was cutting off the top nav item. 100dvh
       tracks the dynamic viewport so the bottom isn't clipped either. */
    position: fixed;
    top: 0;
    left: 0;
    width: min(280px, 86vw);
    max-width: 320px;
    height: 100vh;   /* fallback for browsers without dvh */
    height: 100dvh;
    z-index: 50;
    border-right: 1px solid var(--shell-border);
    border-bottom: none;
    padding: calc(var(--shell-header-h) + 0.5rem) 0.5rem 0.5rem;
    overflow-y: auto;
    transform: translateX(-100%);
    transition: transform 0.22s ease;
    /* Override the desktop-collapsed icon-only behaviour at this width
       — even if the user previously collapsed it on desktop, on mobile
       we always want the labels visible. */
    --shell-sidebar-w: min(280px, 86vw);
  }
  body.mobile-nav-open aside#site-sidebar {
    transform: translateX(0);
    box-shadow: 4px 0 18px rgba(10, 37, 64, 0.18);
  }
  body.mobile-nav-open .site-mobile-nav-backdrop {
    display: block;
  }
  /* In mobile drawer mode, force the sidebar back to full text labels
     even if `body.sidebar-collapsed` is set from a desktop session. */
  body.mobile-nav-open.sidebar-collapsed aside#site-sidebar {
    --shell-sidebar-w: min(280px, 86vw);
  }
  body.mobile-nav-open.sidebar-collapsed .site-nav-group-label,
  body.mobile-nav-open.sidebar-collapsed .site-nav-list a > span,
  body.mobile-nav-open.sidebar-collapsed .site-nav-list a .new-badge,
  body.mobile-nav-open.sidebar-collapsed .site-nav-list a .soon-badge {
    display: revert;
  }
  body.mobile-nav-open.sidebar-collapsed .site-nav-list a {
    justify-content: flex-start;
  }
  /* Sidebar list goes back to a clean vertical stack. */
  .site-nav-list {
    display: block;
  }
  .site-nav-list li {
    margin: 2px 0;
  }
  .site-nav-group-label {
    padding: 0.5rem 0.5rem 0.25rem;
  }
  /* Hide the desktop collapse-toggle on mobile — the hamburger does
     a different (drawer) job. */
  .site-nav-toolbar {
    display: none;
  }
  /* Header pill behaviour on mobile — at 390px there isn't room for
     the informational pills + hamburger + logo. We hide them (Pricing,
     Contact, Briefings) plus the secondary Sign in pill, since those
     routes are reachable elsewhere, and keep the primary CTA (Sign up)
     visible as the conversion anchor. */
  .site-header-actions .site-btn-about,
  .site-header-actions .site-btn-pricing,
  .site-header-actions .site-btn-contactus,
  .site-header-actions .site-btn-briefings,
  .site-header-actions .site-btn-signin {
    display: none;
  }
  /* On mobile the Sign-up CTA lives in the top-right "more" menu (it's the
     highlighted first item), not as a header pill — so hide the pill. */
  .site-header-actions .site-btn-signup { display: none; }
  /* Mobile header de-crowding (Option B — the page title is the anchor on
     tool pages). Three things were fighting for one strip: the wordmark
     logo, the injected page title (#site-header-context, e.g. "PWLBpredict"),
     and the Sign-up CTA.
       1. The logo reserved the sidebar's width (min(280px, 86vw)) — a
          desktop alignment concern that shoved the CTA off the right edge.
          Drop it so the brand takes only its own width.
       2. On pages that inject a header title (body.has-header-title), swap
          the full wordmark for a compact chart mark (still the home link),
          so the page title carries wayfinding. Marketing pages (no title)
          keep the full wordmark.
       3. The title can ellipsize, the row is tightened, and the CTA never
          shrinks — so nothing clips off the right edge. */
  .site-brand { min-width: 0; }
  .site-header-inner { gap: 0.4rem; padding: 0 0.5rem; }
  .site-header-context { min-width: 0; overflow: hidden; }
  .site-page-title-bar { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  /* Signed in, the account avatar takes space on the right — shrink the page
     title so it isn't truncated (e.g. "PWLBacademy"). */
  body.pwlb-signed-in .site-page-title-bar { font-size: 1.2rem; }
  body.has-header-title .site-brand-logo { display: none; }
  body.has-header-title .site-brand-mark { display: block; }
  /* Surface all the hidden header pills (incl. Sign up) via the top-right
     "more" menu. The mobile header is then just hamburger + mark + title. */
  .site-hdr-menu { display: inline-flex; }
  /* Signed in: collapse the account chip to just the avatar on mobile — for
     BOTH the data-page chip (#siteAccountChip) and the academy hub's chip
     (#acUserChip). The name + council were overrunning the header and
     squashing the page title. The chevron is hidden too; tapping the avatar
     opens the menu (wired in auth-header.js / academy.js). */
  #siteAccountChip .acct-text,
  #siteAccountChip .acct-chev,
  #acUserChip .ac-user-text,
  #acUserChip .ac-user-menu-btn { display: none !important; }
  #siteAccountChip .acct-avatar,
  #acUserChip .ac-user-avatar { cursor: pointer; }
  /* (header overflow is now visible globally — see the base rule — so the
     account/"more" dropdown menus aren't clipped on any viewport.) */
  /* When signed in, the account pill ("Firstname") plus the injected
     "Sign out" pill together overran the header on a phone — the Sign out
     pill was clipped off the right edge. Cap the account name so it
     ellipsizes, and reduce the Sign out pill to its icon (it carries inline
     styles from auth-header.js, hence the !important overrides). */
  .site-header-actions .site-btn-signin > span {
    max-width: 90px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  #siteLogoutLink {
    padding: 0.4rem 0.55rem !important;
    margin-left: 0.3rem !important;
  }
  #siteLogoutLink > span {
    display: none;
  }
}

/* Body scroll-lock when drawer is open so the page underneath
   doesn't scroll behind. */
body.mobile-nav-open {
  overflow: hidden;
}

/* ---------- cross-page legacy-style nudges ---------- */
/* Push existing pages toward the SONIA Forward Curve aesthetic without
   rewriting per-page inline CSS. Tokens come from :root above (--pt-*),
   kept in sync with sonia-fwd-curve.css. */

/* shell-side body hygiene */
body[data-page] {
  margin: 0;
  background: var(--shell-bg) !important;
  color: var(--pt-text);
}

/* --- KPI tiles ----------------------------------------------------- */
/* Match .sfc-kpi: navy gradient, soft navy shadow, hover lift,
   radial orange highlight in the top-right corner.
   Selector covers three legacy patterns:
     - .metric-card.card                 (forward-sonia and similar)
     - .card.bg-dark                     (pwlbActivity dynamic strip)
     - .kpi-card .kpi-header             (gilts/dashboard rate widgets — header strip only) */
.site-main-wrap .metric-card.card,
.site-main-wrap .card.bg-dark {
  position: relative;
  overflow: hidden;
  background: var(--pt-navy-grad) !important;
  border: none !important;
  box-shadow: var(--pt-shadow-navy) !important;
  color: #ffffff !important;
  border-radius: 12px !important;
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}

.site-main-wrap .metric-card.card::after,
.site-main-wrap .card.bg-dark::after {
  content: "";
  position: absolute;
  top: -40%;
  right: -25%;
  width: 75%;
  height: 160%;
  background: radial-gradient(circle, rgba(253, 126, 20, 0.16) 0%, transparent 70%);
  pointer-events: none;
}

.site-main-wrap .metric-card.card > *,
.site-main-wrap .card.bg-dark > * { position: relative; z-index: 1; }

.site-main-wrap .metric-card.card:hover,
.site-main-wrap .card.bg-dark:hover {
  transform: translateY(-2px);
  box-shadow: var(--pt-shadow-navy-strong) !important;
}

.metric-card .metric-label,
.metric-card .text-white-50,
.site-main-wrap .card.bg-dark .text-white,
.site-main-wrap .card.bg-dark .small {
  color: rgba(255, 255, 255, 0.72) !important;
  letter-spacing: 0.04em;
}

.metric-card .metric-value,
.site-main-wrap .card.bg-dark .fw-bold {
  color: var(--pt-orange) !important;
  font-weight: 700 !important;
  letter-spacing: -0.01em;
}

.metric-card .metric-sub {
  color: rgba(255, 255, 255, 0.55) !important;
}

/* .kpi-card style (gilts/dashboard rate widgets): white body + navy header */
.site-main-wrap .kpi-card {
  border-radius: 12px !important;
  box-shadow: var(--pt-shadow-navy) !important;
  border: 1px solid var(--pt-line) !important;
}

.site-main-wrap .kpi-card .kpi-header {
  background: var(--pt-navy-grad) !important;
  color: #ffffff !important;
  letter-spacing: 0.06em;
}

/* Card headers using Bootstrap .bg-dark (e.g. trend / chart cards) */
.site-main-wrap .card-header.bg-dark {
  background: var(--pt-navy-grad) !important;
  color: #ffffff !important;
  border-bottom: none !important;
  padding: 0.65rem 0.85rem !important;
}

/* Tile-header layout — buttons right-justified, "Last updated" small hidden */
.site-main-wrap .card-header.bg-dark > .d-flex.justify-content-between {
  align-items: center !important;
  column-gap: 0.75rem;
}
.site-main-wrap .card-header.bg-dark > .d-flex.justify-content-between > .d-flex:first-child > small {
  display: none !important;
}
.site-main-wrap .card-header.bg-dark > .d-flex.justify-content-between > .d-flex:first-child > h3 {
  margin: 0 !important;
}
.site-main-wrap .card-header.bg-dark > .d-flex.justify-content-between > .d-flex:last-child {
  margin-left: auto;
}
.site-main-wrap .card-header.bg-dark .btn-group-sm > .btn {
  padding: 0.18rem 0.55rem;
  font-size: 0.75rem;
}

/* Header titles inside dark card-headers — keep them orange but consistent */
.site-main-wrap .card-header.bg-dark span,
.site-main-wrap .card-header.bg-dark [style*="color:#fd7e14"] {
  color: var(--pt-orange) !important;
  letter-spacing: 0.08em;
  font-weight: 600;
}

/* --- Panel cards --------------------------------------------------- */
/* Match .sfc-panel: subtle border, soft navy shadow, slim navy stripe
   on the left edge for the same vertical accent. */
.site-main-wrap .card:not(.metric-card) {
  position: relative;
  border: 1px solid var(--pt-line) !important;
  box-shadow: var(--pt-shadow-navy) !important;
  border-radius: 12px !important;
  overflow: hidden;
}

/* Left navy stripe on tile cards intentionally removed — keeps the cards
   visually quieter when they sit next to the navy tile headers. */

/* --- Section / panel titles --------------------------------------- */
.site-main-wrap .card-title {
  font-size: 0.78rem !important;
  font-weight: 600 !important;
  letter-spacing: 0.14em !important;
  text-transform: uppercase !important;
  color: var(--pt-navy) !important;
}

/* --- Page hero --------------------------------------------------- */
/* Soften legacy orange h1 page heroes to match .sfc-title. */
.site-main-wrap header h1,
.site-main-wrap .page-title,
.site-main-wrap h1.h4 {
  color: var(--pt-text) !important;
  font-weight: 600 !important;
  font-size: 1.4rem !important;
  margin-bottom: 0.25rem !important;
}

.site-main-wrap header .text-muted,
.site-main-wrap header .header-subtitle {
  font-style: italic;
  font-size: 0.78rem !important;
  color: var(--pt-muted) !important;
}

/* --- Tables: orange-on-dark theme aligned to SFC navy ------------- */
.site-main-wrap .table-wrap thead,
.site-main-wrap table.table thead {
  background: var(--pt-navy-grad) !important;
}

.site-main-wrap .table-wrap thead th,
.site-main-wrap table.table thead th {
  color: var(--pt-orange) !important;
  border-bottom: none !important;
}

/* --- Refresh / status dot in legacy headers ----------------------- */
.site-main-wrap .refresh-dot.active {
  background: var(--pt-orange);
}

/* ---------- standardized in-content page header ---------- */
/* Injected by site.js on every page (skipped if .sfc-title already exists). */
.site-page-header {
  padding: 4px 4px 0 4px;
  margin-bottom: 0.9rem;
}

.site-page-title {
  position: relative;
  margin: 0 0 0.25rem 0;
  padding-left: 14px;
  font-size: 1.5rem;
  font-weight: 600;
  color: var(--pt-text);
  letter-spacing: -0.01em;
}

.site-page-title::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.18em;
  bottom: 0.18em;
  width: 4px;
  border-radius: 2px;
  background: var(--pt-navy-grad);
}

.site-page-subtitle {
  padding-left: 14px;
  font-size: 0.85rem;
  color: var(--pt-muted);
  font-style: italic;
}

/* ---------- KPI tiles: full-card navy gradient ---------- */
/* Covers .kpi-card (gilts, dashboard rate widgets) and the
   .dashboard-kpi-card variant. Applies the full SFC tile look — gradient
   fill, radial orange highlight, hover lift — instead of the legacy
   white-body / dark-header-strip pattern. */
.site-main-wrap .kpi-card,
.site-main-wrap .kpi-card.dashboard-kpi-card {
  position: relative;
  overflow: hidden;
  background: var(--pt-navy-grad) !important;
  border: none !important;
  box-shadow: var(--pt-shadow-navy) !important;
  border-radius: 12px !important;
  color: #ffffff !important;
}

/* Radial-gradient orange highlight removed — it was anchored at top-right
   and tinted the right half of every KPI card unevenly. Cards now show a
   uniform navy gradient. */
.site-main-wrap .kpi-card::after,
.site-main-wrap .kpi-card.dashboard-kpi-card::after {
  content: none;
}

.site-main-wrap .kpi-card > * {
  position: relative;
  z-index: 1;
}

/* No hover effect on KPI cards — they're not generally clickable, and
   even where they are (predictor) we don't want the whole card moving
   or its shadow shifting beneath the cursor. */

.site-main-wrap .kpi-card .kpi-header {
  background: transparent !important;
  color: rgba(255, 255, 255, 0.72) !important;
  font-weight: 600 !important;
  letter-spacing: 0.04em;
  font-size: 0.78rem !important;
  text-transform: uppercase;
  border-bottom: none !important;
  padding-bottom: 0.25rem !important;
}

.site-main-wrap .kpi-card .rate-value,
.site-main-wrap .kpi-card .kpi-value {
  color: var(--pt-orange) !important;
  font-weight: 700 !important;
  letter-spacing: -0.01em;
}

.site-main-wrap .kpi-card .small,
.site-main-wrap .kpi-card .kpi-changes,
.site-main-wrap .kpi-card .text-muted {
  color: rgba(255, 255, 255, 0.65) !important;
}

/* Rate-value polish — large bold orange value, applied to every page */
.site-main-wrap .kpi-card .rate-value,
.site-main-wrap .kpi-card .kpi-value {
  font-size: 2.25rem !important;
  font-weight: 700 !important;
  line-height: 1.05;
  letter-spacing: -0.01em;
}

.site-main-wrap .kpi-card .kpi-changes .col-6 {
  font-size: 0.75rem !important;
  letter-spacing: 0.02em;
}

/* ---------- SFC-style loading skeletons (global) ---------- */
/* Mirrors the .pt-kpi-skel / .pt-loading-* visuals from sonia-fwd-curve.css
   so legacy pages get the same shimmering placeholder while data loads. */
.pt-kpi-skel {
  display: inline-block;
  vertical-align: middle;
  width: 6ch;
  height: 1em;
  border-radius: 6px;
  background: linear-gradient(
    90deg,
    rgba(253, 126, 20, 0.20) 0%,
    rgba(253, 126, 20, 0.55) 50%,
    rgba(253, 126, 20, 0.20) 100%
  );
  background-size: 200% 100%;
  animation: pt-skel-shimmer 1.4s ease-in-out infinite;
}

.pt-loading-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.18rem 0.6rem;
  border-radius: 999px;
  background: rgba(253, 126, 20, 0.12);
  color: var(--pt-orange);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.pt-loading-pulse {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--pt-orange);
  animation: pt-skel-pulse 1.2s ease-in-out infinite;
}

@keyframes pt-skel-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

@keyframes pt-skel-pulse {
  0%, 100% { transform: scale(0.85); opacity: 0.7; }
  50%      { transform: scale(1.15); opacity: 1; }
}

/* ---------- Chart skeleton — faux bars with shimmer ---------- */
.pt-loading-chart {
  width: 100%;
  height: 100%;
  min-height: 200px;
  position: relative;
  background: linear-gradient(180deg, #fcfdfe 0%, #f7f9fb 100%);
  border-radius: 6px;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  padding: 18px 14px 30px;
  gap: 6px;
  isolation: isolate;
}

.pt-loading-chart::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    transparent 0%,
    rgba(26, 71, 117, 0.06) 30%,
    rgba(26, 71, 117, 0.13) 50%,
    rgba(26, 71, 117, 0.06) 70%,
    transparent 100%
  );
  animation: pt-shimmer 1.8s cubic-bezier(0.4, 0.0, 0.2, 1) infinite;
  z-index: 1;
}

.pt-loading-chart-bar {
  flex: 1;
  background: linear-gradient(180deg, rgba(26, 71, 117, 0.18) 0%, rgba(26, 71, 117, 0.06) 100%);
  border-radius: 3px 3px 0 0;
  position: relative;
  z-index: 0;
}

.pt-loading-chart-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
  pointer-events: none;
}

@keyframes pt-shimmer {
  0%   { transform: translateX(-50%); }
  100% { transform: translateX(50%); }
}

/* ---------- Table skeleton — faux rows with shimmer ---------- */
.pt-loading-table {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px 0;
  position: relative;
  isolation: isolate;
}

.pt-loading-table::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    transparent 0%,
    rgba(26, 71, 117, 0.05) 50%,
    transparent 100%
  );
  animation: pt-shimmer 1.8s cubic-bezier(0.4, 0.0, 0.2, 1) infinite;
  z-index: 1;
  pointer-events: none;
}

.pt-loading-table-head {
  height: 24px;
  background: var(--pt-navy);
  border-radius: 4px;
  margin-bottom: 4px;
  opacity: 0.85;
}

.pt-loading-table-row {
  height: 18px;
  background: linear-gradient(90deg, rgba(26, 71, 117, 0.08) 0%, rgba(26, 71, 117, 0.13) 50%, rgba(26, 71, 117, 0.08) 100%);
  border-radius: 4px;
  opacity: 0.85;
}

.pt-loading-table-row:nth-child(odd)  { width: 92%; }
.pt-loading-table-row:nth-child(even) { width: 78%; }
.pt-loading-table-row:nth-child(3n)   { width: 86%; }

/* ---------- shared site footer ----------
   Injected by site.js (injectFooter) on non-authenticated pages — a footer on
   every public page, not just the homepage. Mirrors the landing footer's dark
   navy look, but self-contained here in site.css (app pages don't load
   landing.css). The homepage keeps its own .lp-footer; injectFooter skips any
   page that already has a footer. */
/* Kept visually identical to the homepage footer (landing's .lp-footer) so
   every page's footer matches. If you restyle one, mirror it in the other. */
.site-footer {
  background: #07182c;
  color: rgba(255, 255, 255, 0.7);
  padding: clamp(48px, 7vw, 72px) clamp(18px, 4vw, 44px) 28px;
  /* Break out of .site-main-wrap's 16px 20px 20px padding so the dark bar is
     full-width — no white slivers down the left/right (or below it). */
  margin: 48px -20px -20px;
}
.site-footer-inner {
  max-width: 1180px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr 1fr;
  gap: 32px;
  padding-bottom: 36px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.site-footer-brand img { height: 34px; margin-bottom: 14px; }
.site-footer-brand p {
  font-size: 0.9rem; line-height: 1.55; max-width: 30ch; margin: 0;
  color: rgba(255, 255, 255, 0.6);
}
.site-footer-col h5 {
  color: #fff; font-size: 0.78rem; text-transform: uppercase;
  letter-spacing: 0.08em; font-weight: 700; margin: 0 0 14px;
}
.site-footer-col a {
  display: block; padding: 5px 0; font-size: 0.92rem; text-decoration: none;
  color: rgba(255, 255, 255, 0.62);
}
.site-footer-col a:hover { color: var(--pt-orange, #fd7e14); }
.site-footer-accent { color: var(--pt-orange, #fd7e14); }
.site-footer-base {
  max-width: 1180px; margin: 22px auto 0;
  display: flex; flex-wrap: wrap; gap: 12px; align-items: center;
  justify-content: space-between; font-size: 0.82rem; color: rgba(255, 255, 255, 0.5);
}
.site-footer-links a { color: rgba(255, 255, 255, 0.55); text-decoration: none; margin-left: 16px; }
.site-footer-links a:first-child { margin-left: 0; }
.site-footer-links a:hover { color: rgba(255, 255, 255, 0.85); }
@media (max-width: 760px) { .site-footer-inner { grid-template-columns: 1fr 1fr; } }
/* Stay 2-up on phones — no collapse to a single stacked column — so every page's
   footer matches the homepage's lp-footer, which keeps its columns on mobile. */
@media (max-width: 480px) { .site-footer-base { justify-content: flex-start; } }
