/* v936: grey palette aligned with the Windows BI-Cycle client.
   Previously every chrome/border grey had a slight blue tint (R<B by
   ~8-18 points), which next to the Windows reference felt
   "lightweight / washed out" vs. the Windows client's solid neutral
   #F0F0F0 chrome. Shifted all 18 foundational greys to pure-neutral
   equivalents at the same perceptual luminance, minus ~5 L points
   so the chrome reads with a bit more weight — matching what the
   user sees in the Windows screenshot. Relative lightness between
   shades is preserved, so the hierarchy (page bg vs. panel bg vs.
   border vs. divider) is visually unchanged. Semantic hover-state
   blues (#EAF2FB, #EEF5FF) were explicitly NOT neutralised: the
   Windows client also uses subtle blue tints for hover/selected
   states, and neutralising them would have turned hover feedback
   into a "dirty" grey-on-grey flash. Brand accents (--brand,
   --brand-dark, --nav-bg) are untouched — they are intentional
   identity colours, not chrome. */
:root {
  --bg: #ffffff;
  --soft-bg: #ededed;
  --line: #dadada;
  --brand: #3f78ad;
  --brand-dark: #173f6b;
  --muted: #506b8b;
  --nav-bg: #5f8fbe;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  font-family: Arial, Helvetica, sans-serif;
  background: var(--bg);
  color: #18324d;
}

/* ── Nav ── */
.top-nav {
  width: 100%;
  background: var(--nav-bg);
  border-bottom: 1px solid rgba(23, 63, 107, 0.10);
  align-items: center;
  padding: 0 20px 0 0;
  gap: 12px;
  height: 56px;
  position: relative;
}

/* Public (signed-out) layout: brand | links | actions
   v877 (+1): grid + gutter moved to an inner .top-nav__inner
   wrapper that mirrors .container's max-width (1580px) + 32px
   padding. Result: the AL-Cycle chip and Sign-in chip align with
   the left/right edges of the SUMMARY and PRICING sections below
   instead of hugging the viewport edge. The outer <nav> still
   paints the full-bleed white bar + bottom hairline. Base
   .top-nav's `padding: 0 20px 0 0` is zeroed here so the right
   gutter comes from the inner wrapper, not the outer bar. */
.top-nav--public {
  padding: 0;
  /* v85w: align the signed-out header palette with the signed-in
     version (v85i). Both share the same white top bar + dark text
     + hairline bottom border, so navigating between the marketing
     home and the authenticated app no longer swaps between a blue
     header and a white one.
     v945: bg flipped white → #f0f0f0 (Windows chrome grey) and
     bottom border darkened to #b8b8b8 so the public home's top-nav
     matches the signed-in .top-nav--signed-in chrome introduced in
     v937/v938. User request: "same colors, lines etc as the
     signed-in one" — public + signed-in home should now read as
     the same visual shell. */
  background: #f0f0f0;
  border-bottom: 1px solid #b8b8b8;
  /* v924: after five iterations (v913-v923) trying to keep a
     compact 64px band with a 80px logo spilling over via various
     overflow tricks, start over with the simple design: the nav
     band is exactly as tall as the logo (80px). No overhang, no
     clipping by the viewport edge, no cascade hacks. The base
     .top-nav rule pins height:56px for the signed-in variant;
     height:auto lets this variant size itself from its content. */
  height: auto;
}

.top-nav--public .top-nav__inner {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 12px;
  max-width: 1580px;
  margin: 0 auto;
  padding: 0 32px;
  width: 100%;
  /* v926: 80 → 56px. The logo's internal composition (gear at
     left descending below the wordmark's baseline) read as "not
     centered" at 80px because the right side of the img bounding
     box had visible empty area where the wordmark ended. At 56px
     the whole artwork is smaller relative to the nav; the
     asymmetry isn't noticeable, and the band matches the
     conventional 56px header height used elsewhere in the app. */
  height: 56px;
}

.top-nav--public .brand-mark {
  color: var(--brand-dark);
  background: #f0f0f0;
  border: 1px solid #d8d8d8;
}
.top-nav--public .brand-mark--logo {
  background: transparent;
  border: 0;
  padding: 0;
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  line-height: 1;
}
.top-nav--public .nav-brand {
  height: 56px;
}
.top-nav--public .brand-mark__logo {
  /* v948: height 56 → 40 per user request ("give the logo on the
     left top some more margin"). Matching .reports-panel__title-logo
     at line ~670 (signed-in nav in the sidebar header). The 40px
     logo vertically centers inside the 56px .nav-brand cell,
     leaving 8px breathing room above and below. .top-nav__inner's
     padding: 0 32px still provides the horizontal gutter, so the
     logo now reads as a comfortably-framed chrome element rather
     than one flush against every edge. */
  display: block;
  height: 40px;
  width: auto;
}

.top-nav--public .nav-links a {
  color: var(--brand-dark);
}

.top-nav--public .nav-links a:hover {
  color: #0b2a4a;
  text-decoration: underline;
}

/* v945: page title in the middle grid cell of .top-nav__inner,
   replacing the v85w Pricing/Features/About nav-links (removed in
   the v945 pass to components/app-header.js). User asked to move
   the big <h1> "Asset Lifecycle Decision Platform" hero up into
   the nav. The default browser h1 (2em, 0.67em margin) would blow
   out the 56px nav height and break the grid layout, so this rule
   pins the size + drops the margins explicitly. Font-size 18px +
   weight 600 is bigger than the signed-in search bar's text
   (~13.5px) but smaller than a hero; reads as a "Windows title bar"
   app title, which is the mental model the user is converging on
   for the whole home page.
   Scoped to --public because the signed-in nav uses its middle
   cell for the search bar (different element, no h1). */
.top-nav--public .top-nav__title {
  margin: 0;
  padding: 0;
  /* v948: 18 → 22px per user request ("somewhat larger font-size").
     Still well under the 56px nav height (line-height 1 + 22px =
     22px vertical); no risk of overflowing the chrome band. Also
     bumped the weight up a half-step (600 → 700) so the larger
     glyphs read cleanly as "title chrome" rather than drifting
     toward a heading-in-content feel. */
  font-size: 22px;
  font-weight: 700;
  color: var(--brand-dark);
  text-align: center;
  letter-spacing: 0;
  /* Single-line ellipsis if the viewport is too narrow for the
     full title — safer than wrapping to two lines and breaking the
     56px nav height. The grid's `1fr` middle column already caps
     the horizontal space. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.top-nav--public .sign-in-btn {
  /* Solid brand-dark pill — clear call-to-action contrast against
     the white bar. Shadow simplified (was tuned for the prior
     blue gradient bar).
     v877: geometry aligned with .brand-mark (padding 6px 14px,
     radius 10px, 16px/700) so the two corner elements read as a
     matched chip pair. The base .sign-in-btn rule (L780) sets a
     taller 40px CTA with 12px radius and 15px font; those get
     overridden here so only the public header's left-right pair
     collapses to chip sizing. Font-family: inherit drops the
     browser's default button font back to Arial (body) so the
     "Sign in" label renders at the same size/weight as "AL-Cycle"
     in the brand pill — buttons don't inherit font-family by
     default in Chrome/Safari. */
  background: var(--brand-dark);
  color: #ffffff;
  border: 1px solid var(--brand-dark);
  box-shadow: none;
  height: auto;
  min-width: 0;
  padding: 6px 14px;
  border-radius: 10px;
  font-size: 16px;
  font-family: inherit;
  line-height: 1.2;
}
.top-nav--public .sign-in-btn:hover {
  background: #0b2a4a;
  border-color: #0b2a4a;
}

/* Signed-in home layout: hamburger | centered search | right links/actions
   v85i: top bar now mirrors the analysis-page's two-section chrome —
   main (right) side is white like the report-top-bar, and the
   hamburger/brand strip on the left is grey #f0f0f0 matching the
   report-settings-bar. The earlier full-blue bar clashed with the
   white content below when users moved between home and analysis;
   now both surfaces share the same header palette. */
.top-nav--signed-in {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  /* v937: flipped to grey to match the Windows client's title-bar
     chrome (screenshot request). The old white bar made the header
     disappear into the page body — against the new neutral grey
     palette from v936, a grey top-nav reads as a deliberate chrome
     band, and lets the search box stand out as white "inside"
     chrome (see .top-nav--signed-in .top-search below).
     v938: bg shifted #e5e5e5 → #f0f0f0 (exact Windows chrome match
     per user's sampled reference). Bottom border darkened from
     var(--line) to #b8b8b8 to match the sidebar separator darkening
     (same v938 pass) — previously the 1px bottom line disappeared
     almost completely against #f0f0f0/content-bg with only
     var(--line)'s #dadada, which has ~22 luminance-points contrast.
     #b8b8b8 yields ~56 and reads clearly as a chrome-to-content
     boundary like the Windows title-bar underscore. */
  background: #f0f0f0;
  border-bottom: 1px solid #b8b8b8;
}

/* v870: sticky title bar on the home route. The prior layout had the
   top-nav scrolling away with the page body, leaving the user without
   the search field once they scrolled down. app-header is a custom
   element that defaults to inline; the display:block + sticky
   promotion pins the whole header strip to the viewport top. z-index
   sits above the reports-panel rail (z=10 in most builds) so the
   header always paints on top. */
body.route-home app-header {
  display: block;
  position: sticky;
  top: 0;
  z-index: 20;
}

/* v870: when the left reports-panel is in always-visible rail mode,
   the top-nav's nav-brand div is just an 8px grey sliver — its only
   child (panel-toggle-btn) was hidden back in the .has-reports-rail
   block at line ~678, so the div has no content and paints as a thin
   vertical strip between the sidebar and the search bar. Hide it so
   the search-wrap spans the full top row cleanly. Panel-open only
   (not desktop rail) keeps the hamburger visible on mobile. */
body.has-reports-rail .top-nav--signed-in .nav-brand {
  display: none;
}

.top-nav--signed-in .nav-brand {
  /* v937: transparent now that the entire .top-nav--signed-in is
     grey. Previously this strip painted #f0f0f0 against a white
     main bar to give the hamburger+logo area a grouped "chrome
     cell" appearance. With the whole top-nav now grey, keeping a
     separate grey strip here would paint the same colour twice —
     removing it lets the bar read as one continuous band. */
  background: transparent;
  padding-right: 8px;
}

.top-nav--signed-in .panel-toggle-btn {
  /* Darken icon + use brand-dark hover tint so the hamburger is
     visible on the grey strip. The prior white-on-blue contrast
     no longer applies. */
  color: #1c4f82;
}
.top-nav--signed-in .panel-toggle-btn:hover {
  background: rgba(28, 79, 130, 0.08);
}

.top-nav--signed-in .brand-mark {
  /* Strip the rounded-pill-on-translucent treatment — unnecessary
     on a flat grey strip. Plain dark text is legible and lets the
     brand mark coexist with the hamburger without visual clutter. */
  color: #1c4f82;
  background: transparent;
  border-color: transparent;
}

.top-nav--signed-in .top-search {
  /* v937: flipped to white against the new grey top-nav
     (v937 swap). The search field now reads as a bright input
     "inside" the chrome band — matching the Windows client's
     treatment of edit controls as white islands on grey chrome.
     Border tint kept blue-grey so the control still feels like
     the interactive element on the bar. */
  background: #ffffff;
  border: 1px solid #b9cfe6;
  color: #1c4f82;
}
.top-nav--signed-in .top-search:focus-within {
  /* v937: bg was flipped to white for the base state, so focus
     now distinguishes itself via border + a subtle ring only. */
  background: #ffffff;
  border-color: #2d74c4;
  box-shadow: 0 0 0 2px rgba(45, 116, 196, 0.18);
}

.top-nav--signed-in .top-search__icon {
  color: #5b6e85;
}

.top-nav--signed-in .top-search__input {
  color: #1c4f82;
}
.top-nav--signed-in .top-search__input::placeholder {
  color: #8497ae;
  opacity: 1;
}
.top-nav--signed-in .top-search__close {
  color: #5b6e85;
}

.nav-actions {
  display: flex;
  align-items: center;
  justify-self: end;
  justify-content: flex-end;
  gap: 6px;
}

.nav-links {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: center;
  gap: 32px;
}

.top-nav--signed-in .nav-links {
  justify-content: flex-end;
  gap: 22px;
  padding-right: 4px;
}

.nav-links a {
  font-size: 15px;
  font-weight: 500;
  color: rgba(255,255,255,0.85);
  text-decoration: none;
  white-space: nowrap;
}

.nav-links a:hover { color: #ffffff; }

/* ── Brand area: hamburger toggle + app name ── */
.nav-brand {
  display: flex;
  align-items: center;
  height: 56px;
}

/* Hamburger panel toggle button */
.panel-toggle-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 56px;
  flex-shrink: 0;
  border: 0;
  background: transparent;
  color: #fff;
  cursor: pointer;
  transition: background 0.15s;
}
.panel-toggle-btn:hover { background: rgba(255,255,255,0.10); }
.panel-toggle-btn svg { display: block; }

.brand-mark {
  font-size: 16px;
  font-weight: 700;
  color: #ffffff;
  padding: 6px 14px;
  border-radius: 10px;
  background: rgba(255, 255, 255, .14);
  border: 1px solid rgba(255, 255, 255, .26);
  white-space: nowrap;
  /* v877: pin line-height to match .top-nav--public .sign-in-btn's
     explicit 1.2, keeping the brand pill and sign-in chip at
     pixel-identical heights across browsers. Span previously
     inherited the browser's default line-height (~1.2 in Chrome,
     similar elsewhere) — making it explicit removes any drift. */
  line-height: 1.2;
}

/* ── Centered search in top nav (signed-in only) ── */
.top-search-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  width: 100%;
  /* v874: explicitly claim the 1fr grid column. Without this, the
     flex container's intrinsic max-content size (≈220px — the pill's
     content width) was overriding the grid column assignment in
     some browsers, leaving the wrap stuck at 220px. grid-column:2
     pins it to the second column (1fr) and width:100% then resolves
     to that column's allocated width. */
  grid-column: 2;
  justify-self: stretch;
}

.top-search {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* v871/v872: widened progressively from 560px → 880px → calc below.
     The search pill is the home page's primary navigation action; on
     wide viewports the old fixed cap made it read as a small island
     far from the viewport centre. Now it stretches to fill most of
     its column (column is 1fr inside a grid with one auto-sized
     sibling), capped at 1100px for a hard upper bound and with 24px
     of breathing room on each side via the 100% calc. */
  width: min(1100px, calc(100% - 48px));
  height: 38px;
  padding: 0 6px 0 14px;
  background: rgba(255,255,255,0.18);
  border: 1px solid rgba(255,255,255,0.28);
  border-radius: 999px;
  color: #fff;
  transition: background 0.15s, border-color 0.15s;
}
.top-search:focus-within {
  background: rgba(255,255,255,0.26);
  border-color: rgba(255,255,255,0.45);
}

.top-search__icon {
  flex-shrink: 0;
  opacity: 0.9;
}

.top-search__input {
  flex: 1;
  min-width: 0;
  height: 100%;
  border: 0;
  outline: 0;
  background: transparent;
  color: #fff;
  font: inherit;
  font-size: 14px;
}
.top-search__input::placeholder {
  color: rgba(255,255,255,0.7);
}
/* Hide the native WebKit search "X" clear button — we use our own */
.top-search__input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }

.top-search__close {
  display: none; /* only shown in narrow expanded state */
  width: 26px;
  height: 26px;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: #fff;
  cursor: pointer;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.top-search__close:hover { background: rgba(255,255,255,0.18); }

.top-search__collapsed-btn {
  display: none; /* only shown on narrow screens */
  width: 38px;
  height: 38px;
  border-radius: 999px;
  background: rgba(255,255,255,0.16);
  border: 1px solid rgba(255,255,255,0.28);
  color: #fff;
  cursor: pointer;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.top-search__collapsed-btn:hover { background: rgba(255,255,255,0.26); }

/* ── Slide-in reports panel ── */
.reports-panel {
  position: fixed;
  top: 0;
  left: 0;
  /* v85k: base 260px (was 300px). The panel was reading slightly too
     wide against the rest of the app — the analysis-page report-
     sidebar defaults to 238px, so bringing this closer in line keeps
     the two surfaces on similar dimensions. Category labels + list
     items still fit comfortably at 260px. */
  width: 260px;
  max-width: 90vw;
  /* v939: height reduced by 24px so the fixed-bottom status bar
     (see .site-footer on body.route-home.has-reports-rail) doesn't
     overlay the sidebar's user chip. On non-route-home contexts the
     status bar doesn't render, but the panel is already hidden
     off-screen (`transform: translateX(-100%)` on the base rule)
     so the 24px gap at the bottom has no visual consequence
     anywhere else. If the status bar height changes, update both
     this value and the body padding-bottom near line ~1270.
     26.1.0.47: scoped the 24px reservation to `body.route-home`
     only — the fixed status bar only renders on home, so on
     /report (where the panel is now also pinned) the 24px shaved
     off the bottom was just dead grey space below the user chip.
     The base rule below sets the panel to full viewport height;
     the route-home override (right after) restores the 24px
     reservation. */
  height: 100dvh;
  /* v939: vertical hairline between sidebar and main panel, running
     the full height of the sidebar. Matches the Windows client's
     visible dock-panel separator (user request: "borderline between
     the side-panel and the main-panel all the way up"). Colour
     matches the other v938 chrome separators (#b8b8b8) so the
     sidebar reads as a single framed column with consistent
     hairline borders on all sides where it meets content. */
  border-right: 1px solid #b8b8b8;
  /* v937: base bg flipped white → #f0f0f0 so the panel reads as a
     single grey chrome column (matches top-nav + footer, which
     were already grey). Individual sections (header, filter-bar,
     body, footer) previously painted 4 different backgrounds — the
     new uniform grey is demarcated section-by-section with 1px
     hairline separators (like the Windows client's sidebar). */
  background: #f0f0f0;
  color: #18324d;
  /* v84i: shadow moved to the .has-reports-rail variant below. The base
     rule used to carry `box-shadow: 4px 0 28px rgba(16,36,64,0.22)`,
     but with `transform: translateX(-100%)` hiding the panel off-screen
     the shadow's 4+28=32px right-extent still landed inside the
     viewport at x=0..32 — visible as a stray vertical shadow strip on
     every non-home route once ensurePanel() had ever been called. */
  z-index: 1050;
  display: flex;
  flex-direction: column;
  transform: translateX(-100%);
  transition: transform 0.22s cubic-bezier(0.4,0,0.2,1),
              width 0.22s cubic-bezier(0.4,0,0.2,1);
  will-change: transform, width;
  overflow: hidden;
}
/* Expanded state: panel slides in at full width */
.reports-panel.is-open {
  transform: translateX(0);
  width: 260px;
  /* v85j: shadow removed on both rail and open states. Rail mode
     also has no shadow (below). The panel already has a grey bg +
     border-right to separate from the content; the dropshadow was
     loud and competed with the content area. */
}

/* 26.1.0.47: the fixed status bar at the bottom of the viewport
   only exists on signed-in home (body.route-home.has-reports-rail
   .site-footer becomes position:fixed; height:24px). On every
   other route the panel base rule's `height: 100dvh` is correct,
   but on home we need to give the status bar 24px back. Scoped
   to body.route-home so /report and any future rail-bearing
   route gets the full viewport height for the panel — no dead
   grey space below the user chip. */
body.route-home .reports-panel {
  height: calc(100dvh - 24px);
}

/* 26.1.0.58: report-page panel goes ALL THE WAY DOWN. Earlier
   in 26.1.0.56 we kept `height: calc(100dvh - 24px)` on this rule
   to leave room for the fixed status bar at the bottom; that
   created an empty grey corner at the bottom-left, with the
   status bar crossing the panel below it. The user wanted the
   opposite layout — full-height panel, status bar offset to
   the RIGHT of the panel rail. So the height clamp goes away
   here, and the status-bar rules below pick up the panel's
   right edge as their left edge instead.
   Base rule (.reports-panel { height: 100dvh }) now applies
   without override on /report. */
/* 26.1.0.56: report-page footer status bar. Same chrome and
   geometry as the home-page .site-footer when in route-home mode
   (#f0f0f0 bg, #b8b8b8 hairline border-top, 24px height,
   z-index above the reports panel). Sections sit in a flex row
   with hairlines between them via per-section border-right —
   matches the Windows status bar aesthetic the user referenced.
   Sections are buttons (transparent until hover) so they can
   carry click handlers; the hover state is a soft accent tint
   so the click affordance is visible but doesn't compete with
   the document/tiles content above.
   26.1.0.59: layout restructured for right-alignment + overflow.
   The bar contains: [more-button][overflow-menu][sections-host].
   The sections host flexes to fill remaining width and right-
   aligns its children via justify-content:flex-end. The more
   button sits at the bar's left edge (drop-up menu anchored
   above it via position:absolute + bottom:100%). Overflow is
   handled by JS measurement in renderStatusBar — sections that
   don't fit get hidden and rebuilt as menu items. */
.report-status-bar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 24px;
  display: flex;
  align-items: stretch;
  background: #f0f0f0;
  border-top: 1px solid #b8b8b8;
  z-index: 1100;
  font-size: 12px;
  color: #18324d;
  /* 26.1.0.59: no horizontal scroll on the BAR itself (the
     overflow-to-menu mechanism replaces what would have been a
     scrolled sequence). overflow:visible on the bar so the
     drop-up menu can extend above the bar's bottom-fixed
     boundary. */
  overflow: visible;
}
.report-status-bar__sections {
  display: flex;
  align-items: stretch;
  flex: 1 1 auto;
  min-width: 0;
  /* 26.1.0.60: center-align sections within the available width.
     Was justify-content: flex-end (right-align) in 26.1.0.59 per
     the user's earlier request, now flipped to center per the
     follow-up. The overflow detection logic still works: the JS
     checks each visible section's left edge against the host's
     left edge — a centred row whose total width exceeds the
     host's width has its leftmost children clipped on the left
     (their `getBoundingClientRect().left` sits left of the host
     rect's left), which is the same trigger condition the
     justify-end version used. So the same measure() routine
     handles overflow without changes. */
  justify-content: center;
  /* Hide overflow on the host so a section that's positioned
     outside the host's bounds (because it would protrude left of
     the more button) is visually clipped — the JS hides it
     properly via [hidden], but this is a belt-and-braces guard. */
  overflow: hidden;
}
.report-status-bar__section {
  display: inline-flex;
  align-items: center;
  height: 100%;
  padding: 0 12px;
  border: 0;
  border-left: 1px solid #b8b8b8;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 12px;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  flex-shrink: 0;
  transition: background-color 120ms ease;
}
/* 26.1.0.59: section separator switched from border-RIGHT to
   border-LEFT now that sections are right-aligned. Each section
   gets a hairline on its left, with the leftmost (the first
   visible one after overflow handling) appearing as the first
   visible cell. The first DOM child WAS getting `border-left:0`
   in 26.1.0.56's variant; in this layout we still want a
   separator between every adjacent pair, AND no border on the
   first visible cell (the one that abuts the more-button or the
   bar's left edge if there's no overflow). The first visible
   cell can vary as items get hidden/shown by JS, so we
   special-case the first non-hidden child via :nth-child won't
   work — JS managing [hidden] sets a `--first` modifier instead.
   Simpler rule: just always have border-left on every section.
   The separator between the more-button and the first section
   is provided by the more-button's own border-right. The first
   section after the more-button gets a redundant border-left
   visually merged with the more-button's border-right — that's
   one hairline visually, not two, because both render the same
   pixel column. Keep it. */
.report-status-bar__section:hover {
  background: rgba(42, 109, 179, 0.10);
  color: #1c56a4;
}
.report-status-bar__section:focus-visible {
  outline: 2px solid #2a6db3;
  outline-offset: -2px;
}

/* 26.1.0.59: the "···" trigger that opens the drop-up overflow
   menu. Sits at the bar's left edge (first DOM child) and is
   shown by the JS measurement pass when sections overflow. The
   button has a relative position so the absolutely-positioned
   menu can anchor above it. */
.report-status-bar__more {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  width: 32px;
  border: 0;
  border-right: 1px solid #b8b8b8;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
  transition: background-color 120ms ease;
}
.report-status-bar__more:hover {
  background: rgba(42, 109, 179, 0.10);
  color: #1c56a4;
}
.report-status-bar__more:focus-visible {
  outline: 2px solid #2a6db3;
  outline-offset: -2px;
}
.report-status-bar__more[hidden] {
  display: none;
}

/* Drop-up menu panel — absolutely positioned ABOVE the
   more-button. Width auto-fits content; max-height capped so a
   long footers list scrolls inside the menu rather than running
   off the top of the viewport. The bar has overflow:visible to
   let this escape its box. */
.report-status-bar__overflow-menu {
  position: absolute;
  bottom: 100%;
  left: 0;
  margin: 0;
  padding: 4px 0;
  list-style: none;
  background: #ffffff;
  border: 1px solid #b8b8b8;
  border-radius: 4px 4px 0 0;
  box-shadow: 0 -4px 12px rgba(16, 36, 64, 0.15);
  min-width: 200px;
  max-height: min(60vh, 360px);
  overflow-y: auto;
  z-index: 1101;
}
.report-status-bar__overflow-menu[hidden] {
  display: none;
}
.report-status-bar__menu-item {
  display: block;
  width: 100%;
  padding: 6px 12px;
  border: 0;
  background: transparent;
  color: #18324d;
  font: inherit;
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}
.report-status-bar__menu-item:hover {
  background: #eef5ff;
  color: #1c56a4;
}
.report-status-bar__menu-item:focus-visible {
  outline: 2px solid #2a6db3;
  outline-offset: -2px;
}

/* 26.1.0.58: the report-page status bar offsets to the right of
   the reports panel — same pattern the home page uses for its
   .site-footer (see body.route-home.has-reports-rail .site-footer
   at line ~1627). Two states matched by the existing body classes:
     - rail mode (panel collapsed to 56px): bar starts at left:56px
     - expanded mode (panel.is-open at 260px): bar starts at left:260px
   The .has-reports-rail class is applied to body whenever signed-in
   (set in components/app-header.js); .panel-open is added when the
   panel expands (also in app-header). Both classes also exist on
   /home, so the same selector pattern works here.
   The panel itself spans 100dvh on /report (no -24px override),
   so the status bar sits to the right of the panel's full height
   rather than crossing under it. The bar's left edge meets the
   panel's right edge; together they cover the full viewport
   bottom strip. */
body.route-report.has-reports-rail .report-status-bar {
  left: 56px;
}
body.route-report.has-reports-rail.panel-open .report-status-bar {
  left: 260px;
}

/* 26.1.0.48: the item highlight rule used to live here in 26.1.0.47
   but its specificity tied with the later `.reports-panel__item`
   base rule (both single-class selectors), and the base rule's
   `background: transparent` was winning by source order. Moved to
   sit right after the base + :hover rules — see further down in
   this file under "26.1.0.48". */

/* 26.1.0.47: align the tiles-view background with the reports
   panel's chrome grey (#f0f0f0) so the two surfaces read as one
   continuous left-to-right band. Previously the tiles area
   inherited .report-page's background:#f2f6fb (a faint blue),
   which contrasted with the grey panel and made the seam between
   them visible. Document mode keeps its white background — it's
   meant to read as paper. */
.report-page__content--tiles {
  background: #f0f0f0;
}

/* ── Rail state: signed-in on home, all viewport widths ──
   v944: media query removed entirely. User feedback on v943's 600px
   threshold: even at ~550px they want the rail collapsed-and-visible
   rather than flipping to the slide-over overlay. Rail is now always
   visible on signed-in home; JS (components/app-header.js,
   AUTO_COLLAPSE_BREAKPOINT) handles the "was expanded on wide, got
   narrowed" case by auto-collapsing so a 260px expanded panel never
   leaves <300px of content area on laptop viewports. */
body.has-reports-rail .reports-panel {
  transform: translateX(0);
  width: 56px;
}
body.has-reports-rail .reports-panel.is-open {
  width: 260px;
}

/* v85j: in rail mode, keep the body visible as an empty grey
   spacer (just its children hidden) so the footer sticks to the
   bottom of the viewport instead of bunching up directly under
   the header. Previously .reports-panel__body had display:none in
   rail mode, which collapsed the panel to two touching 56px-tall
   blocks (header + footer) with a huge empty gap below.
   v878 (+1): the grouped report icons are now shown in the rail
   so users can launch reports directly from the collapsed panel
   (hover = native browser tooltip via title= attribute on each
   button). The previous blanket
     .reports-panel:not(.is-open) .reports-panel__body > *
     { display: none; }
   is replaced with targeted hides below (.__domain-header,
   .__type-header, .__item-title, .__empty) so the domain sections
   and report-item icons are kept while text labels + the
   "Loading…" / "No reports available." placeholders are hidden. */
.reports-panel:not(.is-open) .reports-panel__title,
.reports-panel:not(.is-open) .reports-panel__filter-bar {
  display: none;
}
.reports-panel:not(.is-open) .reports-panel__empty,
.reports-panel:not(.is-open) .reports-panel__domain-header,
.reports-panel:not(.is-open) .reports-panel__type-header,
.reports-panel:not(.is-open) .reports-panel__item-title {
  display: none;
}
/* Tighter gutters on the domain-section card so it fits inside the
   56px rail (default 14px side-margin would leave only 28px of
   inner width — less than the 32px icon). 6px side-margin yields
   44px inner card width; with the 1px border on each side and 4px
   of item-padding, the 32px icon sits centred with 2px of breathing
   room. The domain-body's white bg + rounded border still paints,
   so each domain still reads as its own grouped cluster in the
   rail — the visual hierarchy from the expanded panel (groups of
   icons, with a visible separator between groups) carries over. */
.reports-panel:not(.is-open) .reports-panel__domain-section {
  margin: 10px 6px 0;
}
.reports-panel:not(.is-open) .reports-panel__item {
  justify-content: center;
  padding: 6px 4px;
  gap: 0;
}
/* v879 (+1): rail hides vertical overflow entirely — icons beyond
   the visible bottom are clipped and the overflow-more button (see
   .reports-panel__rail-overflow) takes over as the reveal affordance.
   A native scrollbar on the 56px rail was visually noisy and ate
   ~15px of an already-tight column on Windows/Linux. */
.reports-panel:not(.is-open) .reports-panel__body {
  overflow-y: hidden;
}

/* Rail-overflow button: bottom-anchored chevron that expands the
   panel on click. Hidden by default; shown only when the panel is
   in rail state AND has-rail-overflow is set by syncRailOverflow().
   Height (32px) chosen to match the .reports-panel__toggle-btn hit
   target without crowding the icons above. Thin top border
   separates it from the last icon; matches the domain-body border
   tint so the rail reads as one continuous grey column. */
.reports-panel__rail-overflow {
  display: none;
  flex-shrink: 0;
  width: 100%;
  height: 32px;
  border: 0;
  border-top: 1px solid #dddddd;
  background: #f0f0f0;
  color: #1c4f82;
  cursor: pointer;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: background 0.12s;
}
.reports-panel.has-rail-overflow:not(.is-open) .reports-panel__rail-overflow {
  display: flex;
}
.reports-panel__rail-overflow:hover {
  background: rgba(28, 79, 130, 0.08);
}
.reports-panel__rail-overflow svg {
  display: block;
}
/* v85l: the whole panel goes grey in rail mode (not just the three
   inner sections). Painting the panel's own background ensures that
   any child without its own bg (e.g. if an override misses) can't
   leak the base white through. Header border-bottom is also dropped
   in rail mode — it was creating a 1px seam halfway down the rail
   between header and body-spacer, making the supposedly-uniform
   grey strip read as two stacked blocks. */
.reports-panel:not(.is-open) {
  background: #f0f0f0;
}
.reports-panel:not(.is-open) .reports-panel__header {
  /* v887 (+1): rail header keeps the navy brand cap too — the base
     .reports-panel__header rule below sets #002552 and this rule
     no longer re-asserts grey. Border-bottom stays zero in rail so
     the navy cap meets the grey body-spacer without a seam.
     Previously the whole column was a continuous grey; the navy
     cap is now the brand-zone on both states. */
  border-bottom: 0;
}
.reports-panel:not(.is-open) .reports-panel__body {
  background: #f0f0f0;
  padding: 0;
}

.reports-panel__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 56px;
  min-height: 56px;
  /* v937: padding-left 16 → 14 so the logo (after also dropping
     the title's own horizontal padding below) aligns vertically
     with the left edge of the "All" dropdown in the filter-bar
     (which has padding: 10px 14px). Previously the logo's left
     edge sat at 16+12=28px from the panel edge while the dropdown
     sat at 14px — the two chrome elements looked staggered. */
  padding: 0 10px 0 14px;
  /* v887 (+1): navy brand cap matching logo.svg's primary fill
     (#002552) and the bi-cycle.com site header. Scoped to the
     header row only — the body + filter-bar stay on their light
     palette so the navy reads as a deliberate brand zone, not an
     inverted whole panel. Previously this was #f0f0f0 (grey, v85i)
     to match the analysis-page settings-bar; that alignment is now
     lost on home, but the settings-bar is only relevant on the
     analysis route so there's no cross-route clash.
     v888 (+1): flipped from #002552 to #669ACC to match the exact
     shade on bi-cycle.com's .headerDiv (user confirmed via DOM
     inspector). White text/toggle still passes WCAG AA on this
     lighter blue (contrast ratio ≈4.7:1 for 16px).
     v928: flipped from #669ACC → #ffffff now that the logo's top
     gear has been recolored (v927) to sky-blue; it previously
     needed a coloured backdrop because the gear was white and
     invisible on white. Hairline divider keeps the header
     demarcated from the body.
     v929: tweaked to #f0f0f0 (same grey as the analysis-page
     settings-bar and the public nav brand chip). Reads as a
     subtle chrome-strip rather than flat white against the body. */
  background: #f0f0f0;
  color: var(--brand-dark);
  /* v938: separator darkened from var(--line) (#dadada) to #b8b8b8.
     Against the new #f0f0f0 chrome bg, the var(--line) hairline only
     had 22 points of luminance contrast — visibly thinner than the
     Windows client's ~#9F-#B0 section separators (user feedback:
     "panel separators are too light"). #b8b8b8 gives ~56 points of
     contrast, reads clearly as a section break, still lighter than
     Windows' draggable splitter ridges (~#7C) so it doesn't imply
     interactivity. Scoped to the reports-panel — global var(--line)
     is kept at #dadada so generic card borders throughout the app
     don't get visually heavier. */
  border-bottom: 1px solid #b8b8b8;
  flex-shrink: 0;
}
/* Rail header: center the toggle button, no side padding */
.reports-panel:not(.is-open) .reports-panel__header {
  justify-content: center;
  padding: 0;
}
.reports-panel__title {
  /* v887: holds the logo image now (was a text wordmark). Font-size
     + letter-spacing kept as defensive fallbacks in case the <img>
     fails to load — the alt text "BI-Cycle" then still reads.
     v928: fallback text colour back to brand-dark now that bg is
     white (v928 undid v887's navy → v888 blue switch). Only visible
     if the <img> 404s; keeping consistent.
     v937: horizontal padding dropped (was 4px 12px). With the
     chip bg+border now transparent and the content being an image,
     the 12px side padding only served to push the logo away from
     the header's left padding — producing the 28px total offset
     that misaligned it from the dropdown below. Now the logo's
     left edge comes entirely from the parent header's 14px
     padding-left, matching the filter-bar's 14px padding. */
  display: inline-flex;
  align-items: center;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0.01em;
  padding: 4px 0;
  border-radius: 10px;
  background: transparent;
  border: 1px solid transparent;
  color: var(--brand-dark);
  line-height: 1;
}
.reports-panel__title-logo {
  /* v888 (+1): 40px per user spec. Panel header is 56px tall,
     so the logo has 8px breathing room top/bottom. Public-nav
     brand-mark__logo stays at 28px — the in-panel logo is meant
     to read more prominently as the "app shell" brand. */
  display: block;
  height: 40px;
  width: auto;
}
.reports-panel__toggle-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  /* v928: header bg flipped back to white (v927 fixed the logo's
     invisible top gear so the coloured backdrop is no longer
     needed). Icon goes dark navy for contrast on white; hover
     tint is a brand-dark overlay. */
  color: var(--brand-dark);
  cursor: pointer;
  transition: background 0.15s;
  flex-shrink: 0;
}
.reports-panel__toggle-btn:hover { background: rgba(23, 58, 99, 0.08); }

.reports-panel__body {
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 4px 0 12px;
  /* v937: dropped the explicit bg (#f0f4f9). The panel's base
     background is now #f0f0f0 (v937 above), which this body
     inherits — so all four sections (header, filter-bar, body,
     footer) read as the same grey with hairline separators
     between them, matching the Windows client's sidebar. */
}

.reports-panel__filter-bar {
  flex-shrink: 0;
  padding: 10px 14px;
  /* v938: darkened from var(--line) to #b8b8b8 — see rationale at
     .reports-panel__header border-bottom (v938). Separator now
     reads clearly against the #f0f0f0 chrome matching the Windows
     client's visible section separators. */
  border-bottom: 1px solid #b8b8b8;
}

.reports-panel__cat-select {
  /* 26.1.0.44: padding-right bumped 10px → 28px so the native chevron
     has a dedicated gutter and the selected option text doesn't run
     into it (Chrome on Windows draws the chevron ~16px wide, hugging
     the inner edge of the right border). box-sizing: border-box keeps
     `width: 100%` honest against the 1px border + the new right
     padding so the select still fits its container. */
  width: 100%;
  height: 34px;
  padding: 0 28px 0 10px;
  box-sizing: border-box;
  border: 1px solid #b9cde0;
  border-radius: 7px;
  background: #fff;
  font: inherit;
  font-size: 13.5px;
  color: #18324d;
  cursor: pointer;
  appearance: auto;
}
.reports-panel__cat-select:focus {
  outline: 2px solid #2d74c4;
  outline-offset: 1px;
}

/* Domain section (primary grouping) */
.reports-panel__domain-section {
  margin: 12px 14px 0;
}
.reports-panel__domain-section + .reports-panel__domain-section {
  margin-top: 16px;
}
.reports-panel__domain-header {
  padding: 0 2px 7px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #1c3a5c;
}

.reports-panel__domain-body {
  background: #ffffff;
  border: 1px solid #dddddd;
  border-radius: 10px;
  overflow: hidden;
  padding: 0;
}

.reports-panel__type-header {
  padding: 8px 14px 7px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #5b7ea8;
  background: #f0f5fb;
  border-top: 1px solid #e1e1e1;
  border-bottom: 1px solid #e1e1e1;
}

.reports-panel__item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 14px;
  border: 0;
  border-top: 1px solid transparent;
  background: transparent;
  color: #18324d;
  font: inherit;
  font-size: 13.5px;
  text-align: left;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
  white-space: nowrap;
}
.reports-panel__item + .reports-panel__item {
  border-top-color: #edf2f8;
}
.reports-panel__item:hover {
  background: #eef5ff;
  color: #1c56a4;
}
.reports-panel__item:disabled {
  opacity: 0.6;
  cursor: wait;
}

/* 26.1.0.48: highlight the report item that matches the current
   /report route. Same single-class specificity as the base
   .reports-panel__item rule above, so this MUST come after the
   base + :hover rules to win the cascade (source-order tiebreak).
   The 26.1.0.47 attempt had this block placed near the top of
   the file with the panel layout rules, which lost to the base
   `background: transparent` and the :hover `background: #eef5ff`.
   No left border / padding shift this round — the user's
   reference shows just a coloured background and bolder title,
   so we keep the icon + text fully aligned with non-current rows.
   The :hover rule above is allowed to win when the user's mouse
   is over the active item — that's normal hover feedback and
   doesn't conflict visually because both shades are blue. */
.reports-panel__item--current,
.reports-panel__item--current:hover {
  background: #cfe1f7;
  color: #18324d;
}
.reports-panel__item--current .reports-panel__item-title {
  font-weight: 600;
}

.reports-panel__item-icon {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  object-fit: contain;
  border-radius: 5px;
}
svg.reports-panel__item-icon {
  flex-shrink: 0;
}

.reports-panel__item-title {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 13.5px;
}

.reports-panel__empty {
  padding: 20px;
  color: var(--muted);
  font-size: 14px;
}

/* Panel footer — user button (moved from top nav) */
.reports-panel__footer {
  flex-shrink: 0;
  position: relative;
  /* v871: add a thin left border in addition to the existing top
     border so the user control reads as a framed cell at the bottom
     of the sidebar rather than blending into the panel body.
     v875: background switched from white to #f0f0f0 so the footer
     matches the "AL-Cycle" header strip at the top of the panel —
     both now read as chrome bands bracketing the white list body.
     v938: borders darkened from var(--line) (#dadada) to #b8b8b8 —
     see rationale at .reports-panel__header border-bottom (v938).
     All three sidebar separators (header-bottom, filter-bar-bottom,
     footer-top/left) now share this darker hairline for visible
     section breaks against the Windows-match #f0f0f0 chrome. */
  border-top: 1px solid #b8b8b8;
  border-left: 1px solid #b8b8b8;
  /* v939: outer padding 8 → 4 per user request ("decrease the
     height of the User panel a bit"). Combined with the trigger
     padding and avatar size reductions below, total cell height
     goes from ~64px to ~44px — closer to the thin user-chip strips
     Windows apps typically put at the bottom of a navigation panel. */
  padding: 4px 10px;
  background: #f0f0f0;
}
.reports-panel__footer .account-menu__trigger {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  max-width: none;
  /* v939: inner padding 8 → 4 matching the outer footer padding
     change. The trigger's own hover/focus affordances (border, bg)
     stay the same — just less vertical air around the avatar+label. */
  padding: 4px 10px;
  border-radius: 8px;
  background: transparent;
  color: #18324d;
  border: 1px solid transparent;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
.reports-panel__footer .account-menu__trigger:hover {
  background: var(--soft-bg);
  border-color: var(--line);
}
.reports-panel__footer .account-menu__trigger:hover .account-menu__label {
  text-decoration: none;
}
.reports-panel__footer .account-menu__avatar {
  /* v875: white background so the initial(s) pop against the new
     grey #f0f0f0 footer strip. The default var(--soft-bg) avatar
     fill blended into the grey too much after the footer bg change.
     v939: 32 → 28px per the user-panel height reduction. The avatar
     is the tallest element in the cell, so shrinking it drives the
     overall height cut. Kept slightly larger than the 24px top-nav
     action icons so the user initials still read as a distinct
     identity cell rather than just another toolbar icon. */
  background: #ffffff;
  color: var(--brand-dark);
  width: 28px;
  height: 28px;
  border: 1px solid #e1e1e1;
}
.reports-panel__footer .account-menu__label {
  flex: 1;
  text-align: left;
  color: #18324d;
  /* v85j: not bold. The email address reads loud at 600 weight on
     a flat white footer; regular weight keeps the footer quiet
     relative to the active area above it. */
  font-weight: 400;
  max-width: none;
}
.reports-panel__footer .account-menu__chevron {
  color: var(--muted);
  /* v85k: bumped from 16px to 20px — user feedback was that the
     open/close arrow still read as incidental. 20px puts it on par
     with small action-button icons in the toolbar. */
  font-size: 20px;
  line-height: 1;
}

/* Popover pops UP from the footer button */
.reports-panel__footer .user-popover {
  position: absolute;
  top: auto;
  bottom: calc(100% + 2px);
  left: 10px;
  right: 10px;
  width: auto;
  z-index: 1060;
}

/* Rail mode: collapse footer trigger to just the avatar, centered */
.reports-panel:not(.is-open) .reports-panel__footer {
  /* v85j: grey bg + no top border so the rail reads as one uniform
     grey strip from top to bottom. The open state keeps its white
     footer + top-border (below) since the body above it is the
     lighter content colour. */
  background: #f0f0f0;
  border-top: 0;
  padding: 8px 4px;
}
.reports-panel:not(.is-open) .reports-panel__footer .account-menu__trigger {
  justify-content: center;
  padding: 6px 4px;
  gap: 0;
}
.reports-panel:not(.is-open) .reports-panel__footer .account-menu__label,
.reports-panel:not(.is-open) .reports-panel__footer .account-menu__chevron {
  display: none;
}

/* Overlay backdrop — used only when panel is in overlay mode (narrow screens) */
.reports-panel-overlay {
  position: fixed;
  inset: 0;
  background: rgba(10, 28, 55, 0.35);
  z-index: 1049;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.22s;
}
.reports-panel-overlay.is-visible {
  opacity: 1;
  pointer-events: auto;
}

/* ── Panel takes up space at all widths — rail 56px or expanded 260px ──
   v944: media query removed. See rationale at the .reports-panel rail
   block above. Body padding-left now always matches the visible sidebar
   width, so content is pushed (never covered). */
body {
  transition: padding-left 0.22s cubic-bezier(0.4,0,0.2,1);
}
body.has-reports-rail {
  padding-left: 56px;
}
body.has-reports-rail.panel-open {
  padding-left: 260px;
}
/* No overlay when panel is pushing content */
body.has-reports-rail .reports-panel-overlay,
body.panel-open .reports-panel-overlay {
  opacity: 0;
  pointer-events: none;
}
/* Top-nav hamburger is redundant when the rail is always visible */
body.has-reports-rail .panel-toggle-btn {
  display: none;
}

/* ── Nav links — collapse to "more" dropdown on narrow screens ── */
.nav-links-more-btn {
  display: none;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  border: 1px solid rgba(255,255,255,0.28);
  border-radius: 8px;
  background: rgba(255,255,255,0.12);
  color: #fff;
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}
.nav-links-more-btn:hover { background: rgba(255,255,255,0.22); }

.nav-links-dropdown {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  background: #fff;
  color: #18324d;
  border: 1px solid var(--line, #d6dde8);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(16,36,64,0.14);
  min-width: 160px;
  z-index: 200;
  padding: 6px 0;
}
.nav-links-dropdown a {
  display: block;
  padding: 10px 18px;
  font-size: 14px;
  font-weight: 500;
  color: #18324d;
  text-decoration: none;
}
.nav-links-dropdown a:hover { background: #f0f5fb; color: #1c56a4; }

.nav-links-more-wrap {
  position: relative;
}



/* ── Auth elements ── */
.sign-in-btn {
  display: grid;
  place-items: center;
  min-width: 86px;
  height: 40px;
  padding: 0 18px;
  border-radius: 12px;
  background: var(--brand-dark);
  border: 1px solid rgba(255, 255, 255, .26);
  color: #ffffff;
  font-size: 15px;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(23, 63, 107, 0.18);
  transition: background 0.15s ease;
}

.sign-in-btn:hover { background: #1f4770; }

.entra-auth__signed-in {
  display: flex;
  align-items: center;
  gap: 12px;
  color: #ffffff;
  font-size: 14px;
}

.entra-auth__name {
  max-width: min(200px, 42vw);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.entra-auth__sign-out {
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  color: #ffffff;
  background: rgba(255, 255, 255, 0.15);
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 8px;
  padding: 6px 12px;
  cursor: pointer;
}

.entra-auth__sign-out:hover { background: rgba(255, 255, 255, 0.25); }

.entra-auth__name-btn {
  font: inherit;
  color: inherit;
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
}

.entra-auth__name-btn:hover {
  text-decoration: underline;
}

.entra-auth__signed-in {
  position: relative;
}

.user-popover {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  left: auto;
  width: min(360px, calc(100vw - 16px));
  background: #fff;
  color: #18324d;
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 14px 32px rgba(16, 36, 64, 0.18);
  z-index: 40;
}

.user-popover__content {
  padding: 14px 16px;
  font-size: 14px;
}

.user-popover__section + .user-popover__section {
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid #e7edf4;
}

.user-popover__title {
  font-size: 15px;
  font-weight: 700;
  margin-bottom: 12px;
  word-break: break-word;
}

.user-popover__row {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  margin-top: 8px;
}

.user-popover__row span {
  color: var(--muted);
}

.user-popover__row strong {
  text-align: right;
  /* v85j: <strong> tags keep their semantic meaning but visually
     drop to medium weight — Role "Administrator" + Access Groups
     list should read as calm values, not headline emphasis. */
  font-weight: 500;
}

/* Access Groups row can be long when the user belongs to many groups —
   allow the value to wrap under the label instead of shrinking the
   chips into a single overflowing line.  Keeps label on the left but
   lets the value side claim more width as needed. */
.user-popover__row--wrap {
  align-items: flex-start;
}
.user-popover__row--wrap strong {
  flex: 1 1 auto;
  min-width: 0;
  word-break: break-word;
  line-height: 1.35;
}

/* 26.1.0.84: stacked variant for rows whose value can be long
   enough that label-on-left + value-on-right would force the
   value into a tall narrow column. The Access Groups row uses
   this when the user has many groups (8+ is common for some
   roles): label sits on its own line, value claims the full
   popover width below it. Halves the row's vertical footprint
   for typical multi-group cases without sacrificing readability.
   The label keeps its --muted colour from the base rule and the
   value keeps its medium weight. */
.user-popover__row--stacked {
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}
.user-popover__row--stacked strong {
  text-align: left;
  word-break: break-word;
  line-height: 1.4;
  width: 100%;
}

.user-popover__label {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
  margin-bottom: 8px;
}

.user-popover__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.user-popover__chip {
  display: inline-flex;
  align-items: center;
  padding: 6px 10px;
  border-radius: 999px;
  background: var(--soft-bg);
  color: var(--brand-dark);
  font-size: 12px;
  font-weight: 700;
}

.user-popover__chip.is-default {
  background: #dfeeff;
}

.user-popover__muted,
.user-popover__error {
  color: var(--muted);
}


/* ── Hero ── */
.hero-title {
  text-align: center;
  padding: 36px 20px 28px;
}

.hero-title h1 {
  margin: 0;
  font-size: clamp(2.3rem, 4vw, 3.4rem);
  line-height: 1.08;
  color: #000;
}

.hero-title p {
  margin: 14px 0 0;
  font-size: 1.35rem;
  color: #000;
}

/* ── Layout ── */
.container {
  max-width: 1580px;
  margin: 0 auto;
  padding: 8px 32px 48px;
}

/* v870: on the home route the 48px bottom padding pushes the footer
   well above the viewport edge, leaving a big empty strip below
   "© AL-Cycle. All rights reserved." Trim it there — every other
   route that uses .container (analysis, kpi, report) resets its
   padding via route-specific rules further down.
   v951 update: padding-bottom flipped from 0 to 24px to reserve
   status-bar space on the CONTAINER itself rather than on body.
   The previous v939/v945 approach (`body.route-home { padding-
   bottom: 24px }`) didn't work on the public home because the
   global `html, body { height: 100% }` rule (line ~7429) pins
   body to exactly 100vh regardless of content. On public home
   (pricing + features + about stacked flat), the .container's
   content exceeds 100vh and OVERFLOWS body. Body's padding-bottom
   reserves space inside body's fixed 100vh box, but the overflow
   content extends past that reserved region — so at scroll-max
   the last line of the About section landed at viewport.bottom
   and got covered by the fixed status bar.
   Putting padding-bottom on .container (the element that actually
   holds the overflowing flex children) makes the 24px reservation
   move with the content wherever it ends up in the scroll buffer.
   Signed-in home was never affected — its content is short enough
   to fit in 100vh, so no overflow, so body padding-bottom worked.
   But the container-padding fix is safe on both routes: on signed-
   in short-content pages, the `.container > section:last-child
   { margin-top: auto }` rule still pushes the (empty, position:
   fixed'd) footer section to the bottom; its mt-auto absorbs
   whatever space exists above the padding. */
/* v973: public home (route-home without .has-reports-rail) uses a
   position:fixed footer that covers the last 24px of the viewport.
   Content should end flush-to-footer with no trailing whitespace;
   container padding-bottom:0 zeroes the base .container's 48px
   bottom padding. The signed-in rule below adds 24px back for the
   .has-reports-rail case (content must clear the status bar
   there since sections are full-height panels rather than a short
   flow).
   v1008: 0 → 32px on public. The "flush-to-footer" intent of v973
   actually meant the fixed footer painted OVER the last ~24px of
   the About card — the bottom of the About text was hidden behind
   the © line. User screenshot annotation: "needs a small margin
   underneath it before the fixed footer starts". 32px = 24px (the
   fixed footer's height, which would otherwise be covered by the
   footer painting on top) + 8px of visible gap between the About
   card's bottom edge and the footer's top edge. */
body.route-home:not(.has-reports-rail) .container {
  padding-bottom: 32px;
}
body.route-home.has-reports-rail .container {
  padding-bottom: 24px;
}

/* v871: make the home .container a flex column that fills the
   viewport height minus the sticky 56px app-header, with the footer
   section as a mt-auto spacer at the end. This aligns the site-footer
   horizontally with the bottom of the sidebar's user-control (which
   is always flush to viewport bottom in rail mode) instead of
   floating ~44px above it on short-content pages. Uses min-height so
   long-content pages still scroll normally — the footer just sits
   below the last section as before.
   v972: min-height scoped to .has-reports-rail (signed-in) only.
   Public home uses a position:fixed status bar footer that's
   anchored to the viewport, so the container doesn't need to be
   at-least-viewport-height to push anything. The min-height was
   causing a vertical scrollbar on public because the container's
   min (viewport - 56px) + the 56px header = exactly 100vh, but
   any 1-2px subpixel rounding or line-height drift could push it
   over and trigger the html scrollbar. Removing the min-height
   lets public home size exactly to its content. */
body.route-home .container {
  display: flex;
  flex-direction: column;
}
body.route-home.has-reports-rail .container {
  min-height: calc(100vh - 56px);
}
body.route-home .container > section:last-child {
  margin-top: auto;
}

/* v871: site-footer's own spacing trimmed on home so the footer rule
   sits close to its text rather than ~24px above. The push-to-bottom
   logic above handles the big vertical offset; the in-footer padding
   just needs enough room for visual breathing.
   v939: signed-in home only — overridden below with a fixed-bottom
   Windows-style status bar. Public home keeps this compact in-flow
   layout. */
body.route-home .site-footer {
  margin-top: 0;
  padding-top: 10px;
  padding-bottom: 8px;
}

/* v939: turn the footer into a Windows-style status bar on
   signed-in home. Fixed to the viewport bottom spanning full width,
   24px tall, grey chrome with a single hairline top border matching
   the v938 sidebar/topbar separators. The body gets padding-bottom
   24px and the sidebar height is `calc(100dvh - 24px)` so nothing
   renders UNDER the status bar. z-index sits above the reports-
   panel (1050) so the status bar paints over the very bottom of
   the sidebar's vertical hairline rather than stopping at x=260
   — this matches the Windows client's status bar which spans the
   full app width regardless of any docked panels.
   Scoped to .has-reports-rail (signed-in) so the public marketing
   home keeps its roomier in-flow legal line. */
body.route-home .site-footer {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 24px;
  margin: 0;
  /* v972: padding removed from the footer itself so the
     .site-footer__brand (sole visible child on public + signed-in)
     can take over horizontal positioning via its own max-width +
     auto margins. Previously (v970) the padding was 0 32px which
     aligned the text with .container content on viewports ≤ 1580px
     but drifted off on wider screens — the footer spans full
     viewport while .container is capped at max-width:1580px with
     auto margins. Now .site-footer__brand mirrors .container's
     sizing (max-width:1580 + margin:0 auto + padding-left:32px)
     so the copyright text sits at the same x as the Process
     kicker / Pricing kicker / About heading regardless of
     viewport width. */
  padding: 0;
  display: flex;
  align-items: center;
  background: #f0f0f0;
  border-top: 1px solid #b8b8b8;
  z-index: 1100;
  /* .site-footer base rule uses a 2-column grid; override here so
     the status bar lays out as a simple flex row. */
  grid-template-columns: none;
  gap: 0;
}
/* v939: hide the empty links grid on the status bar. On signed-in
   home the footer data is passed with columns:[] (see home-page.js
   line ~62), so the grid is visually empty anyway — but removing
   it from layout keeps the flex row tidy if columns are ever added
   back without a corresponding status-bar redesign. */
body.route-home .site-footer__links {
  display: none;
}
body.route-home .site-footer__brand {
  /* v972: mirror .container sizing (max-width:1580 + margin:0 auto
     + 32px left gutter) so the "© 1996-2026 BICycle BV" line
     sits at the same x as the .container content above on any
     viewport. The base rule at ~line 7050 just makes it a flex
     column with small gap — those don't interact with the
     horizontal positioning we need here. */
  margin: 0 auto;
  width: 100%;
  max-width: 1580px;
  padding: 0 32px;
  box-sizing: border-box;
}
body.route-home .site-footer__legal {
  font-size: 12px;
  color: var(--muted);
  line-height: 1;
}

/* v941: user feedback reversal — sidebar should extend to the
   viewport bottom rather than stop 24px above it (v939's reserved
   status-bar space). Status bar instead shifts right so its left
   edge meets the sidebar's right edge. Sidebar reads as a full-
   height dock column; status bar reads as a strip only under the
   main content area — matching the Windows client's sidebar-plus-
   status-bar arrangement more faithfully than v939's "status bar
   spans under everything" layout.
   The base .reports-panel rule (line ~455) still uses
   `calc(100dvh - 24px)` as a defensive default — this override
   just re-claims the bottom 24px on signed-in home where we
   actually want it. */
body.route-home.has-reports-rail .reports-panel {
  height: 100dvh;
}

/* v944: status bar left offset no longer needs media queries.
   Rail is always visible (see v944 CSS above) so the sidebar's
   right edge is always at 56px (rail) or 260px (expanded). The
   .panel-open selector distinguishes the two states — no viewport-
   width check needed. The v941 media query variant existed because
   at <900px the sidebar was hidden (translateX -100%) and the
   status bar had to span full-width to avoid a gap; that mode is
   gone in v944. */
body.route-home.has-reports-rail .site-footer {
  left: 56px;
}
body.route-home.has-reports-rail.panel-open .site-footer {
  left: 260px;
}

/* v940: signed-in home sub-panels flattened to the Windows client's
   property-dialog layout (user reference: "Design Sub Heading"
   dialog — sections stack on grey bg with plain bold titles and
   hairline separators between them, no rounded-card chrome).
   Scoped to route-home.has-reports-rail so the public (signed-out)
   marketing home keeps its pill-kicker + rounded-card styling —
   those are marketing surfaces where the card chrome reads as
   intentional design, not app chrome. */
body.route-home .section-panel {
  /* Strip the card treatment: transparent bg (reveals the v939
     #f0f0f0 body chrome), no border, no radius, no margin between
     panels (separator handled below). Horizontal padding zeroed
     because the outer .container already provides 32px gutters;
     vertical 12px keeps a compact header-to-content rhythm. */
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 12px 0;
  margin: 0;
}
/* v940: hairline separators between consecutive stacked panels.
   Target by ID rather than sibling-selector (`+`) because the
   outer <section> wrappers aren't DOM-adjacent — .section-panel
   lives one level deeper, inside <summary-section>, <section
   id="bad-actors">, etc., and CSS sibling selectors only see the
   immediate parent's children. Explicit IDs are also more robust
   to the pricing/features/about <section>s (hidden on signed-in
   home) potentially becoming visible in future without re-sorting
   separator rules. */
body.route-home.has-reports-rail #al-cycle .section-panel {
  /* v949: separator rule updated from #bad-actors + #trends (v940)
     to #al-cycle after those two sections were replaced with a
     single AI chat panel. Summary is first visible, al-cycle is
     second — so al-cycle gets a hairline top-border, summary
     doesn't. Same #b8b8b8 hairline as the other v938/v940 chrome
     separators on home. */
  border-top: 1px solid #b8b8b8;
}

/* v945: same separator treatment on the public (signed-out) home.
   Different ID structure: pricing/features render
   <section class="section-panel" id="..."> directly (the id and
   class are on the same element), whereas bad-actors/trends wrap
   <section class="section-panel"> inside an outer <section id="...">.
   That means the selector for public hits the element itself (no
   descendant `.section-panel` needed). Visible order on public is
   pricing → features → about with summary hidden, so pricing gets
   no separator (first visible) and features/about each get one.
   Public is centered by the base .container max-width:1580 rule,
   so separators span the inner 1516px gutter width.
   v971: pricing also gets a separator now that the Process section
   sits above it on public. Order on public is now
   process → pricing → about (summary + features hidden); process
   is first-visible (no separator), pricing + about each get one.
   26.1.0.8: the #about target was replaced by #about-contact, the
   wrapper section that holds About + Contact in a 2-column grid.
   Same hairline #b8b8b8 rule applies — visually consistent with the
   pricing separator above. The wrapper's own border-top spans the
   full container width regardless of which child cards are inside,
   so the line stays uniform across page widths. */
body.route-home:not(.has-reports-rail) #pricing,
body.route-home:not(.has-reports-rail) #features,
body.route-home:not(.has-reports-rail) #about-contact {
  border-top: 1px solid #b8b8b8;
}

body.route-home .section-kicker {
  /* v940: strip pill treatment (bg, radius, padding). Title
     becomes plain bold uppercase text. "A bit larger than the
     Windows App" per user: Windows "Identification" title in the
     reference dialog reads ~12px; 14px keeps a slight size boost
     without breaking the compact layout. Bottom margin 8px gives
     title→content breathing room without the previous 16px gap.
     v950: text-transform:none added so section titles render
     as-typed rather than force-uppercasing. User asked for
     "Data Summary" and "AI-Cycle" literally (not DATA SUMMARY /
     AI-CYCLE) — previously the base .section-kicker rule's
     `text-transform: uppercase` overrode whatever case the source
     string used. Affects all route-home kickers (signed-in and
     public) for visual consistency across the whole home shell. */
  display: block;
  background: transparent;
  border-radius: 0;
  padding: 0;
  margin: 0 0 8px;
  font-size: 14px;
  text-transform: none;
}
/* v973: public home kickers get a larger font (18px vs signed-in's
   14px). User requested "section titles can be somewhat larger
   font" — rather than bump every route-home kicker (which would
   break the signed-in home's compact Windows-app feel where the
   kickers sit next to data-dense content), we only inflate on the
   public marketing home. :not(.has-reports-rail) is the same
   selector the separator-lines above use to scope public-only
   rules. */
body.route-home:not(.has-reports-rail) .section-kicker {
  font-size: 18px;
  margin-bottom: 10px;
}

/* v940 (cont'd): .container no longer needs its 8px top padding
   on signed-in home now that the section-panel itself provides
   its own top padding — otherwise the FIRST kicker ("SUMMARY")
   sat ~20px below the top-nav when 8-12px is more Windows-like. */
body.route-home .container {
  padding-top: 0;
}

/* v942: main-panel left-aligned on signed-in home, matching the
   Windows client. Base .container has `max-width: 1580px; margin: 0
   auto` to center the marketing public home on wide screens — but
   on signed-in home that centering combined with a rail sidebar
   left the main content floating ~180px to the right of the
   sidebar's right edge on wide viewports (e.g. on a 2000px viewport:
   56px rail + 182px left auto-margin + 32px container padding =
   270px of empty grey before SUMMARY started). User wants content
   flush against the sidebar's gutter like the Windows client.
   Override: drop max-width entirely and use `margin-left: 0` so the
   container claims all available horizontal space after the
   sidebar. Horizontal padding (32px) stays as a small gutter so
   content doesn't butt against the sidebar border. Public
   (signed-out) home still inherits the base rule with the 1580
   cap + auto-centering for marketing readability. */
body.route-home.has-reports-rail .container {
  max-width: none;
  margin-left: 0;
  margin-right: 0;
}

/* v1006: signed-in home content (Data Summary, AI-Cycle, footer
   © line) left-aligned with the top-nav search pill instead of the
   container's 32px gutter. User reference screenshot: the section
   labels and panels were sitting ~100px to the LEFT of the search
   box's left edge — they wanted everything on a single vertical x.
   Math (where 100% = container/footer width = vw - sidebar-width):
     - top-nav has padding `0 20px 0 0` (no left pad, 20px right pad)
     - .nav-brand is `display:none` in rail mode so .top-search-wrap
       (grid-column 2 of `auto minmax(0, 1fr)`) fills the nav row
     - search-wrap inner width = 100% - 20px
     - search-pill width = min(1100px, calc(100% - 20px - 48px))
                        = min(1100px, calc(100% - 68px))
       (the 48px is the `width: min(1100px, calc(100% - 48px))` rule
        on .top-search itself, applied relative to the wrap's width)
     - search-pill is centered in the wrap (`justify-content: center`),
       so its left offset from the body content start is
       (wrap-width - pill-width) / 2 = (100% - 20px - pill-width) / 2
   Sections take pill-width as their width and the offset as
   margin-left, with margin-right:auto so the right edge mirrors
   the pill's right edge as well. Container's own 32px horizontal
   padding is dropped here so the section calc resolves against the
   raw body content width. Padding-bottom (24px from v951) is left
   alone — it reserves the fixed status-bar's 24px strip. */
body.route-home.has-reports-rail .container {
  padding-left: 0;
  padding-right: 0;
}
body.route-home.has-reports-rail .container > section {
  --search-pill-width: min(1100px, calc(100% - 68px));
  width: var(--search-pill-width);
  margin-left: calc((100% - 20px - var(--search-pill-width)) / 2);
  margin-right: auto;
}
/* Footer brand (the "© 1996-2026 BICycle BV" line) mirrors the same
   calc. Overrides the v972 default (max-width:1580 + `padding: 0
   32px`) for signed-in home only — public marketing home keeps the
   1580/32 sizing since its top-nav layout is different (.top-nav__inner
   uses max-width:1580 + auto margins, not a search pill, so the
   public home's content wants to mirror THAT instead). The base
   .site-footer is `position: fixed; left: 56px; right: 0;` (or
   `left: 260px` when panel-open), so 100% inside .site-footer__brand
   == body content width — same coord system as the calc above. */
body.route-home.has-reports-rail .site-footer__brand {
  --search-pill-width: min(1100px, calc(100% - 68px));
  max-width: none;
  margin: 0;
  padding: 0;
  padding-left: calc((100% - 20px - var(--search-pill-width)) / 2);
}

/* v941: summary sub-table treatment on signed-in home — grey
   header strip with a compact title, then white data rows. User
   reference: "summary tables should have a grey top and white
   background rows; the top can be less height and less font-size."
   The effect reads as a small Windows-style data table with a
   chrome header band, sitting inside the v940 flat SUMMARY
   sub-panel.
   Scoped to route-home.has-reports-rail so the public (signed-out)
   marketing home keeps the roomier original design (same
   discipline as v940 for the .section-panel flattening). */
body.route-home.has-reports-rail .summary-block {
  /* Base bg flipped from #fbfdff (near-white blue-tinted) to pure
     white, so the data rows — which inherit or explicitly set
     white — read as a solid white table body against the v939
     #f0f0f0 sub-panel chrome. */
  background: #ffffff;
}
body.route-home.has-reports-rail .summary-block-head {
  /* Grey chrome header matching the top-nav + sidebar chrome
     (#f0f0f0, v938). min-height 50 → 32 and padding tightened to
     6/14 per user's "less height" ask. Hairline bottom-border
     demarcates the chrome header from the white data rows using
     the same #b8b8b8 as the other v938 chrome separators. */
  background: #f0f0f0;
  min-height: 32px;
  padding: 6px 14px;
  border-bottom: 1px solid #b8b8b8;
}
body.route-home.has-reports-rail .summary-block h3 {
  /* Title font 22 → 14px per user's "less font-size" — matches
     the v940 .section-kicker size so the hierarchy reads as
     "sub-panel title (14px) > sub-table title (14px)" at the same
     weight but with the chrome band doing the visual separation
     instead of a size delta. Colour shifted from near-black #111
     to --brand-dark, matching the v940 kicker colour. */
  font-size: 14px;
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--brand-dark);
}
body.route-home.has-reports-rail .summary-row,
body.route-home.has-reports-rail .summary-row:nth-child(even) {
  /* Zebra stripe (the nth-child(even) #efefef) removed per user's
     "white background rows". All rows now white, relying on the
     existing border-top row separator for visual row-division. */
  background: #ffffff;
}

/* v939: signed-in home main-content background flipped from the
   body-default white to the Windows-chrome grey #f0f0f0 (same value
   as the sidebar + top-nav chrome from v938). The SUMMARY / BAD
   ACTORS / TRENDS cards are all white-filled themselves, so the
   change reads as "white content cards floating on a grey chrome
   canvas" — the same treatment the Windows client uses for its
   dashboard surfaces. Scoped to route-home.has-reports-rail (i.e.
   signed-in on home) so:
   (a) the public marketing home stays on the brand white bg, and
   (b) other signed-in routes (analysis, kpi, carousel) keep their
       own route-specific backgrounds. */
body.route-home {
  background: #f0f0f0;
}

/* v939: signed-in home is the only route that gets the Windows-
   style fixed-bottom status bar (see .site-footer rules below).
   Reserve 24px of viewport bottom via padding-bottom on the body
   so content above doesn't render UNDER the status bar. 24px
   height matches the status bar rule below — if you change one,
   change both. Public home (no .has-reports-rail) still gets
   padding-bottom: 0 via the v870 rule above.
   v951 REVERSAL: padding-bottom moved off body entirely. The
   global `html, body { height: 100% }` rule (line ~7429) pins
   body to a fixed 100vh, so padding-bottom here only reserved
   space INSIDE body's 100vh box — content that overflows body
   (e.g. public home's pricing+features+about stack) extends past
   that reserved area. See the v951 comment on the v870 rule
   above for the full story. The .container padding-bottom:24px
   fix there now owns the status-bar reservation for both signed-
   in and public home. */
body.route-home {
  /* padding-bottom intentionally left at browser default (0) —
     see v951 note. */
}
body.route-home.has-reports-rail .container {
  /* The min-height calc needs to discount the 24px status bar on
     top of the 56px sticky header; otherwise the container still
     claims the full `100vh - 56px` and pushes the legal-line
     section 24px below the viewport (where the status bar then
     covers it).
     v951: back to calc(100vh - 56px). The v939 extra `- 24px`
     was paired with a body padding-bottom:24 that didn't actually
     work (see v951 comment on the v870 rule). Now that the
     container owns padding-bottom:24 itself, border-box math is
     simpler: total container min-height = 100vh - 56, content
     area (min-height minus bottom-padding) = 100vh - 56 - 24 =
     the 80vh space the v939 rule was originally trying to claim.
     v973: scoped to .has-reports-rail (signed-in) only. This was
     THE rule causing the public home's persistent vertical
     scrollbar across v971/v972 — an earlier v972 fix at line
     ~1345 removed min-height from the first .container rule, but
     this DUPLICATE rule at 1686 re-applied it to ALL route-home,
     defeating the fix. Scoping both to .has-reports-rail finally
     removes the min-height on public where the fixed-position
     footer makes it unnecessary. */
  min-height: calc(100vh - 56px);
}

/* ── Section panel (shared wrapper) ── */
.section-panel {
  border: 1px solid var(--line);
  background: #fff;
  padding: 22px;
  margin-bottom: 18px;
}

.section-kicker {
  display: inline-block;
  margin-bottom: 16px;
  padding: 7px 13px;
  border-radius: 999px;
  background: var(--soft-bg);
  color: var(--brand-dark);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
}

/* ── Loading / error states ── */
.section-loading,
.section-error {
  padding: 20px 0;
  color: var(--muted);
  font-size: 14px;
  margin: 0;
}

/* v86j: upgrade .section-error from a single line of muted red text to a
   visible inline alert. Matches the house error severity from
   notification-service: red left border, tinted background, icon prefix.
   Prevents the "Failed to load…" message from reading like the page is
   still loading. */
.section-error {
  color: #9b2c2c;
  background: #fff5f5;
  border-left: 4px solid #c53030;
  padding: 14px 18px;
  margin: 16px;
  border-radius: 4px;
  font-size: 14px;
  line-height: 1.5;
  max-width: 720px;
}
.section-error::before {
  content: "⚠ ";
  margin-right: 6px;
  font-weight: 600;
}

/* ── Summary cards ── */
.summary-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 14px;
  align-items: start;
}

.summary-block {
  border: 1px solid var(--line);
  background: #fbfdff;
}

.summary-block-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 10px 4px 14px;
  min-height: 50px;
}

.summary-block h3 {
  margin: 0;
  font-size: 22px;
  color: #111;
}

.summary-rows-scroll {
  max-height: 230px;
  overflow-y: auto;
}

.summary-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  min-height: 46px;
  padding: 0 10px 0 12px;
  border-top: 1px solid #d4deea;
}

.summary-row:nth-child(even) { background: #efefef; }

.summary-item {
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
  color: var(--brand-dark);
  font-size: 15px;
}

.summary-item span { line-height: 1.2; }

.summary-icon {
  width: 18px;
  height: 18px;
  flex: 0 0 18px;
  object-fit: contain;
  display: block;
}

.summary-icon--placeholder {
  background: var(--soft-bg);
  border-radius: 3px;
}

.summary-actions {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  white-space: nowrap;
}

.summary-count {
  min-width: 28px;
  text-align: right;
  color: #1c56a4;
  font-size: 15px;
}

/* ── Pricing ── */
.pricing-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 18px;
  align-items: start;
}

.pricing-card {
  display: flex;
  flex-direction: column;
  border: 1px solid #d4e2ef;
  border-radius: 14px;
  padding: 20px;
  background: #ffffff;
  box-shadow: 0 2px 8px rgba(23, 63, 107, 0.06);
  transition: box-shadow 0.15s;
}

.pricing-card:hover { box-shadow: 0 4px 18px rgba(23, 63, 107, 0.11); }

.pricing-card--primary {
  border-color: var(--brand);
  border-width: 2px;
  padding: 19px;
}

.pricing-card-head {
  display: flex;
  align-items: center;
  gap: 11px;
  margin-bottom: 14px;
}

.pricing-icon {
  /* v1013: 28→40px per user direction. The earlier 28px sizing was
     calibrated for the legacy SVG icons (organisation_hierarchy /
     resource / construction_assembly) which had heavier strokes
     and read clearly at small sizes; the v1011 PNG icons cropped
     from the user's composite are line-art at thinner stroke
     weights and need more pixels to read crisply next to the 22px
     pricing-title text. 40×40 sits well-proportioned beside the
     title without competing with it. */
  width: 40px;
  height: 40px;
  object-fit: contain;
  flex-shrink: 0;
}

.pricing-title {
  margin: 0;
  font-size: 22px;
  font-weight: 700;
  color: var(--brand-dark);
}

.pricing-price {
  margin: 0 0 10px;
  font-size: 20px;
  font-weight: 600;
  color: var(--brand-dark);
}

.pricing-price--muted { color: var(--muted); }

.pricing-price-period {
  font-size: 13px;
  font-weight: 500;
  color: var(--muted);
}

.pricing-note {
  margin: -4px 0 10px;
  font-size: 13px;
  color: var(--muted);
  line-height: 1.4;
}

.pricing-divider {
  border: none;
  border-top: 1px solid #e2ecf4;
  margin: 0 0 12px;
}

.pricing-list {
  flex: 1;
  margin: 0 0 12px;
  padding: 0;
  list-style: none;
}

.pricing-list li {
  position: relative;
  padding-left: 17px;
  margin-bottom: 5px;
  font-size: 13px;
  color: #2a4260;
  line-height: 1.35;
}

.pricing-list li::before {
  content: "\2713";
  position: absolute;
  left: 0;
  top: 0;
  color: var(--brand);
  font-weight: 700;
  font-size: 11px;
  line-height: 1.55;
}

.pricing-footer {
  margin: 0;
  padding-top: 10px;
  border-top: 1px solid #e2ecf4;
  font-size: 12px;
  color: var(--muted);
  line-height: 1.4;
}

/* ── Features ── */
.feature-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(280px, 1fr));
  gap: 18px;
}

.feature-card {
  border: 1px solid #dbdbdb;
  border-radius: 14px;
  padding: 18px 20px;
  background: #ffffff;
}

.feature-card h3 {
  margin: 0 0 12px;
  font-size: 18px;
  color: var(--brand-dark);
}

.feature-card ul {
  margin: 0;
  padding-left: 20px;
}

.feature-card li {
  margin-bottom: 8px;
  font-size: 14px;
  line-height: 1.4;
  color: #2a4260;
}

/* ── About ── */
.about-content {
  display: flex;
  align-items: flex-start;
  gap: 24px;
}

/* v976: on the public home, the about-content (icon + text) reads
   as a bordered white card matching the process-card and
   pricing-card chrome. Scoped to public only — signed-in home
   barely shows About (it's hidden), and the card treatment is
   part of the marketing-home rhythm. Process cards use the same
   border + radius + subtle shadow combination for visual
   consistency across the home's three sections (Process,
   Pricing, About). */
body.route-home:not(.has-reports-rail) #about .about-content {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 22px 26px;
  box-shadow: 0 1px 3px rgba(23, 63, 107, 0.04);
  align-items: center;
}

.about-image img {
  /* v948: width 120 → 80, add max-height 100px. Previously the
     illustration extended below the two-paragraph text block on
     signed-in / public home (user feedback: "the bottom About
     section SVG does not fit"). SVG's natural aspect ratio + 120px
     width pushed the image tall enough to get clipped by the
     status bar at the viewport bottom on mid-height screens, and
     on taller viewports it just read as over-sized next to the
     compact text. max-height caps any future SVG swap so a taller
     artwork doesn't reintroduce the same overflow. */
  width: 80px;
  max-height: 100px;
  height: auto;
  display: block;
}

.about-text { flex: 1; }

.about-text p {
  /* v973: color flipped from var(--muted) (#506b8b, a muted blue-
     grey) to #18324d which is the app-wide body color (set on
     body at line ~33). User asked for "normal black" in the About
     section — the muted blue-grey read as secondary / de-
     emphasised on a white card, the body-default reads with
     normal weight. 17px / line-height 1.6 kept — only the color
     changes.
     v1008: inter-paragraph margin trimmed 14px → 6px. User feedback
     on screenshot: "lines in the About section can have less space
     between them" — the four short single-line paragraphs read as
     four detached statements at 14px gap; 6px makes them stack as
     a single paragraph block while still giving each statement its
     own visual line break. line-height (1.6) left alone — that
     governs intra-paragraph leading, not what the user was pointing
     to. :last-child margin-bottom:0 rule below still drops the
     trailing margin so the card padding is the only space below
     the final line.
     26.1.0.9: line-height 1.6 → 1.2 per user. Now that the second
     paragraph (the one explaining ISO 14224 / reliability principles
     basis) has been dropped, the remaining 3 paragraphs are short
     and benefit from tighter line spacing — reads as a denser,
     punchier block instead of an airy four-line layout. */
  margin: 0 0 6px;
  font-size: 17px;
  line-height: 1.2;
  color: #18324d;
  max-width: 980px;
}

.about-text p:last-child { margin-bottom: 0; }

/* 26.1.0.7: About + Contact 2-column row.
   The two sections render as a 2-column grid on the public home so
   the About card (left) and Contact card (right) sit side-by-side
   with equal visual weight. On narrow viewports they stack —
   single-column with About first, Contact second — which is the
   natural reading order (context before action). The grid gap and
   the per-card padding are tuned so the row feels balanced against
   the Pricing section above it (also a card row).
   Scoped to public home only — signed-in route already hides
   About entirely. The grid wrapper takes display:grid only when on
   route-home without rail; otherwise stays as a normal block-flow
   container so signed-in routes (where #about is hidden anyway)
   don't pay any layout cost. */
.about-contact-row {
  display: block;
}
/* 26.1.0.11: explicit [hidden] rule needed because author rules
   (the .about-contact-row display:block above and the route-home
   grid override below) win over the user-agent default
   `[hidden] { display: none }`. Without this, toggling
   .hidden=true on the wrapper section in pages/home-page.js leaves
   the section visible on signed-in route — which is what the user
   saw: both About and Contact still rendering on the signed-in
   home even though toggleSection('about-contact', false) was being
   called. The !important is defensive — !important on a rule the
   user explicitly opts into via the hidden attribute is the right
   place for it (it's exactly what the UA default does internally). */
.about-contact-row[hidden] {
  display: none !important;
}
body.route-home:not(.has-reports-rail) .about-contact-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
  align-items: stretch;
}
body.route-home:not(.has-reports-rail) .about-contact-row > section-panel,
body.route-home:not(.has-reports-rail) .about-contact-row about-section,
body.route-home:not(.has-reports-rail) .about-contact-row contact-section {
  display: flex;
  flex-direction: column;
}
body.route-home:not(.has-reports-rail) .about-contact-row about-section .section-panel,
body.route-home:not(.has-reports-rail) .about-contact-row contact-section .section-panel {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
}
body.route-home:not(.has-reports-rail) .about-contact-row .about-content,
body.route-home:not(.has-reports-rail) .about-contact-row .contact-content {
  flex: 1 1 auto;
}

/* ── Contact section ── 26.1.0.7
   Mirror of .about-content card chrome (border + radius + shadow).
   Card layout is a single column (no icon image — the email link is
   the visual anchor). Inside: short body, response-time line, email
   address, location line. Each gets its own paragraph treatment for
   clear visual rhythm. Spacing tuned to match the About card's
   typography so the two cards read as siblings, not visual mismatch. */
.contact-content {
  display: flex;
  align-items: flex-start;
  gap: 24px;
}
body.route-home:not(.has-reports-rail) #contact .contact-content {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 22px 26px;
  box-shadow: 0 1px 3px rgba(23, 63, 107, 0.04);
  align-items: center;
}
.contact-text {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.contact-body {
  margin: 0;
  font-size: 17px;
  line-height: 1.6;
  color: #18324d;
}
.contact-response {
  margin: 0;
  font-size: 14px;
  color: var(--muted);
  font-style: italic;
}
.contact-email {
  margin: 0;
}
.contact-email a {
  font-size: 17px;
  font-weight: 600;
  color: var(--brand-dark);
  text-decoration: none;
  word-break: break-all;
}
.contact-email a:hover,
.contact-email a:focus {
  text-decoration: underline;
}
.contact-location {
  margin: 0;
  font-size: 14px;
  color: var(--muted);
}

/* ── AI-Cycle chat (signed-in home) ──
   v950 reshape: single textarea as the primary chatbox, response
   area appears below after submit. Dropped the v949 history-log +
   input-row + Send-button layout — user asked to consolidate to
   one editable surface ("remove the bottom part"). Keyboard-only
   submission: plain Enter sends, Shift+Enter inserts a newline. */
/* ── Process section (public home) ── v969 / v970
   Hero 3-step flow (Upload → Structure → Analyse) that sits directly
   below the header on the signed-out home. v970 restructured to
   mirror pricing-section — flat shell (route-home already strips
   border/shadow on .section-panel), .section-kicker tag on the
   left, subtitle line below the kicker, then the cards. The cards
   themselves carry the visual weight (white bg, border, shadow)
   so the process + pricing sections read as a consistent pair
   against the grey page chrome.

   Layout strategy: .process-flow is a flex row on desktop (steps +
   arrow connectors side-by-side). At narrow viewports the row
   wraps + stacks vertically, and the arrow SVGs rotate 90° via a
   media query near the bottom. Cards keep their full chrome across
   viewports — only their arrangement changes. */
.process-subtitle {
  /* v970: sits directly under the .section-kicker tag — matches the
     kicker's left gutter (.container provides the outer padding)
     rather than being centered.
     v971: typography matches .about-text p (17px / 1.6 /
     var(--muted)) so the two one-line clarifiers on the public
     home (Process subtitle + About body) share a voice.
     v973: color changed from var(--muted) to the app body default
     #18324d. Same change made in .about-text p — user requested
     "normal black" for both sections so they read with full
     weight rather than de-emphasised. */
  margin: 0 0 20px 0;
  font-size: 17px;
  line-height: 1.6;
  color: #18324d;
  max-width: 980px;
}
.process-flow {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  align-items: stretch;
  /* v971: left-aligned (was centered). v975: 4 steps instead of 3,
     so using space-between with flex:1 on each step keeps them
     equally spaced across the available width — the arrow <li>s
     between them sit flush between cards. */
  justify-content: flex-start;
  gap: 8px;
  flex-wrap: wrap;
}
.process-step {
  /* v975: flex-basis reduced (220→160) now that 4 cards share the
     row. Min-width dropped (200→160) so 4 cards fit cleanly at
     typical desktop widths (~1400px usable section area); cards
     still grow to fill space evenly. Max-width cap kept so
     extremely wide viewports don't stretch each card to banner
     size. */
  flex: 1 1 160px;
  min-width: 160px;
  max-width: 320px;
  display: flex;
}
.process-card {
  position: relative;
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 28px 16px 18px;
  transition: box-shadow 0.2s, border-color 0.2s;
}
.process-card:hover {
  border-color: var(--brand-dark);
  box-shadow: 0 2px 8px rgba(23, 63, 107, 0.14);
}
/* v975: 'decide' variant — same card chrome, muted step-badge.
   The pipeline steps (1-3) are AI-driven, Decide (4) is a user
   action — a separate semantic. Muted badge (grey instead of
   brand-dark) signals that difference without changing the
   card layout. Card still links as part of the same flow. */
.process-card--decide .process-step-badge {
  background: var(--muted);
  color: #ffffff;
}
.process-step-badge {
  position: absolute;
  /* v975: badge 28→24px per user spec "reduce size by ~10–15%".
     Top offset adjusted -14→-12 to keep ~½ badge above the card
     edge. Font 14→12 proportionally. */
  top: -12px;
  left: 50%;
  transform: translateX(-50%);
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--brand-dark);
  color: #ffffff;
  font-size: 12px;
  font-weight: 700;
  line-height: 24px;
  text-align: center;
  box-shadow: 0 0 0 3px #ffffff;
}
.process-card-icon {
  width: 96px;
  height: 96px;
  margin: 4px 0 14px 0;
  color: var(--brand-dark);
  display: flex;
  align-items: center;
  justify-content: center;
}
.process-card-icon > svg,
.process-card-icon > img {
  /* v1011: extended from `> svg` only to also cover `> img` after
     step icons swapped to raster PNGs. Both element types fill the
     96×96 container; object-fit: contain is a no-op for SVG (which
     scales by its own viewBox) but ensures PNGs preserve aspect
     when the container is non-square (it isn't currently — but the
     rule is robust to layout changes). */
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.process-card-title {
  margin: 0 0 6px 0;
  font-size: 16px;
  font-weight: 700;
  color: var(--brand-dark);
}
.process-card-text {
  margin: 0 0 14px 0;
  font-size: 13px;
  color: #18324d;
  line-height: 1.45;
  min-height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* v975: optional small footnote under the main text (used on
   step 4 to note "Decisions captured with full traceability").
   Muted grey-blue so it reads as a secondary caption, not
   competing with the main card text. Only one line fits the
   design target. */
.process-card-subtext {
  margin: -8px 0 12px 0;
  font-size: 11.5px;
  color: var(--muted);
  line-height: 1.3;
  font-style: italic;
}
.process-card-timing {
  margin-top: auto;
  /* v975: fixed-width pill so all labels share visual weight.
     v978: widened 88→124px so "ORGANISATION" (12 uppercase chars
     at 12px with letter-spacing) fits comfortably. Shorter labels
     like "INSTANT" or "SECONDS" now sit with more internal
     breathing space — the equal-width rhythm across all 4 cards
     is preserved. */
  width: 124px;
  padding: 4px 0;
  border-radius: 12px;
  background: var(--brand-dark);
  color: #ffffff;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  text-align: center;
}
/* v978: muted pill variant for step 4 (Decision — Organisation).
   Same pill shape + size as the AI steps, but reduced contrast so
   the label reads as "part of the process, not automated":
   - Background: transparent (was --brand-dark solid)
   - Border: 1.5px --brand-dark (keeps the outline readable)
   - Color: --brand-dark text (was white)
   Keeps the label clearly legible; the ORGANISATION pill reads
   as a secondary outlined label while SECONDS / MINUTES / INSTANT
   stay as solid AI-driven timers. */
.process-card--decide .process-card-timing {
  background: transparent;
  color: var(--brand-dark);
  border: 1.5px solid var(--brand-dark);
  /* Compensate for the 1.5px border on each side so the pill's
     outer size matches the solid-fill pills on steps 1-3
     (border-box math: 124 - 3 = 121 content; solid pill has 124
     padding-box. Close enough that labels stay visually
     centered across all 4 cards). */
  padding: 2.5px 0;
}
.process-arrow {
  flex: 0 0 auto;
  align-self: center;
  color: var(--brand-dark);
  /* v975: opacity raised 0.65 → 0.9 per user request "make arrows
     more visible". Combined with the thicker stroke inside the
     SVG (see process-section.js) the connector reads clearly
     between cards rather than fading into the chrome. */
  opacity: 0.9;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 4px;
}
.process-arrow-svg {
  width: 40px;
  height: 16px;
}
/* Responsive — stack vertically below 820px (narrower than a
   typical tablet landscape). Arrows rotate so they still point
   forward in the new flow direction. Each step takes full width
   so the cards feel substantial on small screens rather than
   squeezed into a side-by-side grid. */
@media (max-width: 820px) {
  .process-flow {
    flex-direction: column;
    align-items: center;
    gap: 4px;
  }
  .process-step {
    flex: 0 0 auto;
    width: 100%;
    max-width: 360px;
  }
  .process-arrow {
    transform: rotate(90deg);
    padding: 8px 0;
  }
}


.al-cycle-chat {
  display: flex;
  flex-direction: column;
  gap: 12px;
  /* Cap so long bot responses don't stretch to a 2000px line on
     wide viewports. */
  max-width: 860px;
}

/* v1006: on signed-in home the chat reads as the page's PRIMARY
   surface — center it horizontally within the section (which is now
   sized to the search-pill width per the v1006 .container > section
   rule above) and push it down 48px so it has visual focus rather
   than sitting flush under the AI-Cycle kicker. The kicker stays
   left-aligned with the rest of the home content (Data Summary,
   footer ©) per user request; only the chat itself moves to centre.
   margin: 48px auto 0 — top breathing room + auto horizontal centring
   inside the 1100px-capped section. The .al-cycle-chat's existing
   max-width: 860px keeps it from stretching across the full section
   width on wide viewports. */
body.route-home.has-reports-rail #al-cycle .al-cycle-chat {
  margin: 48px auto 0;
}
.al-cycle-chat__input {
  /* White "content card" against the grey v939 panel chrome, same
     #b8b8b8 hairline as summary-block borders. Tall by default (5
     rows) so the textarea reads as THE main surface rather than a
     small input field below content. Resize: vertical lets users
     grow it further for longer questions. */
  min-height: 120px;
  max-height: 400px;
  padding: 12px 14px;
  font: inherit;
  font-size: 14px;
  line-height: 1.45;
  color: #18324d;
  background: #ffffff;
  border: 1px solid #b8b8b8;
  border-radius: 6px;
  resize: vertical;
}
.al-cycle-chat__input::placeholder {
  color: var(--muted);
  font-style: italic;
}
.al-cycle-chat__input:focus {
  outline: 2px solid #2d74c4;
  outline-offset: 1px;
  border-color: #2d74c4;
}

/* Response block — appears below the textarea after the user hits
   Enter. Replaced on each new submit rather than appended; this is
   a single-turn surface, not a scrolling chat log. */
.al-cycle-chat__response {
  padding: 12px 14px;
  background: #f2f2f2;
  border: 1px solid #b8b8b8;
  border-radius: 6px;
}
.al-cycle-chat__question {
  font-size: 13px;
  color: var(--muted);
  font-style: italic;
  margin: 0 0 8px;
  /* Preserve newlines the user typed with Shift+Enter; also break
     super-long tokens rather than horizontally scroll. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.al-cycle-chat__question::before { content: '\201C'; } /* curly open quote */
.al-cycle-chat__question::after  { content: '\201D'; } /* curly close quote */
.al-cycle-chat__reply {
  font-size: 14px;
  line-height: 1.5;
  color: var(--brand-dark);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.al-cycle-chat__reply--pending {
  color: var(--muted);
  font-style: italic;
}

/* ── Summary row links ── */
.summary-row--link {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  min-height: 46px;
  padding: 0 10px 0 12px;
  border-top: 1px solid #d4deea;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
}

.summary-row--link:nth-child(even) { background: #efefef; }
/* v947: base hover updated from #ddeef9 to #eef5ff — matching
   .reports-panel__item:hover at line ~869 so a summary-table
   row link and a sidebar report link light up the same way
   under the cursor (user request: "same hover highlight effect
   as the report links in the side-panel"). See the scoped rule
   below for the bug this also incidentally fixes. */
.summary-row--link:hover { background: #eef5ff; color: #1c56a4; }

/* v947: scoped hover rule for signed-in home — required to beat
   v941's `body.route-home.has-reports-rail .summary-row` white-bg
   specificity. Single-class `.summary-row--link:hover` (specificity
   20) lost the cascade battle to v941's body-prefixed rule
   (specificity 31), so the hover bg had NO visible effect at all
   on signed-in home prior to this fix. Same prefix ties the
   specificity at 31; source order wins it. The .summary-item
   override is needed because .summary-item's own rule (line ~1687)
   explicitly sets color: var(--brand-dark), blocking the inherit
   chain from the link's :hover color. */
body.route-home.has-reports-rail .summary-row--link:hover {
  background: #eef5ff;
  color: #1c56a4;
}
body.route-home.has-reports-rail .summary-row--link:hover .summary-item {
  color: #1c56a4;
}

/* ── Summary skeleton ── */
@keyframes skel-pulse {
  0%, 100% { opacity: 1; }
  50%       { opacity: 0.45; }
}

.skel {
  background: var(--soft-bg);
  border-radius: 4px;
  animation: skel-pulse 1.4s ease-in-out infinite;
}

.skel--title {
  height: 20px;
  width: 55%;
  margin: 14px 14px 10px;
}

.skel--row {
  height: 46px;
  border-top: 1px solid var(--line);
  border-radius: 0;
  margin: 0;
}

.skel-block { background: #fbfdff; }

/* ── Report white top bar ── */

.report-top-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  /* v83d: no heading tabs live here anymore — they moved into
     .report-main. This is now a plain app-bar with back button,
     title, spacer, and action icons. */
  min-height: 42px;
  padding: 0 16px;
  /* v955: bg flipped #ffffff → #f0f0f0 and border-bottom darkened
     from var(--line) (#dadada, blue-tinted) to #b8b8b8 (neutral)
     per user request — matches the v938/v939 home-page chrome
     (top-nav + reports-panel + status bar all use #f0f0f0 with
     #b8b8b8 hairlines). The analysis route now shares the same
     chrome palette as home; transitions between the two routes
     no longer swap between a white app-bar and a grey one.
     26.1.0.52: bg flipped back to #ffffff per user request — the
     analysis top bar now reads as paper, matching the white
     central document panel on /report (.report-doc since 26.1.0.50).
     The grey panel chrome that previously surrounded the top bar
     is gone for the analysis route too: report-page and analysis-
     page both have a white top bar over their content. The
     #b8b8b8 hairline border-bottom stays as the separator. */
  background: #ffffff;
  border-bottom: 1px solid #b8b8b8;
}

/* Back chevron button + small home icon overlay, matching Bicycle's
   app-bar back affordance. The chevron reads as "back"; the home icon
   reinforces where "back" goes (the home page). Both icons sit in a
   single anchor so the whole area is one click target. */
/* v84h: two separate back-navigation buttons — "back to report" (tile
   grid icon) + "back to home" (house icon). Rendered as a small group
   so they sit together at the top-left of the report toolbar without
   a visible seam. Both share the same chrome as the old combined
   button (white bg, soft border, rounded) but are sized as individual
   tap targets. Older .rtb-back-btn / .rtb-back-chevron / .rtb-back-home
   classes are no longer generated (the split render uses rtb-nav-btn)
   — their rules are left below as a harmless fallback for any third
   party code still targeting the old classes. */
.rtb-back-group {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.rtb-nav-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 32px;
  flex-shrink: 0;
  border-radius: 8px;
  /* v85e: top-left nav buttons (back-to-report tile-grid + back-to-home
     house) now blend with the top bar — same treatment as the other
     top-bar action buttons. Transparent until hover, when a soft blue
     tint + matching border reveal the interactive chrome. */
  background: transparent;
  border: 1px solid transparent;
  color: var(--brand-dark);
  text-decoration: none;
  padding: 0;
  /* v85h: explicit pointer cursor. The tile-grid variant switched
     from <a href> to <button> in v85f; <a href> gets cursor: pointer
     from UA defaults, but <button> defaults to cursor: default, so
     hovering stopped showing the interactive hand. The house button
     is still an <a> but setting cursor here on the shared class
     covers both consistently. */
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease;
}
.rtb-nav-btn:hover {
  background: #eaf2fb;
  border-color: #b9cfe6;
}
.rtb-nav-btn:focus-visible {
  outline: 2px solid #2a6db3;
  outline-offset: -2px;
}

.rtb-nav-icon {
  width: 16px;
  height: 16px;
  color: var(--brand-dark);
  display: block;
}

/* 26.1.0.52: "back to report" button — replaces the v84h two-button
   .rtb-back-group (home + tile-picker). Single anchor, chevron +
   reportTitle text, sits at the top-left of .report-top-bar where
   the back-group used to live. Mirrors the React BICycle app's
   "< {reportTitle}" affordance exactly. Width is content-driven
   (max-width caps long titles); the chevron + text are icon-button
   sized so the row sits at ~32px tall consistent with the action
   buttons on the right end of the bar. */
.rtb-back-to-report {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 10px 0 6px;
  height: 32px;
  border: 0;
  background: transparent;
  color: var(--brand-dark);
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  /* 26.1.0.53: line-height:1 removes the extra leading the text
     line-box adds above and below the glyphs, which pushed the
     text baseline ~2-3px off the SVG chevron's optical centre even
     under align-items:center. With line-height:1 the text content
     box and the 14×14 SVG content box have matching heights, and
     the flex centring lines up the visual midpoints exactly. */
  line-height: 1;
  text-decoration: none;
  border-radius: 6px;
  cursor: pointer;
  /* Cap long titles so a verbose reportTitle doesn't push the
     centre title group off centre. Combined with overflow:hidden +
     text-overflow:ellipsis on the inner label below. */
  max-width: 280px;
  overflow: hidden;
  transition: background-color 120ms ease;
}
.rtb-back-to-report:hover {
  background: rgba(42, 109, 179, 0.10);
}
.rtb-back-to-report:focus-visible {
  outline: 2px solid #2a6db3;
  outline-offset: -2px;
}
.rtb-back-to-report__chevron {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  display: block;
}
.rtb-back-to-report__label {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

/* Kept for back-compat with any code still wiring up the old classes. */
.rtb-back-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
  width: 50px;
  height: 32px;
  flex-shrink: 0;
  border-radius: 8px;
  background: #ffffff;
  border: 1px solid #cecece;
  color: var(--brand-dark);
  text-decoration: none;
  padding: 0 4px;
}
.rtb-back-btn:hover { background: var(--soft-bg); }

.rtb-back-chevron {
  font-size: 22px;
  line-height: 1;
  font-weight: 300;
}

.rtb-back-home {
  width: 16px;
  height: 16px;
  color: var(--brand-dark);
}

/* Report title — left-aligned, right of back button */
.rtb-title {
  font-size: 17px;
  font-weight: 700;
  color: var(--brand-dark);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 200px;
  flex-shrink: 0;
}

/* v83m: group wrapping reportTitle + separator + heading dropdown.
   Sits between the two .rtb-spacer flex-1 elements so the whole
   group is centered, and can itself shrink if the headings label is
   long enough to risk colliding with the action buttons on the right. */
.rtb-title-group {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
  flex-shrink: 1;
}

.rtb-title-sep {
  color: var(--brand-dark);
  font-size: 17px;
  font-weight: 400;
  opacity: 0.6;
  user-select: none;
  flex-shrink: 0;
}

.rtb-title-subheading {
  font-size: 17px;
  font-weight: 500;
  color: var(--brand-dark);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 200px;
}

/* Heading dropdown — the "Offshore ▾" half of the title region when
   the report has 2+ headings. Styled as a text-plus-chevron button
   that looks like a link-with-affordance rather than a full toolbar
   action button; it's part of the title, not a separate control. */
.rtb-heading-menu {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-width: 0;
}

.rtb-heading-menu__toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 8px;
  min-height: 28px;
  font-family: inherit;
  font-size: 17px;
  font-weight: 600;
  color: var(--brand-dark);
  /* v83r: resting border + soft fill so the dropdown reads as a button,
     not as styled text. Hover darkens slightly; the expanded state
     deepens the fill so the open dropdown is visually tied to its
     panel below. */
  background: #ffffff;
  border: 1px solid #cecece;
  border-radius: 6px;
  cursor: pointer;
  min-width: 0;
  transition: background 0.12s ease, border-color 0.12s ease;
}

.rtb-heading-menu__toggle:hover {
  background: var(--soft-bg);
  border-color: #8fb2d4;
}

.rtb-heading-menu__toggle[aria-expanded="true"] {
  background: var(--soft-bg);
  border-color: #4a7dbf;
}

.rtb-heading-menu__toggle:focus-visible {
  outline: 2px solid #4a7dbf;
  outline-offset: 2px;
}

.rtb-heading-menu__label {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 200px;
}

.rtb-heading-menu__chevron {
  width: 12px;
  height: 12px;
  flex-shrink: 0;
  transition: transform 0.15s ease;
}

.rtb-heading-menu__toggle[aria-expanded="true"] .rtb-heading-menu__chevron {
  transform: rotate(180deg);
}

.rtb-heading-menu__panel {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 100;
  min-width: 180px;
  max-width: 320px;
  padding: 4px;
  list-style: none;
  margin: 0;
  background: #fff;
  border: 1px solid #cecece;
  border-radius: 8px;
  box-shadow: 0 10px 26px rgba(23, 63, 107, 0.15);
}

.rtb-heading-menu__item {
  display: block;
  width: 100%;
  min-height: 32px;
  padding: 6px 12px;
  text-align: left;
  background: none;
  border: none;
  border-radius: 4px;
  color: var(--brand-dark);
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.rtb-heading-menu__item:hover {
  background: var(--soft-bg);
}

.rtb-heading-menu__item--active {
  font-weight: 700;
  background: var(--soft-bg);
}

/* v83d: rtb-spacer fills the middle of the top bar — where the
   heading chips used to sit — so the action buttons push to the
   far right without any tabs between title and actions. */
.rtb-spacer {
  flex: 1 1 auto;
  min-width: 0;
}

/* Right-side info + export icon buttons */
.rtb-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
  margin-left: auto;
}

.rtb-action-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  /* v85c: matches the blend-in treatment applied to the content-
     toolbar buttons in v85a/v85b. Transparent background + border
     until hover, at which point a soft blue tint + matching border
     reveal the affordance. The top-bar info / full-screen / reset
     icons were otherwise still framed in hard white boxes against
     the brand-blue header, which competed visually with the title
     and title-dropdown. */
  border: 1px solid transparent;
  background: transparent;
  color: var(--brand-dark);
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.15s, border-color 0.15s;
}
.rtb-action-btn:hover {
  background: #eaf2fb;
  border-color: #b9cfe6;
}

/* ═════════════════════════════════════════════════════════════════
   Top-bar Info dropdown (v83e)
   When the active subHeading is single-content, the Info button
   becomes a dropdown offering "Report…" and "Analysis…" as menu
   items. Multi-content subHeadings still render a plain button.
   ═════════════════════════════════════════════════════════════════ */
.rtb-info-menu {
  position: relative;
  display: inline-flex;
}

.rtb-info-menu-panel {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 100;
  min-width: 180px;
  padding: 4px;
  list-style: none;
  margin: 0;
  background: #fff;
  border: 1px solid #cecece;
  border-radius: 8px;
  box-shadow: 0 10px 26px rgba(23, 63, 107, 0.15);
}

.rtb-info-menu-item {
  display: block;
  width: 100%;
  min-height: 32px;
  padding: 6px 12px;
  text-align: left;
  background: none;
  border: none;
  border-radius: 4px;
  color: var(--brand-dark);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
  white-space: nowrap;
}
.rtb-info-menu-item:hover {
  background: var(--soft-bg);
}

/* v881: <favorite-menu> — star-trigger dropdown that lists per-content
   favorites. Lives inside rtb-back-group at the top-left of the report
   top bar, so the trigger is sized to match .rtb-nav-btn (34×32, flat
   until hover) and the panel mirrors .rtb-info-menu-panel but anchors
   left (since the control sits on the left edge). See the old
   BICycle React app's FavoriteMenu.js for the data grouping model. */
favorite-menu {
  position: relative;
  display: inline-flex;
  align-items: center;
}
favorite-menu[hidden] { display: none; }

.favorite-menu__trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 32px;
  flex-shrink: 0;
  border-radius: 8px;
  background: transparent;
  border: 1px solid transparent;
  color: var(--brand-dark);
  padding: 0;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease;
}
/* .216: when an active favorite is set the trigger expands to fit the
   name alongside the icon. Auto-width (vs. the icon-only fixed
   34px), gap between icon + text, and inline padding so the label
   doesn't crowd the icon. The base rule above still applies for
   colour/border/hover/focus — only the layout differs. */
.favorite-menu__trigger--has-name {
  width: auto;
  justify-content: flex-start;
  gap: 6px;
  padding: 0 10px 0 8px;
  max-width: 240px;
}
.favorite-menu__trigger:hover {
  background: #eaf2fb;
  border-color: #b9cfe6;
}
.favorite-menu__trigger[aria-expanded="true"] {
  background: #eaf2fb;
  border-color: #b9cfe6;
}
.favorite-menu__trigger:focus-visible {
  outline: 2px solid #2a6db3;
  outline-offset: -2px;
}
.favorite-menu__icon {
  width: 16px;
  height: 16px;
  display: block;
  /* Star stays in brand-dark ink to pair with the neighbouring
     house + tile-grid buttons. Filling the star keeps it readable
     at 16px where a stroke-only outline would disappear. */
  color: var(--brand-dark);
  flex-shrink: 0;
}
/* .216: active-favorite name label inside the trigger. Truncates
   with ellipsis when the favorite name is longer than the trigger
   can accommodate (max-width above caps the trigger so an
   overlong name doesn't push other top-bar pills off-screen). */
.favorite-menu__active-name {
  font-size: 12.5px;
  font-weight: 500;
  color: var(--brand-dark);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 200px;
  line-height: 1;
}

.favorite-menu__panel {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 100;
  /* v883: wider panel + allow text wrap so long favoriteNames
     (real data can run 30+ chars) render cleanly on two lines
     instead of truncating. max-width caps so the dropdown doesn't
     run off-screen on narrow sidebars. */
  min-width: 220px;
  max-width: 340px;
  max-height: 60vh;
  overflow-y: auto;
  padding: 4px;
  list-style: none;
  margin: 0;
  background: #fff;
  border: 1px solid #cecece;
  border-radius: 8px;
  box-shadow: 0 10px 26px rgba(23, 63, 107, 0.15);
}
.favorite-menu__panel[hidden] { display: none; }

.favorite-menu__item {
  display: block;
  width: 100%;
  min-height: 32px;
  padding: 6px 12px;
  text-align: left;
  background: none;
  border: none;
  border-radius: 4px;
  color: var(--brand-dark);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
  /* v883: wrap long names instead of nowrap-clipping them. Favorite
     names can be quite long per user. */
  white-space: normal;
  word-break: break-word;
  line-height: 1.35;
}
.favorite-menu__item:hover {
  background: var(--soft-bg);
}
.favorite-menu__item--default {
  font-weight: 600;
}
/* v883: items under a category branch get a tree-indent so the
   parent-child hierarchy reads visually. The 28px matches the
   chevron (16px) + its right margin (8px) + a 4px visual offset. */
.favorite-menu__item--branch {
  padding-left: 28px;
}

/* v883: category header upgraded from a plain label to a
   clickable tree-toggle row — chevron + uppercase name. The soft
   background tint gives the header more visual weight than the
   items below it, which was the user's ask ("make the categories
   a bit more distinct"). */
.favorite-menu__category-wrap {
  list-style: none;
}
.favorite-menu__category {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding: 6px 10px;
  margin: 2px 0;
  text-align: left;
  background: #ededed;
  border: 0;
  border-radius: 4px;
  color: #405b7a;
  font: inherit;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  cursor: pointer;
  user-select: none;
}
.favorite-menu__category:hover {
  background: #e4ecf4;
}
.favorite-menu__category-chevron {
  width: 12px;
  height: 12px;
  flex-shrink: 0;
  transition: transform 120ms ease;
  color: currentColor;
}
/* Collapsed = chevron rotates 90° counter-clockwise (points right,
   i.e. "closed, click to open"). Expanded = default down-chevron
   (points down, "open, items below"). Matches the data-tree panel
   chevron convention already used in v86t. */
.favorite-menu__category--collapsed .favorite-menu__category-chevron {
  transform: rotate(-90deg);
}
.favorite-menu__category-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.favorite-menu__separator {
  list-style: none;
  height: 1px;
  margin: 4px 0;
  background: #e3ebf3;
  border: 0;
}

/* v916: sidebar top-bar — horizontal strip hosting favorite-menu,
   time-window pill, and rail-toggle as siblings. Replaces the v883
   arrangement where favorite-menu was a direct child of .report-
   sidebar and rail-toggle was absolute-positioned on top-right.
   User asked for the time-window pill to live here alongside the
   star + chevron (previous home in the top toolbar, v914, was
   wrong). Flex row in expanded mode, flex column (icons) in rail. */
.report-sidebar__top-bar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 3px 6px;
  /* v963: height bumped 40→42px so the sidebar top-bar aligns
     horizontally with .report-top-bar on the right (Topside ·
     Standard Analyses · action-icons row). Both now share the
     same 42px + 1px-border band, and their bottom borders line
     up across the vertical splitter. Border color also switched
     from var(--line) to #b8b8b8 to match report-top-bar's v955
     palette — the two chrome strips now read as one continuous
     band of grey when the sidebar is expanded. */
  min-height: 42px;
  background: #f0f0f0;
  border-bottom: 1px solid #b8b8b8;
}

/* favorite-menu host inside the top-bar: inline, no own chrome
   (the top-bar owns the grey+border). position:relative so the
   dropdown's absolute positioning anchors here. */
.report-sidebar__top-bar > favorite-menu.report-sidebar__favorite-menu {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  position: relative;
  background: transparent;
  border: 0;
  padding: 0;
  min-height: 0;
}
.report-sidebar__top-bar > favorite-menu.report-sidebar__favorite-menu[hidden] {
  display: none;
}

/* v916: time-window pill. Sits between favorite-menu (left) and
   rail-toggle (right, pushed by margin-left:auto). flex:0 1 auto
   lets it shrink when the sidebar is narrow; min-width:0 enables
   the label ellipsis. Hidden by default — the settingsBar.controls
   setter trap in pages/analysis-page.js toggles [hidden] based on
   showTimeWindow. */
.report-sidebar__time-window {
  flex: 0 1 auto;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 28px;
  padding: 0 10px;
  border: 1px solid #cecece;
  border-radius: 14px;
  background: #ffffff;
  color: var(--brand-dark);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.12s, border-color 0.12s;
}
.report-sidebar__time-window:hover {
  background: #eef5fd;
  border-color: #8fb2d4;
}
.report-sidebar__time-window:focus-visible {
  outline: 2px solid #4a7dbf;
  outline-offset: 2px;
}
.report-sidebar__time-window[hidden] {
  display: none;
}
/* v961: read-only variant — server says useTimeWindow=true but
   showTimeWindow=false. Time window is configured for this
   content/subheading but locked against editing; pill renders in
   its usual place to preserve context, but loses pointer affordance
   and hover/focus reactions so it doesn't look clickable. Slightly
   muted foreground matches the "informational" feel of the Windows
   reference where locked status strip items are flat text. */
.report-sidebar__time-window--readonly {
  cursor: default;
  color: #5a6a7d;
  background: #f6f6f6;
  border-color: #d7d7d7;
}
.report-sidebar__time-window--readonly:hover {
  background: #f6f6f6;
  border-color: #d7d7d7;
}
.report-sidebar__time-window--readonly:focus-visible {
  outline: none;
}
.report-sidebar__time-window-icon {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
  shape-rendering: geometricPrecision;
}
.report-sidebar__time-window-label {
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

/* Rail mode top-bar: flex column, no chrome (the sidebar's own
   #f0f0f0 rail background shows through uniformly — see v910). */
.report-sidebar--collapsed .report-sidebar__top-bar {
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 4px 0;
  min-height: 0;
  background: transparent;
  border-bottom: 0;
}

/* Rail mode favorite-menu: 34×34 trigger centered in 44px column;
   dropdown pops out to the right so it doesn't overlap the rail. */
.report-sidebar--collapsed .report-sidebar__top-bar > favorite-menu.report-sidebar__favorite-menu {
  width: 44px;
  justify-content: center;
}
.report-sidebar--collapsed .report-sidebar__top-bar > favorite-menu.report-sidebar__favorite-menu .favorite-menu__trigger {
  width: 34px;
  height: 34px;
}
.report-sidebar--collapsed .report-sidebar__top-bar > favorite-menu.report-sidebar__favorite-menu .favorite-menu__panel {
  /* Pop the dropdown out to the right of the rail so it doesn't
     overlap the 44px icon column. +4px gap matches the original
     `top: calc(100% + 4px)` separation used in expanded mode. */
  left: calc(100% + 4px);
  top: 0;
}

/* Rail mode time-window: shrink to a 34×34 icon button (hide label,
   enlarge icon slightly for parity with the other rail icons).
   v918 made this transparent+borderless to blend with the sidebar;
   v919 reverts to the bordered-chip treatment that the nearby
   .report-settings-bar__toggle rail icons (data-tree, filter-tree)
   use, so the three icons form one visually consistent trio. */
.report-sidebar--collapsed .report-sidebar__time-window {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  padding: 0;
  border-radius: 4px;
  justify-content: center;
  gap: 0;
  background: transparent;
  border: 1px solid #cecece;
}
.report-sidebar--collapsed .report-sidebar__time-window:hover {
  background: rgba(63, 120, 173, 0.08);
  border-color: #8fb2d4;
}
.report-sidebar--collapsed .report-sidebar__time-window-icon {
  /* Bumped from 13px → 16px in rail. The expanded-mode pill keeps its
     compact 13px icon next to the label; in rail there's no label so
     a slightly larger icon fills the square button better and matches
     the settings-bar rail icons visually. */
  width: 16px;
  height: 16px;
}
.report-sidebar--collapsed .report-sidebar__time-window-label {
  display: none;
}



/* v86u: bundle (three-dots "more actions") button collapses the four
   right-side toolbar actions into a single trigger at narrow viewports.
   Both the direct buttons and the bundle are always rendered in the
   DOM; CSS @media rules below decide which set is visible. */
.rtb-bundle-menu {
  position: relative;
  display: inline-flex;
}

.rtb-bundle-menu-panel {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 100;
  min-width: 200px;
  padding: 4px;
  list-style: none;
  margin: 0;
  background: #fff;
  border: 1px solid #cecece;
  border-radius: 8px;
  box-shadow: 0 10px 26px rgba(23, 63, 107, 0.15);
}

.rtb-bundle-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 32px;
  padding: 6px 12px;
  text-align: left;
  background: none;
  border: none;
  border-radius: 4px;
  color: var(--brand-dark);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
  white-space: nowrap;
}
.rtb-bundle-menu-item:hover {
  background: var(--soft-bg);
}
/* v86v: icon slot aligns the 16x16 SVG to a fixed gutter so labels
   line up in a tidy column regardless of glyph width. */
.rtb-bundle-menu-item__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
  color: var(--brand-dark);
}
.rtb-bundle-menu-item__label {
  flex: 1 1 auto;
}

/* Default (desktop): bundle hidden, direct buttons visible. */
.rtb-bundle-menu {
  display: none;
}

/* Narrow viewport: hide direct actions + show bundle. The 720px
   threshold is the point where the title group + action cluster start
   crowding each other on tablets/phones. The info dropdown (when
   .rtb-info-menu exists as a direct child of .rtb-actions) is hidden
   with the other direct buttons — the bundle menu replicates its
   items (Report information / Analysis information). */
/* v906: the right-hand action cluster (info / download / fullscreen /
   reset) now flips to overflow mode (single bundle menu) at viewport
   ≤ 900px — previously this happened only at ≤ 720px, which left a
   wide mid-range where the center title group ("Standard Analysis")
   ellipsized to "Stand…" while the four full-size action buttons on
   the right still held their space. v905 tried to fix this by
   progressively shrinking the buttons, but the user prefers full-size
   buttons that overflow earlier over shrunken buttons that stay
   visible. Below 900px a single "⋯" bundle menu replaces the direct
   buttons; its dropdown replicates their actions so nothing is lost. */
@media (max-width: 900px) {
  .rtb-actions > .rtb-action-btn,
  .rtb-actions > .rtb-info-menu {
    display: none;
  }
  .rtb-actions > .rtb-bundle-menu {
    display: inline-flex;
  }
}

/* ═════════════════════════════════════════════════════════════════
   Heading strip (v83d)
   Sits inline at the top of .report-main, directly above the
   subheading tabs. Right-aligned chips over a grey shelf; the
   active chip lifts to white so it reads as "attached" to the
   white subheading tabs row below (.report-nav). Mirrors the
   Windows BI-Cycle app's heading → subheading stack.
   ═════════════════════════════════════════════════════════════════ */
.report-heading-strip {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-end;
  /* v83e: left-aligned chips — matches the requested layout where
     the primary heading tabs sit at the left edge of the main
     column, in line with the grid below. */
  justify-content: flex-start;
  gap: 6px;
  padding: 6px 22px 0;
  background: #e8ecf1;
  border-bottom: 1px solid var(--line);
  min-height: 34px;
}
.report-heading-strip[hidden] { display: none; }

.report-heading-strip__chip {
  background: #dddddd;
  border: 1px solid #bdbdbd;
  border-bottom: none;
  border-radius: 6px 6px 0 0;
  margin-bottom: -1px;
  padding: 5px 16px;
  color: var(--muted);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  font-family: inherit;
  white-space: nowrap;
  position: relative;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.report-heading-strip__chip:hover {
  background: #ececec;
  color: var(--brand-dark);
}
.report-heading-strip__chip--active {
  /* Active chip merges into the white .report-nav (subheading row)
     below — same sleight-of-hand the old top-bar tabs used, now
     adapted to the subheading row's white background. */
  background: #ffffff;
  color: var(--brand-dark);
  font-weight: 700;
  border-color: var(--line);
  box-shadow: 0 -1px 0 rgba(26, 74, 135, 0.6) inset;
}

/* ── Report settings bar (blue bar separating toolbar from tab row) ── */
report-settings-bar {
  display: block;
  /* v85c: darker grey than the other four chrome strips (subheading
     tabs, content toolbar, connected tabs, meta-panel header) which
     share #f1f1f1. The settings-bar is vertical in rail mode and
     sits as a distinct column next to the main content, so giving
     it a slightly darker tone (#f0f0f0) helps it read as a separate
     left-side utility strip rather than bleeding into the toolbar
     row above. v83n's "all five strips match" unity is intentionally
     broken here — the settings-bar is architecturally distinct from
     the horizontal toolbar band. */
  background: #f0f0f0;
  border-bottom: 1px solid var(--line);
}

.report-settings-bar__inner {
  min-height: 38px;
  width: 100%;
  display: flex;
  /* v83d: wrap so the time-window pill can flow to a second row at
     narrow sidebar widths. The data-tree / filter / spacer / reset
     buttons typically all fit in one row; only the long pill drops
     down when it would otherwise overflow the sidebar's ~238px. */
  flex-wrap: wrap;
  row-gap: 4px;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
}

.report-settings-bar__toggle,
.report-settings-bar__status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;
  height: 28px;
  padding: 0 9px;
  /* v83o: transparent idle background so the buttons read as part of
     the shelf (matching Windows BI-Cycle), not as white chips floating
     on it. The border+tinted-on-hover treatment still gives them a
     clear affordance, and the pressed state stays visually distinct
     via the soft-blue fill below. */
  border: 1px solid #cecece;
  border-radius: 4px;
  background: transparent;
  color: var(--brand-dark);
  font-size: 13px;
  line-height: 1;
  white-space: nowrap;
}

.report-settings-bar__toggle {
  cursor: pointer;
  font: inherit;
}

.report-settings-bar__toggle:hover,
.report-settings-bar__toggle[aria-pressed="true"],
.report-settings-bar__status--button:hover {
  background: rgba(63, 120, 173, 0.08);
  border-color: #8fb2d4;
}

/* v86o: time-window pill in the expanded-mode settings-bar. Matches the
   toggle button visual (same height, border radius, hover tint) but
   shows the time-window label alongside the clock icon.
   v86p: white background so the pill stands out against the settings-bar
   grey (#f0f0f0) instead of blending in. */
.report-settings-bar__time-window {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;
  height: 28px;
  padding: 0 10px;
  border: 1px solid #cecece;
  border-radius: 4px;
  background: #ffffff;
  color: var(--brand-dark);
  font: inherit;
  font-size: 13px;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  max-width: 100%;
}
.report-settings-bar__time-window:hover {
  background: #f3f8fd;
  border-color: #8fb2d4;
}
.report-settings-bar__time-window-icon {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
}
.report-settings-bar__time-window-label {
  overflow: hidden;
  text-overflow: ellipsis;
}

.report-settings-bar__toggle-icon,
.report-settings-bar__status-icon {
  display: inline-flex;
  align-items: center;
}

.report-settings-bar__icon-svg {
  /* v906.1: size reverted to 15px after user clarification that
     rail-mode icons (Image 1) were already fine — it was the panel-
     header icons (Image 2, .report-sidebar__panel-title-icon) that
     read too large. shape-rendering:geometricPrecision kept as a
     harmless AA hint on the circles. */
  width: 15px;
  height: 15px;
  display: block;
  shape-rendering: geometricPrecision;
}

.report-settings-bar__toggle-label {
  font-weight: 700;
}

.report-settings-bar__toggle-value,
.report-settings-bar__status-label {
  opacity: 0.96;
}

.report-settings-bar__toggle-count {
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  /* Tinted brand fill on light backgrounds. Previous rgba(255,255,255,0.18)
     only read against the solid-blue bar and went invisible on the new
     light settings-bar background. */
  background: rgba(26, 74, 135, 0.12);
  color: var(--brand-dark);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  line-height: 1;
}

/* Filter badge: absolute overlay on top-right of the filter button */
.report-settings-bar__toggle--filter {
  position: relative;
}

.report-settings-bar__filter-badge {
  position: absolute;
  top: -5px;
  right: -5px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: #1a4a87;
  color: #ffffff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  pointer-events: none;
  box-sizing: border-box;
}

/* Filter tree node icons (imageId-based or placeholder) */
.report-sidebar__node-icon {
  flex: 0 0 16px;
  width: 16px;
  height: 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.report-sidebar__node-icon--img {
  object-fit: contain;
}

.report-settings-bar__toggle[data-action="toggle-filter-tree"] {
  min-width: 24px;
  justify-content: center;
}

/* Right-side spacer — takes all remaining horizontal space so every
   button after it pushes to the right edge. Replaces the previous
   `margin-left: auto` on individual buttons, which misbehaved with
   multiple auto-margins in the same flex container (they split the
   free space, stranding the earlier button in the middle). */
.report-settings-bar__spacer {
  flex: 1 1 auto;
}

/* ── Report page (full-width, no container) ── */
.analysis-page {
  background: #fff;
  min-height: calc(100vh - 56px);
  /* v83d: flex-column so .report-workspace can take the full
     remaining viewport. Without this, an inline-height workspace
     would collapse to its content height and the sidebar would
     stop halfway down the page. */
  display: flex;
  flex-direction: column;
}

/* ── Report subheading tab row ── */
.report-nav {
  /* 26.1.0.30: lock to natural height.  Without this, the row defaults
     to flex: 0 1 auto inside .report-main (column flex) and the
     browser shrinks it under vertical pressure when sibling content
     grows — e.g. when the meta-panel opens below the data table.
     User-visible symptom: the subheading tabs visibly squashed
     vertically when the Settings/Comments panel was expanded.
     Same fix applied to <content-sub-tabs> below and
     .report-heading-strip earlier in the chain. */
  flex: 0 0 auto;
  background: #fff;
  border-bottom: 1px solid var(--line);
}

content-sub-tabs {
  /* 26.1.0.30: same flex lock as .report-nav — see comment there. */
  flex: 0 0 auto;
}

.report-heading-strip {
  /* 26.1.0.30: same flex lock — when the heading strip is visible
     it must not shrink when sibling content grows. */
  flex: 0 0 auto;
}

report-subheading-tabs {
  display: block;
  width: 100%;
}

.subheading-tabs {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 2px;
  width: 100%;
  min-width: 0;
  overflow: visible;
  padding: 4px 22px 0;
  /* v83f: min-height reduced to match the smaller font. Was 44px. */
  min-height: 36px;
  /* v83n: unified chrome-strip grey. All five toolbar-like strips
     (sidebar settings-bar, subheading tabs, content toolbar, connected
     tabs, meta-panel header) share the same #f1f1f1 so the "chrome"
     around the grid reads as one coherent band — mirroring the Windows
     BI-Cycle app. The white active chip is still defined against this
     shelf via its inset shadow + border. */
  background: #f1f1f1;
}

.subheading-tab {
  /* v83h: sunken/raised chip treatment like the Windows BI-Cycle app.
     Inactive tabs sit flat on the grey shelf with a subtle hairline.
     Active tab "sinks" with an inset shadow + lighter bg, reading as
     a pressed button that merges into the content below. */
  background: #dfdfdf;
  border: 1px solid #bdbdbd;
  border-bottom: 1px solid var(--line);
  border-radius: 4px 4px 0 0;
  margin-bottom: -1px;
  /* v83f: tighter padding for the smaller font. */
  padding: 6px 12px 5px;
  color: var(--muted);
  /* v83f: 13px — was 15px. Matches the Windows BI-Cycle app's more
     compact subheading row. */
  font-size: 13px;
  cursor: pointer;
  font-family: inherit;
  white-space: nowrap;
  flex: 0 0 auto;
  transition: background 0.12s, color 0.12s, box-shadow 0.12s;
}

.subheading-tab:hover {
  background: #ececec;
  color: #18324d;
}


.subheading-tab--active {
  /* Active tab: white bg merging into the white subheading-underneath
     content area, with an inset shadow on the top+sides reading as a
     sunken button. No 3px bottom border anymore — the active chip
     overlaps the strip's bottom edge via margin-bottom:-1 so its
     bottom border-line and the row's bottom border coincide. */
  background: #ffffff;
  color: var(--brand-dark);
  font-weight: 700;
  border-color: var(--line);
  border-bottom-color: transparent;
  box-shadow:
    inset 1px 1px 0 rgba(26, 74, 135, 0.18),
    inset -1px 0 0 rgba(26, 74, 135, 0.10);
}

.subheading-more {
  /* v85y: was `margin-left: auto` which consumed all remaining row
     space and collapsed the gap between the last visible tab and
     the overflow chip. Relying on the parent `.subheading-tabs`
     flex gap (2px, same as tab-to-tab) produces the same 2px
     breathing room the connected-tabs strip has between its last
     tab and its overflow chip at the bottom. Keeping position:
     relative so the dropdown popover anchors correctly. */
  position: relative;
  flex-shrink: 0;
}

.subheading-more-btn {
  /* v85x: render as a tab-chip matching .data-table__connected-tab
     --overflow at the bottom — grey pill with a hairline border,
     rounded top corners, and the "…" glyph centred. Previously flat
     text on the grey shelf, which made the overflow affordance
     visually disappear. The connected-tab strip's overflow renders
     as a clear chip; mirroring that here means both overflow
     controls in the app read as the same "tab with dots in it"
     pattern the user expects. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 26px;
  padding: 5px 12px 4px;
  background: #dfdfdf;
  border: 1px solid #bdbdbd;
  border-bottom: 1px solid var(--line);
  border-radius: 4px 4px 0 0;
  margin-bottom: -1px;
  color: var(--muted);
  font-size: 18px;
  line-height: 1;
  letter-spacing: 0.05em;
  cursor: pointer;
  font-family: inherit;
  white-space: nowrap;
  flex: 0 0 auto;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}

.subheading-more-btn:hover {
  background: #ececec;
  color: #18324d;
}

.subheading-more-btn--active {
  /* Subheading hidden inside the overflow — lift to white active
     chip to signal selection, same trick subheading-tab--active
     uses. Keeps the affordance consistent with the other tabs. */
  background: #ffffff;
  color: var(--brand-dark);
  border-color: var(--line);
  border-bottom-color: transparent;
  box-shadow:
    inset 1px 1px 0 rgba(26, 74, 135, 0.18),
    inset -1px 0 0 rgba(26, 74, 135, 0.10);
}

.subheading-dropdown {
  position: absolute;
  top: 100%;
  right: 0;
  background: #fff;
  border: 1px solid #dddddd;
  border-radius: 4px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.12);
  z-index: 200;
  min-width: 160px;
  padding: 4px 0;
}

.subheading-dropdown-item {
  display: block;
  width: 100%;
  background: none;
  border: none;
  padding: 9px 16px;
  text-align: left;
  font-size: 14px;
  font-family: inherit;
  color: var(--muted);
  cursor: pointer;
  white-space: nowrap;
}

.subheading-dropdown-item:hover {
  background: #f4f6f8;
  color: #18324d;
}

.subheading-dropdown-item--active {
  color: var(--brand-dark);
  font-weight: 700;
}

/* ── Report content area ── */
.report-workspace {
  display: flex;
  align-items: stretch;
  /* v83d: workspace now starts immediately below the top bar and
     spans the full remaining viewport. The settings bar lives inside
     the sidebar, so we no longer subtract its height here. */
  min-width: 0;
  flex: 1 1 auto;
  min-height: 0;
}

.report-main {
  display: flex;
  flex: 1 1 auto;
  min-width: 0;
  flex-direction: column;
}

/* v83x: report-toolbar is now the first child of .report-main (moved
   out of app-header). It's a custom element and defaults to inline
   display, so force block + content-shaped height. The child .report-top-bar
   already owns the visual treatment (42px + 1px border). */
.report-main > report-toolbar {
  display: block;
  flex: 0 0 auto;
  width: 100%;
}

.report-sidebar {
  width: var(--report-sidebar-width, 238px);
  min-width: var(--report-sidebar-width, 238px);
  flex: 0 0 var(--report-sidebar-width, 238px);
  overflow: hidden;
  /* v83d: sidebar is now a vertical flex container. Settings bar
     sits at the top (fixed height, content-shaped), panels + inner
     splitter take the rest. */
  display: flex;
  flex-direction: column;
  background: #ffffff;
  border-right: 1px solid var(--line);
}
.report-sidebar[hidden] { display: none; }

/* Settings bar lives inside the sidebar now. It keeps its own
   background (#f1f1f1) as a subtle separation from the panel body
   below it, and flex-wraps so the time-window pill flows to a
   second row when it can't fit next to the filter + data-tree
   buttons at a narrow sidebar width. */
.report-sidebar > report-settings-bar {
  flex: 0 0 auto;
  width: 100%;
  border-bottom: 1px solid var(--line);
}

/* When the sidebar is only hosting the settings bar (no tree panels
   expanded), collapse its height so the panel-less portion doesn't
   leave a big empty white rectangle below. */
.report-sidebar--settings-only {
  flex: 0 0 auto;
  min-width: 0;
  align-self: flex-start;
  height: auto;
  min-height: 0;
}

.report-sidebar__inner {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  margin: 0;
  background: #ffffff;
}
/* Hide the inner panel region entirely in settings-only mode —
   otherwise its flex:1 would claim vertical space we're trying to
   give back to .report-main. */
.report-sidebar--settings-only .report-sidebar__inner {
  display: none;
}

/* v84c: sidebar rail mode. When collapsed, the whole sidebar shrinks
   to a 44px-wide icon column. The settings-bar inside stacks its
   buttons vertically; the tree panel region + vertical splitter are
   hidden. Click any rail icon (data-tree / time-window / filter) to
   expand the sidebar and jump to that panel — see the event handlers
   in pages/analysis-page.js. */
.report-sidebar--collapsed {
  /* Override the default --report-sidebar-width var so even if the
     inline style setter runs after this class applies, the width
     stays at 44px. `!important` because the inline width came from a
     style.setProperty call that wins normal cascade. */
  width: 44px !important;
  min-width: 44px !important;
  flex-basis: 44px !important;
  --report-sidebar-width: 44px !important;
  /* v909.1: loosen the base .report-sidebar overflow:hidden clip
     so the favorite-menu dropdown can escape the 44px rail column.
     Must live here (not up at line 2080 where v908 placed it) so
     cascade order — base at line 2648 first, this override after —
     actually wins. */
  overflow: visible;
  /* v909.1: uniform rail background. v908 stacked favorite-menu
     (#f1f1f1) above settings-bar (#f0f0f0) above the sidebar base
     white, giving the rail a three-tone striped look. Set one bg
     here and neutralize the child sections' backgrounds below so
     the whole column reads as one surface — matching the reference
     eu.al-cycle.com screenshot. */
  background: #f0f0f0;
}

/* v909.1: in rail mode, suppress settings-bar's own chrome (bg + bottom
   border) so the uniform .report-sidebar--collapsed background above
   shows through the whole column. Expanded mode keeps the original
   #f0f0f0 + border defined on the host rule — that is the architectural
   "utility strip" treatment this comment explains at line 2234. */
.report-sidebar--collapsed > report-settings-bar {
  background: transparent;
  border-bottom: none;
}

/* Hide the tree panel region and horizontal splitter — rail mode has
   no room for them. Inner is display:none so it stops participating
   in the sidebar's flex-column height allocation. */
.report-sidebar--collapsed .report-sidebar__inner {
  display: none;
}
.report-sidebar--collapsed + .report-sidebar__splitter--vertical {
  display: none;
}

/* Stack the settings-bar buttons vertically in rail mode. Tight
   padding + small row-gap so the three icons (data-tree, time-window,
   filter) fit comfortably within the narrow column. No flex-wrap in
   this layout — we're already single-column. */
.report-sidebar--collapsed > report-settings-bar {
  /* Let the bar claim the full sidebar height so its icons can breathe
     along the column instead of bunching at the top. */
  flex: 1 1 auto;
  height: 100%;
}
.report-sidebar--collapsed .report-settings-bar__inner {
  flex-direction: column;
  flex-wrap: nowrap;
  align-items: center;
  row-gap: 6px;
  column-gap: 0;
  /* v917.1: was `padding: 38px 4px 6px 4px`. The 38px top cleared
     the old absolute-positioned rail-toggle. In v916 the rail-
     toggle became a flex child of .report-sidebar__top-bar and
     the 38px became dead space — showing up as a gap between the
     top-bar's time-window icon and this bar's first icon
     (data-tree). Trimmed to uniform 6px so the icons read as one
     continuous vertical column. */
  padding: 6px 4px;
  min-height: 0;
}

/* In rail mode the buttons are icon-only: strip labels, pad for a
   square tap target, center the icon. Applies to all three triggers. */
.report-sidebar--collapsed .report-settings-bar__toggle,
.report-sidebar--collapsed .report-settings-bar__status {
  width: 34px;
  height: 34px;
  padding: 0;
  justify-content: center;
  position: relative; /* for the badge positioning below */
}

.report-sidebar--collapsed .report-settings-bar__toggle-value,
.report-sidebar--collapsed .report-settings-bar__status-label {
  display: none;
}

/* Spacer is a no-op in rail mode — we're not using it to push things
   to the right of a row. Keep it rendered but 0-height so it doesn't
   nudge the icon column. */
.report-sidebar--collapsed .report-settings-bar__spacer {
  display: none;
}

/* Small notification badge on the rail-mode filter icon, mirroring the
   FILTER panel header badge. Absolutely positioned to the top-right of
   the button so the icon glyph stays centered. */
.report-settings-bar__toggle-badge {
  position: absolute;
  top: -3px;
  right: -3px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: #1a4a87;
  color: #ffffff;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  box-shadow: 0 0 0 1.5px #f1f1f1;
  pointer-events: none;
  box-sizing: border-box;
}

/* v84d: sidebar rail-toggle button — direct child of report-sidebar,
   absolutely positioned at the sidebar's top-right corner in expanded
   mode (floats over the data-tree panel's top-right). In rail mode it
   sits inline at the top of the (now-narrow) sidebar, above the icon
   column. The SVG path is `«` — expanded state shows "collapse me"
   (chevrons point INWARD toward the main content). CSS rotates 180°
   when sidebar is collapsed → `»` (chevrons point OUTWARD, "expand
   me"). Same SVG, one rotation class. */
.report-sidebar {
  /* v84d: needs positioning context so the absolute-positioned
     rail-toggle pins to the sidebar's top-right corner, not the
     workspace's. */
  position: relative;
}

.report-sidebar__rail-toggle {
  /* v916: no longer absolute-positioned — the rail-toggle is now a
     flex child of .report-sidebar__top-bar (see the wrapper DOM in
     pages/analysis-page.js). margin-left:auto pushes it to the far
     right of the expanded horizontal top-bar; in rail mode the
     override below resets margin-left and uses order:-1 to hoist it
     to the top of the vertical column. */
  margin-left: auto;
  width: 26px;
  height: 26px;
  border: 0;
  padding: 0;
  background: transparent;
  color: #3c6fa5;
  cursor: pointer;
  border-radius: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: background-color 120ms ease;
}

/* 26.1.0.52: removed the entire 26.1.0.43 home-button block that
   used to sit here — both the .report-sidebar__home-btn rules
   (with all variants for rail mode, hover, focus, icon) AND the
   .report-workspace--with-sidebar .rtb-nav-btn--home hide rule.
   Reason: the .43 strategy was to dedupe the home affordance by
   moving it into the sidebar and hiding the toolbar copy. .52
   removes the home affordance entirely from the analysis-page
   toolbar (replaced by the new "back to report" button — see
   .rtb-back-to-report higher up in this file) and removes the
   .43 sidebar copy too. So neither block of CSS has anything to
   apply to anymore — leaving them in would just be dead CSS. */

.report-sidebar__rail-toggle:hover {
  background: rgba(42, 109, 179, 0.16);
}
.report-sidebar__rail-toggle:focus-visible {
  outline: 2px solid #2a6db3;
  outline-offset: -2px;
}
.report-sidebar__rail-toggle-icon {
  width: 14px;
  height: 14px;
  display: block;
  transition: transform 0.18s ease;
}
.report-sidebar--collapsed .report-sidebar__rail-toggle-icon {
  transform: rotate(180deg);
}

/* v908.1: in rail mode the rail-toggle joins the flex flow with
   order:-1, sitting above every other flex child (including the
   favorite-menu which is first in DOM order but should visually
   render BELOW the expand chevron). Previously absolute-positioned
   at top:6px; right:50% transform:translateX(50%), which worked
   when it was the only item at the top of the rail but overlapped
   the favorite-menu after v908 placed the star at the top of the
   flex column. */
.report-sidebar--collapsed .report-sidebar__rail-toggle {
  /* v916: margin-left:0 resets the expanded-mode margin-left:auto.
     order:-1 hoists the chevron above favorite-menu and time-window
     in the flex-column rail layout. width/height bumped so it
     matches the other rail icon dimensions. */
  margin-left: 0;
  order: -1;
  align-self: center;
  width: 34px;
  height: 30px;
  margin-top: 0;
  margin-bottom: 2px;
}
.report-sidebar--collapsed .report-sidebar__rail-toggle .report-sidebar__rail-toggle-icon {
  transform: rotate(180deg);
}

/* v84d: the report-settings-bar no longer renders the rail-toggle
   itself, but the CSS rules below target the old class names as a
   harmless fallback. Kept for the fallback-path comments in
   components/report-settings-bar.js. */
.report-settings-bar__rail-toggle {
  display: none;
}

.report-sidebar__panel {
  /* v83u: flex-column layout so the panel-header stays pinned and the
     panel-inner flexes into the remaining space with its own scroll.
     Previously the panel itself scrolled (overflow-y:auto) and the
     inner had min-height:100% of the panel — adding a 38px header
     pushed the inner's bottom 38px past the panel's visible area and
     triggered a spurious scrollbar even when the filter tree contents
     easily fit. Moving scroll responsibility down one level keeps the
     header visible while dragging content and eliminates the phantom
     track. */
  display: flex;
  flex-direction: column;
  min-height: 0;
  /* Let open panels fill the remaining sidebar height so the last
     panel's bottom sits flush with the sidebar's bottom edge, matching
     the subheading tab strip's full-width layout. Without this, a
     single open panel (common case: just Filters) only takes its
     natural content height and leaves empty background below. */
  flex: 1 1 auto;
  overflow: hidden;
}

/* v84l: force-hide panels with the `hidden` HTML attribute. The class
   rule above sets `display: flex`, which wins specificity over the UA
   `[hidden] { display: none }` rule — so JS setting `panel.hidden = true`
   flipped the attribute but the panel stayed visible. This attribute
   selector matches specificity of the class rule, and its source-order
   position after it wins the cascade. The user-visible bug this fixed:
   CAPEX-style reports where showDataTree resolves to `false` still
   rendered an empty data-tree panel occupying ~40% of the sidebar
   above the filter panel. */
.report-sidebar__panel[hidden] {
  display: none;
}

/* v84m: time-window-only filter panel (no filter tree configured, but
   a time-window IS). The panel renders just a header strip — no body,
   no collapse chevron. Default panel rule is `flex: 1 1 auto` to claim
   the sidebar's remaining height; for a bodyless panel that leaves a
   tall empty rectangle below the header. `flex: 0 0 auto` shrinks to
   the header's intrinsic height (~38px). */
.report-sidebar__panel--header-only {
  flex: 0 0 auto !important;
  min-height: 0 !important;
  overflow: visible !important;
}

/* When the panel is header-only, the time-window pill is the only
   affordance — let it stretch to fill the strip instead of clinging
   to its 180px max. Override both the max-width cap and the left-only
   margin (no funnel icon to hug) so the pill centers horizontally. */
.report-sidebar__panel--header-only .report-sidebar__panel-time-window {
  max-width: none;
  margin-left: 8px;
  margin-right: 8px;
}

.report-sidebar__panel-inner {
  /* v83u: take remaining space after the panel-header; own the vertical
     scroll so long trees remain scrollable without the header drifting
     off. min-height:0 allows flex shrink below content size. */
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
}

.report-sidebar__panel-header {
  /* v963: height aligned with main-panel rows on the right —
     data-tree panel header sits next to subheading-tabs (36px)
     and filter-tree panel header sits next to data-table-toolbar
     (~34-36px). v964: user measured in-browser and confirmed
     32px is the matching value once the 1px bottom border +
     any flex-row baseline offsets are factored in; 36px left a
     visible few-pixel misalignment. */
  min-height: 32px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  /* Match the tree items' asymmetric padding (6px left, 12px right)
     so the header text lines up with the expand chevrons below. */
  padding: 0 8px 0 10px;
  border-bottom: 1px solid var(--line);
  /* v83s: share the unified chrome grey so the header reads as part of
     the sidebar's control strip, not a separate panel shelf. */
  background: #f1f1f1;
  font-size: 13px;
  font-weight: 700;
  color: var(--brand-dark);
  text-transform: uppercase;
  letter-spacing: 0.01em;
  flex: 0 0 auto;
}

/* v83t: title + inline badge. v83u: badge is now an absolute-positioned
   notification-style dot at the top-right corner of the title text,
   rather than sitting inline next to it. Reads as a status indicator
   on the label itself — less verbose in the narrow sidebar and more
   consistent with familiar UI conventions. */
.report-sidebar__panel-title-wrap {
  display: inline-flex;
  align-items: center;
}

.report-sidebar__panel-title {
  position: relative;
  /* v84: title is now just the funnel icon — no trailing padding. The
     badge sits ON the icon (overlapping its upper-right quadrant) and
     closing this padding lets the time-window pill shift left against
     the icon instead of floating with a 14px dead strip between them. */
  display: inline-flex;
  align-items: center;
  padding-right: 0;
}

/* v83x: funnel SVG inside the filter panel header. v83y: bumped from
   14px to 22px — the earlier size read as a small inline glyph and was
   easy to miss; at 22px the icon carries the panel's identity the way
   a section header would. The header's min-height already accommodates
   it (38px > 22px + top/bottom padding). */
.report-sidebar__panel-title-icon {
  /* v907: 22 → 16px. The panel-header icon (data-tree nodes /
     filter funnel, rendered from the same SVG builders used in the
     rail) was visually heavier than the title text and the chevron
     next to it, making the header strip feel icon-dominated. 16px
     matches the natural viewBox size so strokes hit whole pixels. */
  width: 16px;
  height: 16px;
  display: block;
  shape-rendering: geometricPrecision;
}

/* v83y: time-window pill. Lives in the FILTER panel header between the
   funnel icon (left) and collapse chevron (right). Flex:1 means it
   takes remaining horizontal space, letting the date range (or "Time
   Window" placeholder) breathe in the ~170-180px mid-strip. Styled as
   a subtle button — no heavy border, soft hover, because it sits on
   the already-distinct chrome-grey header and shouldn't compete with
   the Apply/Cancel affordances in the filter body below. */
.report-sidebar__panel-time-window {
  /* v83z: align-self:stretch + small vertical margin so the pill fills
     most of the 38px header height, giving it real presence instead of
     reading as a small mid-line chip. Horizontal padding lifted to 10px
     to balance the taller box.
     v84: trimmed left margin from 6px to 2px so the pill sits close to
     the funnel icon (now that the title's right-padding is 0, the pill
     starts only a hair's-breadth from the icon's edge). Right margin
     kept at 6px to preserve clearance from the collapse chevron.
     v84 (late): capped at max-width 180px and switched from flex-grow
     to flex:0 1 auto so the pill shrinks under pressure but doesn't
     balloon to fill the entire header at wide sidebar widths. Uses
     `margin-right: auto` so the collapse chevron still pins to the far
     right of the header even when the pill is narrower than the
     available space. */
  flex: 0 1 auto;
  align-self: stretch;
  min-width: 0;
  max-width: 180px;
  margin-right: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 10px;
  margin: 4px auto 4px 2px;
  border: 1px solid #cecece;
  border-radius: 6px;
  background: #ffffff;
  color: var(--brand-dark);
  /* v84e: font-size bumped 12 → 14 so the date range reads comfortably
     at a glance. With the pill now ~30px tall (v83z stretch) the smaller
     text looked lost in the box. */
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease;
}

.report-sidebar__panel-time-window:hover {
  background: var(--soft-bg);
  border-color: #8fb2d4;
}

.report-sidebar__panel-time-window:focus-visible {
  outline: 2px solid #2a6db3;
  outline-offset: -2px;
}

.report-sidebar__panel-time-window-icon {
  width: 13px;
  height: 13px;
  flex: 0 0 auto;
  display: block;
}

.report-sidebar__panel-time-window-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

.report-sidebar__panel-badge {
  /* v84: reposition badge to overlap the icon's upper-right quadrant
     rather than floating outside to the right. With the title-span's
     right-padding removed, the title's box is now just the 22px icon;
     right:-4px pulls the 16px-wide badge's right edge 4px past the
     icon's right (so the badge's left edge sits at icon-x≈10, landing
     on the icon's upper-right corner). top:-4px raises it slightly
     above the icon for the notification-dot look.
     v934: right:-4 → -8 after v907 reduced the panel-title icon from
     22 → 16px. At 16px + badge width 16px + right:-4px the badge's
     left edge sat at icon-x≈-4 — covering the entire 16px icon, so
     the filter funnel was invisible under the badge. -8px pushes the
     badge's left edge out to icon-x≈0 (the badge sits mostly OUTSIDE
     the icon to the right), keeping the funnel fully visible. */
  position: absolute;
  top: -4px;
  right: -8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: #1a4a87;
  color: #ffffff;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0;
  text-transform: none;
  pointer-events: none;
  box-sizing: border-box;
  /* Thin white ring so the badge stands out against the chrome-grey
     panel header, matching typical notification-dot treatments. */
  box-shadow: 0 0 0 1.5px #f1f1f1;
}

/* v83s: collapse toggle for the filter panel. Mirrors the meta-panel
   (SETTINGS/COMMENTS) collapse pattern: chevron-down when expanded
   ("click to collapse"), rotates 180° when collapsed. */
.report-sidebar__panel-collapse {
  width: 22px;
  height: 22px;
  border: 0;
  padding: 0;
  background: transparent;
  color: #3c6fa5;
  cursor: pointer;
  border-radius: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background-color 120ms ease;
}

.report-sidebar__panel-collapse:hover {
  background: rgba(42, 109, 179, 0.16);
}

.report-sidebar__panel-collapse:focus-visible {
  outline: 2px solid #2a6db3;
  outline-offset: -2px;
}

.report-sidebar__panel-collapse-icon {
  display: block;
  width: 14px;
  height: 14px;
  transition: transform 0.15s ease;
  /* v86t: chevron direction flipped per user request — now points UP (∧)
     when expanded, DOWN (∨) when collapsed. Previously was the opposite.
     The base SVG draws ∨; rotating by 180° in the expanded state makes
     it read as ∧. The --collapsed rule below cancels the rotation. */
  transform: rotate(180deg);
}

.report-sidebar__panel--collapsed .report-sidebar__panel-collapse-icon {
  transform: none;
}

/* When collapsed, the panel shrinks to just its header row. flex-grow
   drops to 0 so the data tree above reclaims the freed vertical space;
   overflow switches to visible so a residual scrollbar never shows on
   an empty panel body. The body itself is hidden via display:none. */
.report-sidebar__panel--collapsed {
  flex: 0 0 auto !important;
  min-height: 0 !important;
  overflow: visible !important;
}

.report-sidebar__panel--collapsed > .report-sidebar__panel-inner {
  display: none;
}
/* .203: toolbar lives between header and panel-inner so it doesn't
   scroll with the tree; mirror the inner's collapse rule. */
.report-sidebar__panel--collapsed > .report-sidebar__panel-toolbar {
  display: none;
}

/* .203: data-tree toolbar (search button + hierarchy switcher).
   Sits at the top of the panel-inner, between the header (hidden in
   the temporary always-expanded layout) and the tree list. Mirrors
   React's <ui5-bar design="Subheader"> strip — a thin row of
   transparent icon buttons that flips one of them to a search input
   on click. Layout uses flex so the optional hierarchy button anchors
   to the right edge. */
.report-sidebar__panel-toolbar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  border-bottom: 1px solid #e2e8f0;
  background: #f8fafc;
  flex: 0 0 auto;
  min-height: 34px;
}
.report-sidebar__panel-toolbar-btn {
  width: 26px;
  height: 26px;
  border: 0;
  padding: 0;
  background: transparent;
  color: #3c6fa5;
  cursor: pointer;
  border-radius: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background-color 120ms ease;
}
/* .207: explicit display:none for the hidden state. Without this the
   `display: inline-flex` rule above wins over the UA default for
   `[hidden]`, leaving the collapsed-state search button visible
   alongside the expanded input. */
.report-sidebar__panel-toolbar-btn[hidden] { display: none; }
.report-sidebar__panel-toolbar-btn:hover {
  background: rgba(42, 109, 179, 0.16);
}
.report-sidebar__panel-toolbar-btn:focus-visible {
  outline: 2px solid #2a6db3;
  outline-offset: -2px;
}
.report-sidebar__panel-toolbar-btn--end {
  margin-left: auto;
}
.report-sidebar__panel-toolbar-btn svg {
  width: 14px;
  height: 14px;
  display: block;
}
/* Search-input wrap. Replaces the search button when expanded. The wrap
   itself is a flex row; the input flexes, and a clear-button anchors
   to the right edge of the wrap with a border-divider mimicking
   ui5-input's slot icons. */
.report-sidebar__panel-toolbar-search {
  display: flex;
  align-items: center;
  flex: 1 1 auto;
  min-width: 0;
  background: #fff;
  border: 1px solid #cfd6de;
  border-radius: 4px;
  padding: 0 4px;
  height: 26px;
  gap: 2px;
}
.report-sidebar__panel-toolbar-search[hidden] { display: none; }
.report-sidebar__panel-toolbar-input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  outline: 0;
  background: transparent;
  font: inherit;
  color: #18324d;
  padding: 0 4px;
  height: 100%;
}
.report-sidebar__panel-toolbar-input::placeholder {
  color: #94a3b8;
}
/* .206: shared style for the cancel (×) and execute (🔍) buttons that
   sit inside the expanded search input. Mirrors React's TreeSearchControl
   layout: [input][cancel][execute]. The execute button is the active
   trigger — clicking it (or pressing Enter) fires /findDataTree. */
.report-sidebar__panel-toolbar-search-action {
  width: 20px;
  height: 20px;
  border: 0;
  padding: 0;
  background: transparent;
  color: #6b7c93;
  cursor: pointer;
  border-radius: 3px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
}
.report-sidebar__panel-toolbar-search-action:hover {
  color: #18324d;
  background: rgba(15, 23, 42, 0.06);
}
.report-sidebar__panel-toolbar-search-action[data-role="data-tree-search-execute"] {
  color: #3c6fa5;
}
.report-sidebar__panel-toolbar-search-action[data-role="data-tree-search-execute"]:hover {
  color: #2a6db3;
  background: rgba(42, 109, 179, 0.16);
}
.report-sidebar__panel-toolbar-search-action svg {
  display: block;
}
.report-sidebar__panel-toolbar-search-action[data-role="data-tree-search-cancel"] svg {
  width: 11px; height: 11px;
}
.report-sidebar__panel-toolbar-search-action[data-role="data-tree-search-execute"] svg {
  width: 13px; height: 13px;
}

/* .203: multi-result search dialog — mirrors React's TreeSearchDialog.
   Modal overlay + centered card. The list shows itemPath as the title
   and an optional description below. Click highlights, double-click
   commits. The OK button is disabled until something is selected. */
.data-tree-search-dialog__overlay {
  position: fixed;
  inset: 0;
  z-index: 2100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(15, 23, 42, 0.34);
}
.data-tree-search-dialog {
  width: min(560px, calc(100vw - 40px));
  max-height: min(72vh, 720px);
  background: #fff;
  border: 1px solid #cfd6de;
  border-radius: 10px;
  box-shadow: 0 18px 40px rgba(15, 23, 42, 0.22);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.data-tree-search-dialog__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-bottom: 1px solid #e2e8f0;
  font-weight: 600;
  color: #18324d;
}
.data-tree-search-dialog__close {
  width: 22px;
  height: 22px;
  border: 0;
  padding: 0;
  background: transparent;
  color: #3c6fa5;
  cursor: pointer;
  border-radius: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.data-tree-search-dialog__close:hover { background: rgba(42, 109, 179, 0.16); }
.data-tree-search-dialog__close svg { width: 12px; height: 12px; }
.data-tree-search-dialog__list {
  list-style: none;
  margin: 0;
  padding: 6px 0;
  overflow-y: auto;
  flex: 1 1 auto;
}
.data-tree-search-dialog__item {
  padding: 8px 16px;
  cursor: pointer;
  border-left: 3px solid transparent;
  transition: background-color 80ms ease;
}
.data-tree-search-dialog__item:hover { background: #f1f5f9; }
.data-tree-search-dialog__item--selected {
  background: rgba(42, 109, 179, 0.10);
  border-left-color: #2a6db3;
}
.data-tree-search-dialog__item-title {
  color: #18324d;
  font-weight: 500;
  word-break: break-all;
}
.data-tree-search-dialog__item-desc {
  margin-top: 2px;
  color: #5b6b80;
  font-size: 12px;
}
.data-tree-search-dialog__footer {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 16px;
  border-top: 1px solid #e2e8f0;
  background: #f8fafc;
}
.data-tree-search-dialog__btn {
  height: 30px;
  padding: 0 14px;
  border-radius: 6px;
  border: 1px solid #cfd6de;
  background: #fff;
  color: #18324d;
  cursor: pointer;
  font: inherit;
}
.data-tree-search-dialog__btn--ghost { color: #3c6fa5; }
.data-tree-search-dialog__btn--ghost:hover { background: #f1f5f9; }
.data-tree-search-dialog__btn--primary {
  background: #2a6db3;
  color: #fff;
  border-color: #2a6db3;
}
.data-tree-search-dialog__btn--primary:hover { background: #235a96; }
.data-tree-search-dialog__btn--primary:disabled {
  background: #b5c2d2;
  border-color: #b5c2d2;
  cursor: not-allowed;
}

/* .203: hierarchy switcher menu — anchored popup. Items show a check
   mark next to the active hierarchy. */
.data-tree-hier-menu {
  z-index: 2050;
  background: #fff;
  border: 1px solid #cfd6de;
  border-radius: 6px;
  box-shadow: 0 12px 24px rgba(15, 23, 42, 0.18);
  padding: 4px;
  min-width: 180px;
  max-height: 50vh;
  overflow-y: auto;
}
.data-tree-hier-menu__item {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  border: 0;
  background: transparent;
  padding: 6px 10px;
  border-radius: 4px;
  cursor: pointer;
  text-align: left;
  color: #18324d;
  font: inherit;
}
.data-tree-hier-menu__item:hover { background: #f1f5f9; }
.data-tree-hier-menu__item--active { color: #2a6db3; font-weight: 500; }
.data-tree-hier-menu__check {
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
  color: #2a6db3;
}

/* 26.1.0.41: temporary — sidebar tree panels always expanded, no
   collapse/expand UI. Hides the panel headers (the title-icon +
   chevron bars at the top of each tree) and overrides the
   --collapsed-state styling above so a stale `--collapsed` class
   on the filter panel (persisted from earlier in localStorage) or
   on the data-tree panel doesn't shrink the panel or hide its
   body. Pairs with the dataTreeBodyOpen change at line ~3114 of
   pages/analysis-page.js, which guarantees the data-tree body
   actually gets rendered (the renderDataTreePanel function
   returns header-only when bodyOpen is false). To revert this
   feature: delete this entire block and restore the original
   `dataTreeBodyOpen = dataTreePanelVisible && !!dataTreeOpen`
   line in analysis-page.js. */
.report-sidebar__panel-header { display: none; }
.report-sidebar__panel--collapsed {
  flex: 1 1 auto !important;
  min-height: 0 !important;
  overflow: hidden !important;
}
.report-sidebar__panel--collapsed > .report-sidebar__panel-inner {
  display: block;
}

.report-sidebar__panel-body {
  padding: 0;
}

.report-sidebar__panel-body--tree {
  padding: 0;
}

.report-sidebar__summary {
  padding: 0 12px 8px;
  color: #35506b;
  font-size: 12px;
}

.report-sidebar__list {
  display: grid;
  gap: 4px;
  overflow-x: hidden;
}

.report-sidebar__list-item {
  display: flex;
  align-items: center;
  /* Tighter gap between the expand chevron and the checkbox/label
     — matches BICycle's DataTree row layout where the two affordances
     sit adjacent. The old 8px left a disproportionate gap that made
     each row look sparser than the label content warranted. */
  gap: 2px;
  min-height: 28px;
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
  /* Left padding trimmed from 12px to 6px so the tree sits closer to
     the sidebar edge. Right padding unchanged so labels still get
     breathing room before hitting the border. */
  padding: 0 12px 0 6px;
  color: #1d2d44;
  font-size: 13px;
}

/* Whole-row hover target on tree items — makes it easier to hit the
   checkbox without pixel-precise aim. Doesn't fire on the placeholder
   "No filters available" / "Loading..." rows which use --muted. */
.report-sidebar__tree-item:hover:not(.report-sidebar__list-item--muted) {
  background: rgba(42, 109, 179, 0.06);
  cursor: pointer;
}

.report-sidebar__list-icon {
  color: #6281a3;
  font-size: 12px;
}


.report-sidebar__list-item--muted {
  color: #6f8499;
}

.report-sidebar__tree-item {
  align-items: center;
  cursor: default;
  min-height: 24px;
}

.report-sidebar__tree-item--selected {
  /* Selection styling moved to .report-sidebar__tree-select so the
     blue background wraps tightly around the label/icon row instead
     of the full-width container (which leaves the chevron unstyled).
     Kept here only as a sentinel so cascading selectors that rely on
     `--selected` for non-color rules still match. */
  background: transparent;
  color: inherit;
  font-weight: 600;
  box-shadow: none;
}

/* Drag-drop target highlight — shows which tree item the user is about
   to drop one-or-more grid rows onto. Brand-colored dashed outline so
   it reads as "active target" distinct from the solid-fill selection
   state above. See services/row-drag-drop-service.js for the drag
   payload contract. */
.report-sidebar__tree-item--drop-target {
  background: rgba(63, 120, 173, 0.15);
  outline: 2px dashed var(--brand-dark);
  outline-offset: -2px;
  border-radius: 3px;
}

.report-sidebar__tree-group {
  display: grid;
  gap: 0;
}

.report-sidebar__tree-children {
  display: grid;
  gap: 0;
}

.report-sidebar__tree-item--level-1 {
  padding-left: 28px;
}

.report-sidebar__tree-item--level-2 {
  padding-left: 44px;
}

.report-sidebar__tree-item--level-3 {
  padding-left: 60px;
}

.report-sidebar__tree-item--level-4 {
  padding-left: 76px;
}

.report-sidebar__tree-item--level-5 {
  padding-left: 92px;
}

.report-sidebar__tree-item--level-6 {
  padding-left: 108px;
}

/* 26.1.1.37: tree depths past 6 were inheriting padding-left: 0,
   which made deep nodes (e.g. P / P/M under MAH04 → F01 → A04 → ...)
   visually jump back to the sidebar's left edge. Continue the
   +16 px-per-level progression up to level 15 to cover any
   realistic FDB / MAH hierarchy depth. */
.report-sidebar__tree-item--level-7 {
  padding-left: 124px;
}

.report-sidebar__tree-item--level-8 {
  padding-left: 140px;
}

.report-sidebar__tree-item--level-9 {
  padding-left: 156px;
}

.report-sidebar__tree-item--level-10 {
  padding-left: 172px;
}

.report-sidebar__tree-item--level-11 {
  padding-left: 188px;
}

.report-sidebar__tree-item--level-12 {
  padding-left: 204px;
}

.report-sidebar__tree-item--level-13 {
  padding-left: 220px;
}

.report-sidebar__tree-item--level-14 {
  padding-left: 236px;
}

.report-sidebar__tree-item--level-15 {
  padding-left: 252px;
}

.report-sidebar__expand-icon {
  width: 16px;
  flex: 0 0 16px;
  color: #3c6fa5;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  align-self: center;
  justify-content: center;
  margin-top: 0;
}

.report-sidebar__expand-toggle {
  border: 0;
  background: transparent;
  padding: 0;
  cursor: pointer;
  border-radius: 4px;
  transition: background-color 120ms ease, box-shadow 120ms ease;
}

.report-sidebar__expand-toggle:hover {
  background: rgba(42, 109, 179, 0.16);
  box-shadow: inset 0 0 0 1px rgba(42, 109, 179, 0.45);
}

.report-sidebar__expand-toggle:focus-visible,
.report-sidebar__tree-select:focus-visible {
  outline: 2px solid #2a6db3;
  outline-offset: 2px;
}

.report-sidebar__expand-icon--spacer {
  pointer-events: none;
}

.report-sidebar__tree-select {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  border: 0;
  background: transparent;
  padding: 2px 4px;
  margin: 0 0 0 4px;
  text-align: left;
  cursor: pointer;
  color: inherit;
  font: inherit;
  border-radius: 4px;
  transition: background-color 120ms ease, box-shadow 120ms ease;
}

/* Per-item icon image (resolved from item.imageId via app-metadata).
   Sits between the chevron and the label, with a small gap. Same 16px
   target size used by the filter tree's node icons for visual parity. */
.report-sidebar__tree-node-icon {
  display: inline-block;
  flex: 0 0 16px;
  width: 16px;
  height: 16px;
  margin-right: 6px;
  object-fit: contain;
  vertical-align: middle;
}

.report-sidebar__tree-select:hover {
  /* UI5-style hover: light blue tint, matching the old React app's
     ui5-li-tree row look. Replaces the previous green hover. */
  background: rgba(50, 116, 196, 0.10);
}

/* Selected row(s) — applies to single-select and Ctrl-click multi-
   select alike. UI5 light-blue rgb(142, 199, 249) with the standard
   dark text colour (rgb 35, 38, 41) — matches the old app's
   .dataTreeItem selected state. */
.report-sidebar__tree-item--selected > .report-sidebar__tree-select,
.report-sidebar__tree-item--selected > .report-sidebar__tree-select:hover {
  background: rgb(142, 199, 249);
  color: rgb(35, 38, 41);
  box-shadow: none;
}

.report-sidebar__tree-item--selected > .report-sidebar__tree-select .report-sidebar__tree-text,
.report-sidebar__tree-item--selected > .report-sidebar__tree-select .report-sidebar__tree-meta {
  color: rgb(35, 38, 41);
}

.report-sidebar__chevron-svg {
  width: 12px;
  height: 12px;
  display: block;
}

.report-sidebar__chevron-spacer {
  width: 12px;
  height: 12px;
  display: inline-block;
}

.report-sidebar__tree-text {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-self: center;
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
}

.report-sidebar__tree-label {
  display: block;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  /* UI5 dataTreeItemText typography from the old app:
     font: '72', '72full', Arial, Helvetica, sans-serif
     12px / 18px line-height, color rgb(35, 38, 41). */
  font-family: "72", "72full", Arial, Helvetica, sans-serif;
  font-size: 12px;
  line-height: 18px;
  color: rgb(35, 38, 41);
}

.report-sidebar__tree-item--selected .report-sidebar__tree-label {
  /* Selected text stays dark on the light-blue background — matches
     the old app's selected look (the right-side screenshot). */
  color: rgb(35, 38, 41);
}

/* ─── Data-tree indicators + edit pen (v74) ─────────────────────────────────
   Mirrors BICycle's DataTreeItem.js::mapKPIs layout.  Indicators are
   right-aligned fixed-width slots appended to each row; the edit pen sits
   between the label and the indicators.  (v83f: the per-tree
   showAllIndicators toggle moved to the settings bar — the old
   in-panel header bar + toggle button CSS was removed.) */

.report-sidebar__tree-pen {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border: 0;
  padding: 0;
  background: transparent;
  color: #3c6fa5;
  cursor: pointer;
  border-radius: 4px;
  transition: background-color 120ms ease;
}

.report-sidebar__tree-pen:hover {
  background: rgba(42, 109, 179, 0.16);
}

.report-sidebar__tree-pen:focus-visible {
  outline: 2px solid #2a6db3;
  outline-offset: 2px;
}

.report-sidebar__tree-pen-svg {
  display: block;
  width: 14px;
  height: 14px;
}

/* v83o: indicator toggle (`»` / `«`) at the far right of the tree's
   root row. Matches the pen button's treatment — small transparent
   icon button with a blue hover tint — so the row's controls read
   as one group. Click flips the per-tree showAllIndicators flag. */
.report-sidebar__tree-indicator-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border: 0;
  padding: 0;
  margin-left: 2px;
  background: transparent;
  color: #3c6fa5;
  cursor: pointer;
  border-radius: 4px;
  transition: background-color 120ms ease;
}

.report-sidebar__tree-indicator-toggle:hover {
  background: rgba(42, 109, 179, 0.16);
}

.report-sidebar__tree-indicator-toggle:focus-visible {
  outline: 2px solid #2a6db3;
  outline-offset: 2px;
}

.report-sidebar__tree-indicator-toggle[aria-pressed="true"] {
  color: #1e4e8c;
}

.report-sidebar__tree-indicator-toggle-svg {
  display: block;
  width: 14px;
  height: 14px;
}

.report-sidebar__tree-indicators {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  gap: 0;
  margin-left: auto; /* push to the right edge when label is short */
}

.report-sidebar__tree-indicator {
  flex: 0 0 auto;
  min-width: 56px;
  padding: 0 6px;
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  line-height: 1.2;
  color: #1d2d44;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.report-sidebar__tree-indicator--label {
  color: #456a8e;
  font-weight: 600;
}

.report-sidebar__tree-item--selected .report-sidebar__tree-indicator--label {
  color: #123f73;
}

/* Pen menu (v75).  Attached to document.body (position:fixed) so it floats
   above the scrollable sidebar.  Styled to match the bundle-menu pattern
   used elsewhere in the app for visual consistency. */

.report-sidebar__tree-edit-menu {
  position: fixed;
  z-index: 1200;
  min-width: 180px;
  background: #ffffff;
  border: 1px solid rgba(42, 109, 179, 0.28);
  border-radius: 6px;
  box-shadow: 0 6px 18px rgba(18, 63, 115, 0.18);
  padding: 4px 0;
  display: flex;
  flex-direction: column;
  font-size: 13px;
  color: #1d2d44;
}

.report-sidebar__tree-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 14px;
  border: 0;
  background: transparent;
  text-align: left;
  cursor: pointer;
  color: inherit;
  font: inherit;
  white-space: nowrap;
}

.report-sidebar__tree-menu-item:hover:not(.report-sidebar__tree-menu-item--disabled) {
  background: rgba(42, 109, 179, 0.10);
}

.report-sidebar__tree-menu-item:focus-visible {
  outline: 2px solid #2a6db3;
  outline-offset: -2px;
}

.report-sidebar__tree-menu-item--disabled {
  color: #9aa8b5;
  cursor: default;
}

.report-sidebar__tree-menu-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  color: #3c6fa5;
}

.report-sidebar__tree-menu-item--disabled .report-sidebar__tree-menu-icon {
  color: #b0bcc7;
}

.report-sidebar__tree-menu-label {
  flex: 1 1 auto;
}

/* The fourth-and-beyond slots share the same slot class (capped at 3) so
   arbitrary-width KPI counts still lay out predictably. */
.report-sidebar__tree-indicator-slot--0 { min-width: 56px; }
.report-sidebar__tree-indicator-slot--1 { min-width: 56px; }
.report-sidebar__tree-indicator-slot--2 { min-width: 56px; }
.report-sidebar__tree-indicator-slot--3 { min-width: 56px; }

.report-sidebar__tree-record-count.report-sidebar__tree-indicator--value {
  color: #6281a3;
}

/* ─────────────────────────────────────────────────────────────────────── */

.report-sidebar__filter-item {
  align-items: center;
}


.report-sidebar__filter-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-bottom: 1px solid var(--line);
  background: #ffffff;
}

.report-sidebar__filter-action {
  min-height: 28px;
  padding: 0 10px;
  border: 1px solid #2a6db3;
  border-radius: 6px;
  background: #ffffff;
  color: #1c56a4;
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
}

.report-sidebar__filter-action--apply {
  min-width: 62px;
}

.report-sidebar__filter-action:hover {
  background: #eef5ff;
}


.report-sidebar__checkbox {
  width: 14px;
  height: 14px;
  margin: 0;
  flex: 0 0 auto;
}

/* Colored square shown next to filter members that carry a color (e.g. Severity Class) */
.report-sidebar__color-swatch {
  flex: 0 0 12px;
  width: 12px;
  height: 12px;
  border-radius: 2px;
  margin-right: 4px;
  display: inline-block;
  border: 1px solid rgba(0, 0, 0, 0.18);
  vertical-align: middle;
}

/* Tri-state check button for the filter tree — matches export dialog style */
.filter-check {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 14px;
  padding: 0;
  flex: 0 0 20px;
  width: 20px;
  height: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  color: #444;
}
.filter-check--checked  { color: #1a4a87; }
.filter-check--partial  { color: #1a4a87; }
.filter-check--unchecked { color: #aab4be; }
.filter-check:hover { opacity: 0.75; }

.report-sidebar__tree-meta {
  color: #6f8499;
  font-size: 11px;
}

/* 26.1.0.74: splitters flattened to a thin hairline-style per
   user request. The old design had a raised look — gradient
   background (#f4f4f4 → #ededed), full 1px border on all sides,
   permanent blue-grey grip-dot pattern at 0.9 opacity. The
   Windows-app reference is a flat neutral strip with no chrome
   and no grip pattern, just a single hairline indicating the
   panel separation. New behaviour:
   - 8px hit area kept (narrower would be hard to grab; the
     visible body inside the 8px is what's flattened, not the
     interaction width).
   - Solid #f0f0f0 panel-grey background instead of the gradient
     so the bar reads flat against either neighbouring panel.
   - One hairline (border-left for vertical, border-top for
     horizontal) instead of a 4-sided border — this is the
     visible "line" between panels, matching the flat-panel
     idiom.
   - Grip dots hidden at rest (opacity 0). They re-appear on
     :hover/:focus-visible so the affordance is still
     discoverable when the user goes to grab the splitter, but
     doesn't visually clutter the layout when idle. */
.report-sidebar__splitter {
  position: relative;
  flex: 0 0 auto;
  border: 0;
  background: #f0f0f0;
}

.report-sidebar__splitter--vertical {
  width: 8px;
  cursor: col-resize;
  background: #f0f0f0;
  border-left: 1px solid var(--line);
}

.report-sidebar__splitter--horizontal {
  height: 8px;
  cursor: row-resize;
  border-top: 1px solid var(--line);
}

/* Grip dots via ::before.
   26.1.0.74: in idle state I had opacity:0 (only fade in on
   hover) as part of the flat-style splitter pass. That was a
   misread of the flatten request — flat is about removing the
   gradient/bordered chrome around the bar, NOT about hiding the
   grip. With the dots invisible, the splitter looked like an
   empty seam: users couldn't tell it was draggable until they
   chanced to hover.
   26.1.0.78: dots always visible (opacity 0.7) per user
   feedback. The hover/focus rule below now bumps opacity
   slightly stronger as a subtle interaction hint, instead of
   being the only state where the grip appears at all. */
.report-sidebar__splitter::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  background: repeating-linear-gradient(90deg, #9db0c4 0 3px, transparent 3px 5px);
  opacity: 0.7;
  pointer-events: none;
  transition: opacity 120ms ease;
}

.report-sidebar__splitter:hover::before,
.report-sidebar__splitter:focus-visible::before {
  /* 26.1.0.78: hover-bump from idle 0.7 to full opacity. Subtle
     lift that confirms the splitter is interactive without the
     splitter being invisible at rest. */
  opacity: 1;
}

.report-sidebar__splitter--horizontal::before {
  width: 34px;
  height: 4px;
}

.report-sidebar__splitter--vertical::before {
  width: 4px;
  height: 34px;
  background: repeating-linear-gradient(0deg, #9db0c4 0 3px, transparent 3px 5px);
}

.report-content-area {
  /* 26.1.0.30: the content area is the only flex child of .report-main
     that should consume remaining vertical space.  toolbar / heading-
     strip / .report-nav / content-sub-tabs are all locked to
     flex: 0 0 auto, leaving the content area to grow or shrink as the
     viewport / meta-panel changes.  Without this explicit grow, every
     sibling defaults to flex: 0 1 auto and the browser distributes the
     overflow proportionally — squashing the tab rows. */
  flex: 1 1 auto;
  padding: 10px 12px;
  min-width: 0;
  min-height: 0;
}

/* Single-content subHeadings: remove the outer padding so the
   data-table/kpi-chart sits flush against the subheading-tab row
   above and the sidebar to the left. Multi-content dashboards keep
   the padding so their cards breathe. */
.report-content-area--single-content {
  padding: 0;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.report-content-area--single-content > data-table,
.report-content-area--single-content > kpi-chart,
.report-content-area--single-content > .section-loading {
  /* Grid + chart must fill the content area (grow + stretch). This
     was briefly broken in v86j/k when the section-error alert fix was
     merged into this selector and pulled data-table / kpi-chart along
     with it into flex:0 0 auto — the chart collapsed to its natural
     content width. Split out again. */
  flex: 1 1 auto;
  min-height: 0;
}

.report-content-area--single-content > .section-error {
  /* v86j: when section-error is the inline alert, it must sit at its
     natural height rather than stretch to fill the content area. This
     rule is deliberately SEPARATE from data-table / kpi-chart — do not
     re-merge. flex:0 0 auto + align-self: flex-start pins the alert to
     the top without collapsing its siblings. */
  flex: 0 0 auto;
  align-self: flex-start;
  min-height: 0;
}

/* Single-content layout: strip the rounded corner + border from the
   data-table shell so it sits flush against the subheading tabs and
   sidebar. Multi-content dashboard cells keep their framed look. */
.report-content-area--single-content > data-table .data-table-shell,
.report-content-area--single-content > kpi-chart .data-table-shell {
  border: 0;
  border-radius: 0;
}

.report-content-area--single-content,
.report-content-area--single-content.report-content-area--with-meta {
  overflow: hidden;
}

/* When the meta panel is open, --single-content stays as flex-column.
   The critical change is switching from flex-basis:auto to flex-basis:0
   on the content child. basis:auto lets the natural content height bypass
   the flex constraint, so the horizontal scrollbar ends up behind the panel.
   basis:0 forces growth from zero — the table is always constrained to
   the space above the panel. */
.report-content-area--single-content.report-content-area--with-meta > data-table,
.report-content-area--single-content.report-content-area--with-meta > kpi-chart,
.report-content-area--single-content.report-content-area--with-meta > .section-loading {
  flex: 1 1 0;   /* basis:0 is the key difference from the base rule */
  min-height: 0;
  overflow: hidden; /* clip to allocated flex space so scrollbar stays above the panel */
}

/* v86j: section-error in the meta-panel-open variant follows the same
   "natural height, pinned top" rule as the non-meta variant above. */
.report-content-area--single-content.report-content-area--with-meta > .section-error {
  flex: 0 0 auto;
  align-self: flex-start;
  min-height: 0;
}

/* When meta panel is open, constrain the data-table shell to the element's
   flex-allocated height. Without this, the shell uses max-height:calc(100vh-290px)
   which can be larger than the available space; its bottom (and the horizontal
   scrollbar) would overflow the element's overflow:hidden boundary and disappear. */
.report-content-area--single-content.report-content-area--with-meta > data-table .data-table-shell,
.report-content-area--single-content.report-content-area--with-meta > data-table .data-table-shell--fill-viewport {
  max-height: 100% !important;
  height: 100%;
}

/* Meta panel sits as a fixed-height flex child below the table */
.report-content-area--single-content.report-content-area--with-meta > .report-content-meta {
  flex: 0 0 auto;
  margin-top: 0;
  border-top: 2px solid #bdbdbd;
  min-height: 0;
}


.report-multi-content {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 100%;
  height: 100%;
  /* Dashboards with an explicit rowPercentage (e.g. "50") lay each row
     out at a fixed fraction of the multi-content viewport. When the
     dashboard has 3+ such rows, the rows' combined height exceeds the
     viewport and needs to scroll vertically — matching the original
     BICycle React behaviour where dashboard rows scroll inside the
     content area rather than being squeezed to fit. */
  overflow-y: auto;
  overflow-x: hidden;
}

.report-multi-content__row {
  display: grid;
  gap: 8px;
  min-height: 0;
}

.report-multi-content__cell {
  min-width: 0;
  min-height: 0;
  display: flex;
}

.report-multi-content__cell > .section-loading,
.report-multi-content__cell > .section-error {
  width: 100%;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line);
  background: #fff;
}

/* v86j: re-assert the error-left-accent + tinted background on
   multi-content cells (the shorthand border above clobbers them,
   and the shared cell background rule hides the tinted alert
   background). Keeps error identity consistent across single-
   and multi-content layouts. */
.report-multi-content__cell > .section-error {
  border-left: 4px solid #c53030;
  background: #fff5f5;
}

.report-multi-content__cell > kpi-chart {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  height: 100%;
}

/* When a dashboard cell switches to table display (types 1/2), the data-table
   element fills its grid slot.  Scrolling is handled entirely by the inner
   .data-table-scroll — setting overflow:hidden here prevents the duplicate
   vertical scrollbars that would otherwise appear alongside .data-table-scroll. */
.report-multi-content__cell > data-table {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  height: 100%;
}

/* data-table's syncViewportLayout() computes --table-shell-height as
   (window.innerHeight - shell.top - 12), which is correct for a full-page
   single-content view but wrong inside a constrained dashboard grid cell:
   the shell's top may be 300 px from the viewport top while the cell is only
   200 px tall, so the variable becomes 400 px and the shell overflows.
   Overriding height to 100% here makes the fill-viewport shell occupy exactly
   the height of its data-table parent element (which itself fills the cell via
   flex-stretch), regardless of the incorrectly computed CSS variable. */
.report-multi-content__cell .data-table-shell--fill-viewport {
  height: 100% !important;
}

body.route-analysis .report-multi-content {
  height: 100%;
  min-height: 0;
}

body.route-analysis .report-multi-content__row {
  flex: 1 1 0;
  min-height: 0;
}

/* Rows carrying an explicit rowPercentage set their own flex via inline
   style (`flex:0 0 <n>%`), which must win over the generic `flex:1 1 0`
   above — the shorthand `flex` expands to all three longhands, so the
   equal-split rule would otherwise clobber the basis + grow we intend.
   This rule targets only rows flagged by the JS so it doesn't affect
   subHeadings without a rowPercentage. */
body.route-analysis .report-multi-content__row--explicit {
  flex: none;
}

/* Fix 2: In multi-content dashboard cells and single-content table views,
   table columns should size to their content rather than stretching to fill
   the full available width — matching the original BICycle React behaviour. */
body.route-analysis .report-multi-content__cell .data-table,
body.route-analysis .report-content-area--single-content .data-table {
  min-width: 0;
}

/* ── Content placeholder ── */
.content-placeholder {
  border: 1px dashed var(--line);
  border-radius: 8px;
  padding: 48px 20px;
  background: #fafcff;
  text-align: center;
  min-height: 160px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.content-placeholder__text {
  margin: 0;
  font-size: 14px;
  color: var(--muted);
}

/* ── Responsive ── */
@media (max-width: 1100px) {
  .pricing-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 899px) {
  /* Hide expanded search; show the collapsed icon */
  .top-search-wrap .top-search { display: none; }
  .top-search-wrap .top-search__collapsed-btn { display: inline-flex; }

  /* When the icon is tapped, the search bar drops below the nav as an overlay row */
  .top-search-wrap.is-expanded .top-search {
    display: inline-flex;
    position: absolute;
    top: 56px;
    left: 8px;
    right: 8px;
    width: auto;
    height: 44px;
    z-index: 60;
    background: var(--nav-bg);
    border-radius: 0 0 12px 12px;
    border-color: rgba(255,255,255,0.35);
    box-shadow: 0 6px 18px rgba(10,28,55,0.22);
  }
  .top-search-wrap.is-expanded .top-search__close { display: inline-flex; }
}

@media (max-width: 760px) {
  .container { padding: 16px 16px 32px; }

  /* v877 (+1): match the container's mobile gutter so the AL-Cycle
     chip and Sign-in chip stay aligned with content below at
     narrow widths. */
  .top-nav--public .top-nav__inner { padding: 0 16px; }

  /* On narrow screens: hide nav links (public nav only now) */
  .top-nav--public .nav-links { display: none; }

  /* Public-nav user label collapses to avatar-only */
  .top-nav--public .account-menu__label,
  .top-nav--public .account-menu__chevron { display: none; }

  .summary-grid { grid-template-columns: 1fr; }
  .pricing-grid { grid-template-columns: 1fr; }
  .feature-grid { grid-template-columns: 1fr; }

  .about-content { flex-direction: column; }
  .about-image img { width: 80px; }
  /* 26.1.0.7: stack About + Contact on narrow viewports. Grid
     collapses to single column; DOM order is Contact first, About
     second (per 26.1.0.8 swap), preserving the action-before-context
     reading order on mobile. */
  body.route-home:not(.has-reports-rail) .about-contact-row {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 760px) {
  .report-top-bar { padding: 6px 10px; gap: 8px; }
  .rtb-title { max-width: 120px; }
  .subheading-tabs { overflow: visible; flex-wrap: nowrap; }
  /* v85i: at narrow viewport widths the sidebar stays as a full-height
     left rail — matching the "normal" rail-mode look rather than
     stacking the sidebar below the main column as v84k did. The prior
     stacked layout dropped the settings-bar icons off into a strip
     below the content, which hid the data-tree / time-window / filter
     affordances users still need at narrow widths. Forcing rail mode
     here means the three vertical rail icons stay visible and
     clickable regardless of viewport width. */
  .report-workspace { flex-direction: row; }
  .report-content-area { padding: 10px 12px; }
}

/* v933: auto-rail @media removed. v932 forced the visual rail at
   ≤600px via CSS, but the settings-bar component renders its rail
   icons based on the JS `sidebarCollapsed` flag — which the CSS
   media query didn't touch. So auto-rail looked like a rail but
   had no data-tree/filter-tree icons (they only render when
   controls.sidebarCollapsed is true), and the rail-toggle chevron
   didn't invert (it uses the .report-sidebar--collapsed class,
   added only on JS toggle). Auto-rail is now driven from JS by a
   resize listener that flips the flag at the same threshold,
   triggering the same render path as the user-initiated chevron
   click. See pages/analysis-page.js for the wire-up. */


/* ── Inspector strip (v997) ── */
/* Left-side single-record inspectors, one per drilled stack level.
   Sibling of <data-table> inside .analysis-inspector-layout. Each
   panel collapses to a narrow strip (32px) with a chevron; expanded
   panels show a two-column label/value list of the parent row's
   fields. Strip hides entirely when no drill is active.
   See renderInspectorStrip in pages/analysis-page.js. */
.analysis-inspector-layout {
  display: flex;
  flex-direction: row;
  align-items: stretch;
  height: 100%;
  min-height: 0;
  min-width: 0;
  gap: 0;
  /* v1003: default label-column ratio (inner-panel). Overridden
     at runtime by the column-splitter drag via setProperty on
     the strip. */
  --inspector-label-ratio: 0.4;
}
/* v1004: main content column to the right of the inspector strip
   + splitter. Holds the top-bar (Back / path / collapse chevron)
   above the data-table so the bar spans only the main area, not
   the full layout width. */
.analysis-inspector-main {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  gap: 0;
}
.analysis-inspector-main > data-table {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* v1003: topbar row above strip + grid. Hosts Back (left), path
   (center), strip-collapse chevron (right). Hidden via [hidden]
   when no drill is active. */
.analysis-inspector-topbar {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  padding: 4px 8px;
  min-height: 32px;
  border-bottom: 1px solid var(--line);
  background: #f8f9fa;
  flex: 0 0 auto;
}
.analysis-inspector-topbar[hidden] { display: none; }
.analysis-inspector-topbar__back {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 24px;
  padding: 0 6px;
  border: none;
  border-radius: 4px;
  background: transparent;
  color: #18324d;
  font: inherit;
  /* v1004: Back label dropped from inherited toolbar font (~14px)
     to a quieter 11px so it reads as the secondary navigation
     affordance — the breadcrumb path in the center is the primary
     orientation cue. Icon size unchanged. */
  font-size: 11px;
  cursor: pointer;
  flex: 0 0 auto;
}
.analysis-inspector-topbar__back:hover {
  background: rgba(23, 63, 107, 0.08);
  color: var(--brand-dark);
}
.analysis-inspector-topbar__back svg {
  width: 14px;
  height: 14px;
  flex: 0 0 14px;
}
.analysis-inspector-topbar__path {
  display: flex;
  flex: 1 1 auto;
  justify-content: center;
  align-items: center;
  gap: 6px;
  min-width: 0;
  overflow: hidden;
  font-size: 13px;
  color: var(--brand-dark);
  font-weight: 500;
}
.analysis-inspector-topbar__crumb {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.analysis-inspector-topbar__sep {
  color: var(--muted);
  font-weight: 400;
  flex: 0 0 auto;
}
.analysis-inspector-topbar__collapse {
  width: 24px;
  height: 24px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  color: inherit;
  cursor: pointer;
  border-radius: 3px;
  flex: 0 0 24px;
}
.analysis-inspector-topbar__collapse:hover {
  background: rgba(23, 63, 107, 0.08);
  color: var(--brand-dark);
}
.analysis-inspector-topbar__collapse svg {
  width: 14px;
  height: 14px;
}

.analysis-inspector-strip {
  /* v1001: inspectors stack vertically in a single column. Panel
     width follows the strip's width (set 1:1 by the splitter
     drag), so dragging the resize handle directly changes the
     inspector-panel width — not "how much empty space the strip
     has". When there are more inspectors than fit vertically, the
     strip scrolls vertically inside itself.
     Previous (v998) flex-wrap behavior put extra inspectors into
     additional 240px columns; that meant drag changed column-count
     rather than panel width. User's screenshot + direct request
     proved they want the panels themselves to grow/shrink. */
  display: flex;
  flex-direction: column;
  flex: 0 0 auto;
  min-width: 0;
  width: 240px;
  max-width: 60%;
  max-height: 100%;
  overflow-y: auto;
  overflow-x: hidden;
}
.analysis-inspector-strip[hidden] { display: none; }

.analysis-inspector-panel {
  /* v1001: panel width follows the strip (100%) so the splitter
     resize directly changes panel width. Collapsed panels lock
     to 32px regardless of strip width so a narrow icon strip
     stays usable. */
  flex: 0 0 auto;
  width: 100%;
  max-height: 100%;
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: #fafafa;
  border-bottom: 1px solid var(--line);
  transition: none;
  overflow: hidden;
}
.analysis-inspector-panel__header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  border-bottom: 1px solid var(--line);
  background: #f1f3f5;
  font-size: 12px;
  font-weight: 600;
  color: var(--brand-dark);
  min-height: 32px;
  flex: 0 0 auto;
}
.analysis-inspector-panel__title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
  flex: 0 1 auto;
}
/* v1000: record-nav widget (< N of M >) replaces the old key-value
   span in the header. The widget hugs the right side of the header
   row, arrows are small icon-buttons matching the collapse toggle,
   and the counter is a muted fixed-width label so digit-width
   changes don't shuffle the layout. */
.analysis-inspector-panel__nav {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  flex: 0 0 auto;
  margin-left: auto;
  color: var(--muted);
  font-weight: 400;
  font-size: 12px;
}
.analysis-inspector-panel__nav-btn {
  width: 22px;
  height: 22px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  color: inherit;
  cursor: pointer;
  border-radius: 3px;
}
.analysis-inspector-panel__nav-btn:hover:not(:disabled) {
  background: rgba(23, 63, 107, 0.08);
  color: var(--brand-dark);
}
.analysis-inspector-panel__nav-btn:disabled {
  opacity: 0.35;
  cursor: default;
}
.analysis-inspector-panel__nav-btn svg {
  width: 14px;
  height: 14px;
}
.analysis-inspector-panel__nav-counter {
  min-width: 54px;
  text-align: center;
  font-variant-numeric: tabular-nums;
  user-select: none;
}
.analysis-inspector-panel__body {
  flex: 1 1 auto;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 0;
  /* v1003: panel body hosts a two-column label/value grid AND a
     vertical divider + drag splitter between the columns. Label
     column width is driven by --inspector-label-ratio on the strip
     (shared across all panels). Divider is a 1px line painted via
     ::before running full panel-body height. Splitter is an
     absolutely-positioned 6px-wide hit area on top of the divider
     that the user drags to change the ratio. */
  position: relative;
}
.analysis-inspector-panel__body::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  /* Divider sits at (horizontal padding + ratio×usable-width)
     where usable-width = 100% - 2×10px padding - 10px column-gap
     = 100% - 30px. The gap center is + column-gap/2 = +5px past
     the label-column end. */
  left: calc(10px + (100% - 30px) * var(--inspector-label-ratio) + 5px);
  width: 1px;
  background: var(--line);
  pointer-events: none;
}
.analysis-inspector-panel__body dl {
  display: grid;
  /* v1004: column tracks use percentage-based calcs derived from
     --inspector-label-ratio. Previous fr-calc form
     (`calc(var * 1fr)`) had spotty support — Firefox/Safari at
     various versions silently drop the calc and behave as if 1fr
     was specified, which produced 50:50 columns regardless of
     ratio and visually overflowed when content was long. Using
     percentages of the dl width instead. The dl's 10px horizontal
     padding eats into 100%, and the column-gap of 10px sits
     between the two tracks; usable width is 100% - 10px gap. We
     use minmax with 0 minimum so columns can shrink rather than
     overflow when narrow; nowrap on dt/dd handles ellipsis there. */
  grid-template-columns:
    minmax(0, calc(var(--inspector-label-ratio) * (100% - 10px)))
    minmax(0, calc((1 - var(--inspector-label-ratio)) * (100% - 10px)));
  column-gap: 10px;
  row-gap: 2px;
  margin: 0;
  padding: 8px 10px;
  font-size: 12px;
  line-height: 1.4;
}
/* v1003: intra-panel drag handle. Sits on top of the ::before
   divider, slightly wider to give a comfortable hit area, and
   becomes visible on hover. Drag wiring lives in wireInspectorStrip
   (mousedown on [data-inspector-col-splitter]) → updates the
   --inspector-label-ratio CSS var on the strip so every panel
   resizes together. Double-clicking resets by removing the
   sessionStorage key. */
.analysis-inspector-panel__col-splitter {
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(10px + (100% - 30px) * var(--inspector-label-ratio) + 5px - 3px);
  width: 6px;
  cursor: col-resize;
  background: transparent;
  z-index: 1;
}
.analysis-inspector-panel__col-splitter:hover::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 3px;
  width: 1px;
  background: var(--brand-dark);
}
.analysis-inspector-panel__body dt {
  color: var(--muted);
  font-weight: 400;
  /* v1004: min-width 0 so the grid track honors its calc width
     and the cell ellipsizes rather than pushing wider. Default
     min-content sizing on grid items would otherwise force the
     track to fit the longest single word, breaking the ratio. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding-right: 4px;
}
.analysis-inspector-panel__body dd {
  margin: 0;
  color: #18324d;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* v1002: vertical resize handle between inspector strip and the
   main data-table. Styled to match .report-sidebar__splitter
   (8px wide, light gradient background, 1px border on the sides,
   vertical dotted grip via ::before) so the app has a single
   visual language for draggable dividers. During an active drag
   .analysis-inspector-resizing is applied to <body> to suppress
   hover/selection flicker and force the col-resize cursor even
   when the pointer drifts off the handle. */
.analysis-inspector-resize {
  position: relative;
  flex: 0 0 auto;
  width: 8px;
  align-self: stretch;
  border: 1px solid var(--line);
  border-top: 0;
  border-bottom: 0;
  background: linear-gradient(to right, #f4f4f4, #ededed);
  cursor: col-resize;
  user-select: none;
}
.analysis-inspector-resize[hidden] { display: none; }
.analysis-inspector-resize::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 4px;
  height: 34px;
  background: repeating-linear-gradient(0deg, #9db0c4 0 3px, transparent 3px 5px);
  opacity: 0.9;
  pointer-events: none;
}
body.analysis-inspector-resizing {
  cursor: col-resize !important;
  user-select: none;
}
body.analysis-inspector-resizing * {
  pointer-events: none !important;
}


/* ── Data table ── */
.data-table-shell {
  /* Positioning context for drag artefacts (column-resize guide line,
     future drag ghosts). Without this the absolutely-positioned guide
     would resolve against the viewport, not the table. */
  position: relative;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #ffffff;
  overflow: hidden;
  max-height: calc(100vh - 290px);
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.data-table-shell--fill-viewport {
  max-height: none !important;
  height: var(--table-shell-height, 100%);
  margin-bottom: 0;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}

.data-table-shell--hide-row-numbers .data-table__utility-cell {
  display: none;
}

.data-table-shell--hide-row-numbers .data-table__corner-cell {
  display: none;
}

.data-table-shell--hide-row-numbers .data-table__utility-header {
  display: none;
}

/* v85u: KPI displayType 1/2 grids set hideRowNumbers:true, which hides
   the utility column + the in-flow corner TH. The floating corner
   overlay (gear + sort buttons positioned sticky at 0,0) must also
   be hidden, otherwise it paints on top of the Description column
   header where row numbers would have been. Without this rule the
   overlay shows as a stray hamburger in the top-left even though
   the rest of the utility-column chrome is gone. */
.data-table-shell--hide-row-numbers .data-table__corner-overlay {
  display: none;
}

.data-table-shell--with-text {
  display: flex;
  flex-direction: column;
  height: var(--table-shell-height, auto);
  /* v83h: removed min-height:520 — on short viewports it pushed the
     connected-tabs + panel below the fold, making the tab row
     visually cut off at the bottom. Let the JS-computed
     --table-shell-height own sizing entirely. */
  min-height: 0;
  /* Let the JS-computed --table-shell-height own the size. The base
     .data-table-shell declares max-height: calc(100vh - 290px) which otherwise
     caps the shell below what syncViewportLayout allocates, producing dead
     space beneath the connected panel. */
  max-height: none;
}

.data-table__path {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 10px 12px 0;
  color: var(--muted);
  font-size: 13px;
}

.data-table__path-link {
  padding: 0;
  border: none;
  background: none;
  color: #1c56a4;
  font: inherit;
  cursor: pointer;
}

.data-table__path-current {
  font-weight: 700;
  color: #18324d;
}

.data-table__path-sep {
  color: #90a3b8;
}

.data-table-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--line);
  background: #f2f4f7;
}

.data-table-toolbar__left {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  flex-wrap: wrap;
}

.data-table-toolbar__kpi-title {
  /* v83f: was 15px — brought down to 13px to match the Windows
     BI-Cycle app's content-title feel (the subheading tabs below
     already use 13px so the content-title now reads as a slightly
     lighter label beneath them, not a competing heading). */
  font-size: 13px;
  font-weight: 400;
  color: #16395f;
  white-space: nowrap;
}

/* v83f: regular content title (.data-table-toolbar__title) — same
   13px reduction. Without this the title inherits the default body
   font size which visually competed with the subheading tabs. */
.data-table-toolbar__title {
  font-size: 13px;
  font-weight: 400;
  color: #16395f;
}

.data-table-toolbar__back {
  /* v994: flat/ghost back-button per user request. Previously a
     bordered pill (white bg + blue 1px border + 6px radius) which
     read as a regular button; user wanted it to sit like a text
     link with a leading chevron. No bg, no border, muted label
     color with hover emphasis. Chevron is an inline SVG sibling
     so sizing is controlled separately. */
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 28px;
  padding: 0 6px;
  border: none;
  border-radius: 4px;
  background: transparent;
  color: #18324d;
  font: inherit;
}

.data-table-toolbar__back {
  cursor: pointer;
}
.data-table-toolbar__back:hover {
  background: rgba(23, 63, 107, 0.06);
  color: var(--brand-dark);
}
.data-table-toolbar__back-icon {
  width: 14px;
  height: 14px;
  flex: 0 0 14px;
}
.data-table-toolbar__back-label {
  line-height: 1;
}
/* v995: in-toolbar title shown when drilled into a connected record.
   Reads the active table name + post-filter count, e.g.
   "Plant (4)". Sits immediately after the Back button so the left
   cluster reads "← Back   Plant (4)". Typography matches the
   .data-table-toolbar__title used on non-drilled views (medium
   weight, brand-dark color) so users recognise it as the active
   content label; the count uses the muted grey so it reads as a
   secondary annotation. Only rendered by data-table.js when
   canGoBack is true (see v995 branch in components/data-table.js). */
.data-table-toolbar__drilled-title {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  margin-left: 4px;
  padding: 0 4px;
  color: var(--brand-dark);
  font-weight: 600;
  font-size: 14px;
  line-height: 28px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 320px;
}
.data-table-toolbar__drilled-count {
  color: var(--muted);
  font-weight: 400;
}

.data-table-toolbar__search-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.data-table-toolbar__search {
  width: 280px;
  max-width: 100%;
  min-height: 28px;
  /* v85e: reverted v85d's blend treatment — the Quick Find input
     stays white with a visible border at all times, like a standard
     text field. Buttons blend because their "clickable target" is
     obvious from the icon alone; a text field needs a persistent
     boundary so users know where to click to start typing. Focus
     still strengthens the border to the brand-blue accent. */
  border: 1px solid #b9cde0;
  border-radius: 4px;
  padding: 0 52px 0 10px;
  background: #ffffff;
  font: inherit;
  font-size: 14px;
  color: inherit;
  transition: border-color 0.15s;
}
.data-table-toolbar__search:focus {
  border-color: #2d74c4;
  outline: none;
}

.data-table-toolbar__clear,
.data-table-toolbar__search-button {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  border: none;
  background: none;
  color: #1c56a4;
  cursor: pointer;
  font: inherit;
  line-height: 1;
}

.data-table-toolbar__clear {
  right: 24px;
  font-size: 18px;
}

.data-table-toolbar__search-button {
  right: 6px;
  font-size: 16px;
}

.data-table-toolbar__toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--muted);
  font-size: 14px;
}

/* .59: drag-rows checkbox. Sits right of Quick Find. Visible only
   when the content allows row-drag (see data-table.js dragAllowed).
   Layout mirrors .data-table-toolbar__toggle but with its own class
   so it can pick up subtle tweaks (smaller gap, vertical alignment
   nudge to keep the checkbox optical-centre on the input baseline)
   without affecting the existing generic toggle. */
.data-table-toolbar__drag-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: 8px;
  color: var(--muted);
  font-size: 14px;
  user-select: none;
  cursor: pointer;
  white-space: nowrap;
}
.data-table-toolbar__drag-toggle input[type="checkbox"] {
  margin: 0;
  cursor: pointer;
}
.data-table-toolbar__drag-toggle-label {
  line-height: 1;
}


.data-table-toolbar__pager {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--muted);
  font-size: 14px;
}

.data-table-toolbar__pager-button {
  min-width: 32px;
  min-height: 28px;
  /* v85a: pager buttons now blend with the toolbar by default —
     transparent background and border, blue icon. Hover reveals a
     soft blue tint + matching border so the button still reads as
     interactive without a permanent outline framing every icon.
     Matches the new action-button treatment below; both were sitting
     framed in white-on-white against the toolbar, which drew the
     eye away from the actual content. */
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: #1c56a4;
  font: inherit;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}

.data-table-toolbar__pager-button:hover:not([disabled]) {
  background: #eaf2fb;
  border-color: #b9cfe6;
}

.data-table-toolbar__pager-button[disabled] {
  opacity: 0.45;
  cursor: default;
}

.data-table-toolbar__pager-status {
  white-space: nowrap;
}

.data-table-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
}

.data-table-shell--with-text .data-table-scroll {
  flex: 1 1 auto;
  min-height: 220px;
  overflow: auto;
}

/* ============================================================
   Split row between grid and inspector. When the inspector is
   closed the scroll takes the full width as before; when open
   the grid gets a min-width so its columns don't collapse and
   the inspector sits on the right at a fixed width.
   ============================================================ */
.data-table-split {
  display: flex;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}
.data-table-split > .data-table-scroll {
  flex: 1 1 auto;
  min-width: 0;
}

/* v85z: shell restructured — inspector is now a SIBLING of the
   whole content column (which includes grid + connected tabs +
   text panel), not a sibling of just the grid. This lets the
   inspector extend the full shell-minus-toolbar height, matching
   the React Bicycle layout where the inspector is a right-side
   column alongside every grid affordance below the toolbar. */
.data-table-shell__row {
  display: flex;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}
.data-table-shell__main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.data-table-shell__row--with-inspector > data-row-inspector {
  flex: 0 0 var(--inspector-width, 320px);
  display: flex;
  min-height: 0;
  border-left: 1px solid #cecece;
  background: #fff;
  overflow: hidden;
}
.data-table-shell__inspector-resizer {
  /* v85y1: match the other draggable splitters in the app
     (.report-sidebar__splitter--vertical, .data-table__text-
     splitter) — 8px-wide gradient band with a centered dotted
     grip, hairline borders, col-resize cursor. Consistent visual
     weight so users recognise it as the same control pattern. */
  position: relative;
  flex: 0 0 8px;
  cursor: col-resize;
  background: linear-gradient(to right, #f4f4f4, #ededed);
  border-left: 1px solid var(--line);
  border-right: 1px solid var(--line);
  transition: background 0.15s;
}
.data-table-shell__inspector-resizer::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 4px;
  height: 34px;
  background: repeating-linear-gradient(0deg, #9db0c4 0 3px, transparent 3px 5px);
  opacity: 0.9;
  pointer-events: none;
}
.data-table-shell__inspector-resizer:hover,
.data-table-shell__inspector-resizer:focus-visible {
  background: linear-gradient(to right, #e8eff7, #dde6f2);
  outline: none;
}

/* Inspector panel — right-side record-details column. */
.inspector {
  display: flex;
  flex-direction: column;
  width: 100%;
  min-height: 0;
  font-size: 13px;
  color: var(--ink, #1a2737);
}
.inspector__header {
  flex: 0 0 auto;
  border-bottom: 1px solid #dedede;
  background: #f2f2f2;
}
.inspector__tabs {
  display: flex;
  gap: 0;
}
.inspector__tab {
  flex: 1 1 auto;
  padding: 10px 12px;
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  font: inherit;
  color: var(--muted, #6b7685);
  cursor: pointer;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 160px;
}
.inspector__tab:hover { color: var(--ink, #1a2737); }
.inspector__tab--active {
  color: var(--brand-dark, #18324d);
  font-weight: 600;
  border-bottom-color: var(--accent, #2d7ff9);
  background: #fff;
}
.inspector__body {
  flex: 1 1 auto;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 0 0 12px 0;
}

.inspector__group {
  border-bottom: 1px solid #ebebeb;
}
.inspector__group-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  padding: 8px 12px;
  /* v86b: grey group-header band matching the old Bicycle app
     (was #fff4e8 peach). The old app used a neutral blue-grey
     that reads more like a section divider than a highlight. */
  background: #dde5ed;
  border: 0;
  border-bottom: 1px solid #c7d0db;
  font: inherit;
  font-weight: 600;
  color: var(--brand-dark, #18324d);
  cursor: pointer;
}
.inspector__group-header:hover { background: #cfd9e4; }
.inspector__group-chevron {
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
}
.inspector__group-label {
  flex: 1 1 auto;
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.inspector__field {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 6px 12px;
  border-bottom: 1px solid #f3f5f8;
}
.inspector__field:last-child { border-bottom: 0; }
.inspector__field-label {
  /* v85z: label width is a CSS var so an inner vertical drag handle
     can live-update the column split. Default 45% matches the
     previous fixed allocation. */
  flex: 0 0 var(--inspector-label-pct, 45%);
  display: flex;
  align-items: center;
  gap: 4px;
  color: var(--muted, #6b7685);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.inspector__field-value {
  flex: 1 1 auto;
  color: var(--ink, #1a2737);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-align: right;
}
.inspector__field--text .inspector__field-value,
.inspector__field-value--text {
  white-space: normal;
  word-break: break-word;
  text-align: left;
  max-height: 140px;
  overflow-y: auto;
}

.inspector__quality {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  line-height: 1;
  flex: 0 0 auto;
}
.inspector__quality--mandatory { color: var(--danger, #d43a2f); }
.inspector__quality--desirable { color: #e6910b; }

.inspector__empty,
.inspector__qa-placeholder {
  padding: 14px 14px;
  color: var(--muted, #6b7685);
  font-size: 13px;
  line-height: 1.5;
}
.inspector__qa-placeholder p { margin: 0 0 8px 0; }

/* v86b: inner label/value split resizer is a thin vertical line,
   matching the old Bicycle app — NOT a full 8px splitter band.
   The user wants the inner split to read as a quiet column
   divider rather than the heavy chrome we use for the outer
   (inspector-width) splitter. The drag target is still 8px wide
   for usability, but only a 1px visible line is painted; hover
   thickens the visible line to signal interactivity. */
.inspector__body {
  position: relative;
}
.inspector__field-resizer {
  position: absolute;
  top: 0;
  bottom: 0;
  /* The label has `padding: 6px 12px` + gap:8px — the split sits
     at `12px + (label-col-width = label-pct of body-width - 24px
     padding) + gap/2`. */
  left: calc(12px + (100% - 24px) * var(--inspector-label-pct-num, 0.45) + 4px);
  width: 8px;
  margin-left: -4px;
  cursor: col-resize;
  z-index: 2;
  background: transparent;
}
.inspector__field-resizer::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 1px;
  margin-left: -0.5px;
  background: var(--line, #d8d8d8);
  transition: background 0.15s, width 0.15s, margin-left 0.15s;
}
.inspector__field-resizer:hover::before,
.inspector__field-resizer:focus-visible::before,
.inspector__field-resizer--dragging::before {
  background: var(--accent, #2d7ff9);
  width: 2px;
  margin-left: -1px;
}
.inspector__field-resizer:focus-visible {
  outline: none;
}

/* v85z: QA tab — per-field percent-filled list. Each row shows
   the field label on the left and the fill percentage on the
   right, colour-graded so the user scans red=empty / amber=low /
   green=full at a glance. Structurally identical to the Fields
   tab so the two feel like the same inspector in different modes. */
.inspector__qa-percent {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.inspector__qa-percent--low    { color: var(--danger, #d43a2f); }
.inspector__qa-percent--mid    { color: #c58e00; }
.inspector__qa-percent--high   { color: #2a9060; }
.inspector__qa-loading {
  padding: 14px;
  color: var(--muted, #6b7685);
  font-size: 13px;
}
.inspector__qa-error {
  padding: 14px;
  color: var(--danger, #d43a2f);
  font-size: 13px;
}

/* Toggle button pressed state (inspector open, meta-toggle expanded,
   etc.). v85a: added a border so pressed state is unmistakable on
   the now-transparent toolbar — otherwise the soft-blue background
   could blend with the hover state and users couldn't tell whether
   a toggle was "currently active" or just being hovered. */
.data-table-toolbar__action-button--pressed {
  background: var(--accent-soft, #e6f0ff);
  color: var(--accent, #2d7ff9);
  border-color: #b9cfe6;
}

.data-table {
  width: max-content;
  min-width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  background: #ffffff;
}

.data-table__group-cell,
.data-table__column-cell,
.data-table__body-cell {
  border-right: 1px solid #d0d0d0;
  border-bottom: 1px solid #d0d0d0;
  padding: 6px 10px;
  white-space: nowrap;
  max-width: 300px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.data-table__group-cell:first-child,
.data-table__column-cell:first-child,
.data-table__body-cell:first-child {
  border-left: 1px solid #d0d0d0;
}

.data-table__group-cell {
  position: sticky;
  top: 0;
  z-index: 3;
  text-align: center;
  font-size: 12px;
  font-weight: 700;
  height: 28px;
  /* Override the shared max-width:300px from the sibling selector above —
     a group cell spans its colSpan and must size to that, not to 300px. */
  max-width: none;
  /* The inner label is position:sticky to the left, so we must allow it to
     escape the cell's visual bounds — `overflow:hidden` from the shared
     rule would clip the sticky span when it scrolls past the normal
     center point. Keep text ellipsis on the span itself instead. */
  overflow: visible;
  /* Darker borders on the group-header row so the section dividers read
     as distinct from the body grid lines — matches the Windows BI-Cycle
     app's treatment where group headers carry a visibly heavier border
     than body cells. */
  border-right-color: #7a8b9e;
  border-bottom-color: #7a8b9e;
}

.data-table__group-cell:first-child {
  border-left-color: #7a8b9e;
}

/* Column headers get the darker bottom border so the transition from
   header row to data rows also reads crisply. Vertical dividers between
   headers also darken so sections visually line up with the group row
   above — matches the Windows BI-Cycle app. Body cells still use the
   lighter `#d0d0d0` so column-to-column separation in the data stays
   subtle. */
.data-table__column-cell {
  border-right-color: #7a8b9e;
  border-bottom-color: #7a8b9e;
}

.data-table__column-cell:first-child {
  border-left-color: #7a8b9e;
}

/* Sticky-left group label.
   A <th colspan="19"> with centered text shows its label at roughly column
   10; when the viewport doesn't include that centred position, the label
   vanishes. Wrapping the label in a sticky-positioned span pins it to the
   left edge of whatever part of the cell is visible, so the group name is
   readable at any scroll position.

   position: sticky on the inner span tracks the nearest scrolling
   ancestor — the horizontal scroll wrapper — so as the user scrolls
   right, the label slides right with the cell up to its right edge,
   then stops at the cell boundary. */
.data-table__group-label {
  display: inline-block;
  position: sticky;
  left: 10px;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: middle;
}

.data-table__column-cell {
  position: sticky;
  top: 28px;
  z-index: 4;
  font-size: 12px;
  font-weight: 700;
  height: 28px;
  /* Solid background — without this, sticky cells are transparent and
     the 1px gap between the group-row's darker bottom border and the
     column-row's top reads as a faint light line (the scroll
     container's white bleeding through). A uniform background ensures
     the border transition is crisp — only the one dark line visible. */
  background: #ffffff;
}

/* Column resize handle — thin bar on the right edge of resizable
   headers. Only rendered on non-fixed, non-KPI, non-subtable columns
   (see the render() logic in data-table.js). The bar spans the full
   header height so it's easy to grab; hover + dragging states give
   visual feedback. z-index sits above the cell's content but below
   header menus (which are position: fixed and escape the stacking
   context entirely). */
.data-table__col-resize {
  position: absolute;
  top: 0;
  right: -3px;     /* straddle the border so the hit-target covers
                      both sides of the visual seam */
  width: 6px;
  height: 100%;
  cursor: col-resize;
  z-index: 5;
  /* Transparent by default — only light up on hover/drag. Keeping the
     handle invisible in the resting state matches Excel/Google Sheets
     and the Windows BI-Cycle app: users find it by hovering the
     column edge, not by seeing a vertical line on every header. */
  background: transparent;
  user-select: none;
  touch-action: none;
}
.data-table__col-resize:hover,
.data-table__col-resize--dragging {
  background: rgba(26, 74, 135, 0.3);
}

/* Vertical guide line — follows the pointer during a column-resize
   drag so the user can see exactly where the new right edge will
   land. Parented to the .data-table-scroll (tight clip box around
   just the grid) with vertical extent set by JS to the actual
   <table>'s offsetTop/offsetHeight — so the guide stops at the
   last rendered row instead of extending into empty scroll space
   below. z-index sits above sticky headers (z:4) but below floating
   menus so popups still render on top. Thin 2px solid line with a
   soft brand tint to read as a guide without competing with content. */
.data-table__col-resize-guide {
  position: absolute;
  width: 2px;
  background: rgba(26, 74, 135, 0.8);
  pointer-events: none;
  z-index: 10;
}

/* Body-wide cursor override while dragging. Without this, the cursor
   reverts to default the moment the user drags past the narrow handle
   strip, which reads as "drag ended" even though pointermove is still
   active on window. */
body.data-table-col-resizing,
body.data-table-col-resizing * {
  cursor: col-resize !important;
  user-select: none !important;
}

.data-table__body-cell {
  font-size: 12px;
  color: #18324d;
  background: #ffffff;
  height: 30px;
  /* 26.1.1.40: explicit text-selectability so users can highlight
     cell content for copy/paste. The row's draggable="true" is
     toggled off on mousedown (see data-table.js) so the browser
     performs a native text-selection drag instead of a row DnD. */
  user-select: text;
  -webkit-user-select: text;
}

/* Column-based zebra — alternating background on scrollable columns only.
   Fixed columns and the utility column stay white/uniform. */
.data-table__row > .data-table__body-cell--scrollable:nth-child(even) {
  background: #f5f5f5;
}
.data-table__row > .data-table__body-cell--scrollable:nth-child(odd) {
  background: #ffffff;
}

.data-table__row {
  cursor: pointer;
}


.data-table__column-cell--center,
.data-table__body-cell--center { text-align: center; }
.data-table__column-cell--right,
.data-table__body-cell--right { text-align: right; }

.data-table__empty-row {
  padding: 20px;
  text-align: center;
  color: var(--muted);
  background: #ffffff;
}

.data-table__connected-tabs {
  display: flex;
  align-items: stretch;
  gap: 0;
  padding: 4px 10px 0;
  border-bottom: 1px solid var(--line);
  /* v83n: unified chrome-strip grey — see .subheading-tabs. Active tab
     stays defined by its white background + var(--line) border even
     against this near-white shelf. */
  background: #f1f1f1;
  flex: 0 0 auto;
  min-height: 34px;
}

.data-table__connected-tabs-list {
  display: flex;
  align-items: stretch;
  gap: 2px;
  min-width: 0;
  flex: 1 1 auto;
  overflow: hidden;
  flex-wrap: nowrap;
}

.data-table__connected-tab--hidden { display: none !important; }

/* Group trigger: same tab chrome + chevron */
.data-table__connected-tab--group {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
}
.data-table__connected-tab-label { white-space: nowrap; }
.data-table__connected-tab-chevron {
  display: inline-flex;
  align-items: center;
  color: currentColor;
  opacity: 0.7;
}
.data-table__connected-tab-count {
  color: inherit;
  opacity: 0.75;
  font-size: 12.5px;
  font-weight: 400;
  margin-left: 2px;
}

/* Overflow (⋯) tab */
.data-table__connected-tab--overflow {
  font-size: 18px;
  line-height: 1;
  padding: 0 12px;
  letter-spacing: 0.05em;
  color: var(--muted);
}

/* Base tab: raised grey chip matching the subheading-tabs on top.
   v85c: previously these were flat text on the grey shelf with
   transparent borders — reading as a horizontal menu rather than a
   tab strip. Now every tab is a defined chip (grey bg, darker
   border) so the tab bar visually parallels the subheading tab row
   above the table. Active tab lifts to white "card" covering the
   strip's bottom border, same trick as subheading-tab--active. */
.data-table__connected-tab {
  display: inline-flex;
  align-items: center;
  /* v83g: compact sizing — was min-height:30 + 12px horizontal padding
     + font-size:13.5, which was wider per-tab than bicycle and made
     the tab strip overflow to the ⋯ menu sooner. Matches the older
     app's density so more tabs fit on the same row. */
  min-height: 26px;
  padding: 5px 10px 4px;
  /* Inactive chip chrome mirrors .subheading-tab. */
  background: #dfdfdf;
  border: 1px solid #bdbdbd;
  border-bottom: 1px solid var(--line);
  border-radius: 4px 4px 0 0;
  color: var(--muted);
  font: inherit;
  font-size: 12.5px;
  cursor: pointer;
  white-space: nowrap;
  flex: 0 0 auto;
  margin-bottom: -1px;         /* cover the strip's bottom border when active */
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.data-table__connected-tab:hover:not([disabled]):not(.data-table__connected-tab--active) {
  background: #ececec;
  color: #18324d;
}

/* Floating menu shared by group dropdowns and the overflow ⋯ button */
.data-table__connected-menu {
  position: fixed;
  z-index: 1800;
  min-width: 220px;
  max-width: 340px;
  max-height: 60vh;
  overflow: auto;
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 12px 28px rgba(16, 36, 64, 0.20);
  padding: 4px;
}

.data-table__connected-menu-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 6px 10px;
  border: 0;
  background: transparent;
  color: #18324d;
  font: inherit;
  font-size: 13.5px;
  text-align: left;
  cursor: pointer;
  border-radius: 4px;
}
.data-table__connected-menu-item:hover { background: var(--soft-bg); }
.data-table__connected-menu-item--active {
  background: rgba(45, 116, 196, 0.12);
  color: #18324d;
  font-weight: 600;
}
.data-table__connected-menu-item--disabled {
  color: #8aa1bb;
  cursor: default;
}
.data-table__connected-menu-item--disabled:hover { background: transparent; }

.data-table__connected-menu-item-label {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.data-table__connected-menu-item-count {
  flex: 0 0 auto;
  color: var(--muted);
  font-size: 12.5px;
}

.data-table__connected-tab[disabled] {
  color: #8aa1bb;
  cursor: default;
}
.data-table__connected-tab[disabled]:hover {
  background: transparent;
  color: #8aa1bb;
}

.data-table__connected-tab--active {
  /* v85c: matches .subheading-tab--active — white card with a
     subtle inset shadow on the top+sides so it reads as a sunken/
     selected chip lifting off the grey shelf. Bottom border is
     hidden (transparent) and the tab overlaps the strip's bottom
     edge via margin-bottom:-1 so the chip blends into the white
     content area below. */
  background: #ffffff;
  border-color: var(--line);
  border-bottom-color: transparent;
  color: var(--brand-dark);
  font-weight: 700;
  z-index: 1;
  box-shadow:
    inset 1px 1px 0 rgba(26, 74, 135, 0.18),
    inset -1px 0 0 rgba(26, 74, 135, 0.10);
}
.data-table__connected-tab--active:hover {
  background: #ffffff;
}

.data-table__connected-collapse {
  /* v83z: match the FILTER panel collapse chevron style (v83s) —
     transparent button, soft-tint hover, no prominent border. The prior
     heavy blue outline looked like a primary action, but this is just
     a toggle and shouldn't compete with Apply / New-record buttons. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  margin-left: auto;
  border: 0;
  padding: 0;
  background: transparent;
  color: #3c6fa5;
  cursor: pointer;
  border-radius: 4px;
  flex: 0 0 auto;
  transition: background-color 120ms ease;
}

.data-table__connected-collapse:hover {
  background: rgba(42, 109, 179, 0.16);
}

.data-table__connected-collapse:focus-visible {
  outline: 2px solid #2a6db3;
  outline-offset: -2px;
}

.data-table__connected-collapse-icon {
  width: 14px;
  height: 14px;
  display: block;
  transition: transform .18s ease;
}

.data-table-shell--text-collapsed .data-table__connected-collapse-icon {
  transform: rotate(180deg);
}

.data-table-empty {
  padding: 18px;
  border: 1px dashed var(--line);
  color: var(--muted);
}

.data-table-toolbar__search-button,
.data-table-toolbar__clear {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* 26.1.0.75: was 14×14. Standardised to 16×16 to match the
   data-table corner buttons + the toolbar action icons that
   live in the same cluster. Same change applied to the
   parallel .kpi-chart__icon-svg and .content-toolbar__icon-svg
   rules elsewhere in this file. */
.data-table-toolbar__icon-svg {
  width: 16px;
  height: 16px;
  display: block;
}


.data-table__corner-cell,


.data-table__corner-cell {
  position: sticky;
  left: 0;
  top: 0;
  z-index: 6;
  padding: 0;
}





.data-table__corner-button {
  width: 100%;
  height: 100%;
  min-height: 56px;
  border: none;
  background: transparent;
  color: #5b6f86;
  cursor: pointer;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  position: relative;
}
.data-table__corner-button:hover {
  background: #ececec;
  color: #3f78ad;
}

/* v956: KPI grids (displayType 1/2) render a second settings button
   INLINE inside the first column header, because the full corner
   overlay is hidden in KPI mode (see v85u at line ~4757). The
   inline variant shares the base .data-table__corner-button class
   so hover/color tokens apply, but needs its own compact sizing
   rules — the base rule's `width: 100%; height: 100%; min-height:
   56px` would blow it out to fill the whole header cell.
   Positioned absolute at the top-left of the header, sized to fit
   next to the v956 ";" → "-" label. The header cell gets
   position:relative via .data-table__column-cell--kpi-corner so
   this button positions against it rather than the nearest
   positioned ancestor above.
   v957: button MOVED from the column-row first cell (where v956
   put it) UP into the group-row first cell — user asked to match
   the Windows reference which puts the button in the same cell
   as the "Description" group label AND drops the label text. So
   .data-table__group-cell--kpi-corner now also gets relative +
   the button is centered in its empty cell. */
.data-table__column-cell--kpi-corner,
.data-table__group-cell--kpi-corner {
  position: relative;
}
/* v959: flex container wrapping the settings + (optional) sort
   button inside the KPI group cell. Absolutely-centered in the
   cell via transform — v957 centred a single button directly, but
   with two buttons we need a container so the pair centres as a
   unit rather than stacking individually. gap:4 matches the
   dataview corner-overlay's internal spacing between its two
   buttons. */
.data-table__group-cell--kpi-corner .data-table__kpi-corner-buttons {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  gap: 4px;
}
.data-table__kpi-column-menu-btn {
  /* v957/v959: compact icon button used inside the KPI corner cell.
     v957 made it absolute-positioned at the top-left of the cell.
     v959 moves it INTO a flex container (see rule above) so multiple
     buttons can sit side-by-side — position switched to static.
     Width/height stay at 22×22 matching v957's footprint. */
  position: static;
  width: 22px;
  height: 22px;
  min-height: 0;
  padding: 2px;
  border-radius: 3px;
  /* Reset the base .data-table__corner-button flex-column +
     gap:3 — this is an icon-only button, single child. */
  gap: 0;
}
.data-table__kpi-column-menu-btn > svg {
  width: 14px;
  height: 14px;
}
.data-table__kpi-column-menu-btn .data-table__corner-badge {
  /* Downsize the filter badge for the smaller button. Base rule
     is min-width:16 height:16 — too large for a 22px button. */
  min-width: 12px;
  height: 12px;
  font-size: 9px;
  line-height: 12px;
  top: -2px;
  right: -2px;
  padding: 0 3px;
}
/* v956: leave room on the left of the label so it doesn't collide
   with the inline menu button (24px button + ~4px gap). Only
   applies to the KPI first-column header — other column headers'
   labels keep their natural flow.
   v957: button moved to group-row cell; column-row cell no longer
   has a sibling button to dodge — padding reset back to default.
   Rule kept (empty) as a sentinel in case the layout swings back. */
.data-table__column-cell--kpi-corner > .data-table__column-label {
  /* padding-left intentionally unset — see v957 note above. */
}

/* Filter-count badge — pinned to the top-right of the corner button.
   Scoped to .data-table__corner-badge so the generic corner-button span
   rule (legacy, for the old decorative hamburger lines) doesn't apply.
   26.1.1.18: colour switched from the lighter #4ba3e3 to #1a4a87 so
   it matches the status-bar filter badge ("windows task bar" badge in
   the user's wording) — three filter-count indicators across the page
   now read as the same semantic colour. The sort-button badge takes
   the lighter blue (#4ba3e3) — see the sort-specific override below.
   Layout also widened to fit a small filter-funnel SVG before the
   count digits, so the badge reads as "filter: N" at a glance. */
.data-table__corner-button .data-table__corner-badge {
  position: absolute;
  top: 4px;
  right: 4px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px 0 2px;
  border-radius: 8px;
  background: #1a4a87;
  color: #ffffff;
  font-size: 10px;
  font-weight: 600;
  line-height: 16px;
  text-align: center;
  display: inline-flex;
  align-items: center;
  gap: 2px;
}

/* The filter-funnel SVG that sits at the left of the count inside
   the corner-filter badge. Sized small enough to live in a 16px
   pill without crowding the digit. White so it tracks the badge
   foreground colour against the dark blue background. */
.data-table__corner-badge-icon {
  width: 9px;
  height: 9px;
  flex: 0 0 auto;
  display: block;
}

/* ══════════════════════════════════════════════════════════════════════════
   Column Visibility dialog
   Reuses the sidebar's report-sidebar__tree-item / filter-check markup,
   so the checkbox iconography, expand chevrons, and colours stay
   consistent with the sidebar filter tree. This stylesheet only adds
   the dialog's own chrome (backdrop, panel, header/footer, button) and
   a row padding tweak to compensate for the denser dialog context.
   ════════════════════════════════════════════════════════════════════════ */

column-visibility-dialog {
  display: contents;
}

.column-visibility-dialog__backdrop {
  position: fixed;
  inset: 0;
  background: rgba(12, 22, 40, 0.45);
  z-index: 1300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.column-visibility-dialog__panel {
  background: #ffffff;
  border-radius: 6px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.25);
  width: 420px;
  max-width: 100%;
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.column-visibility-dialog__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 12px 16px;
  border-bottom: 1px solid #e2e2e2;
  background: #f4f4f4;
}

.column-visibility-dialog__title {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  color: #1d2d44;
}

.column-visibility-dialog__close {
  width: 28px;
  height: 28px;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: #4a5a70;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}
.column-visibility-dialog__close:hover { background: #ececec; }

.column-visibility-dialog__body {
  flex: 1 1 auto;
  overflow: auto;
  padding: 6px 10px 10px;
}

/* Row tweaks: tighter vertical rhythm than the sidebar so a long list
   fits without scrolling, and a hover highlight because this dialog
   is a direct interaction surface. */
.column-visibility-dialog__row {
  cursor: default;
  padding-top: 2px;
  padding-bottom: 2px;
  border-radius: 3px;
}
.column-visibility-dialog__row:hover {
  background: #ececec;
}

.column-visibility-dialog__footer {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 16px;
  border-top: 1px solid #e2e2e2;
  background: #f4f4f4;
}

.column-visibility-dialog__btn {
  padding: 6px 14px;
  border: 1px solid #bdbdbd;
  border-radius: 4px;
  background: #ffffff;
  color: #1d2d44;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
}
.column-visibility-dialog__btn:hover {
  background: #ececec;
}

.column-visibility-dialog__btn--primary {
  background: #3f78ad;
  color: #ffffff;
  border-color: #3f78ad;
}
.column-visibility-dialog__btn--primary:hover {
  background: #2d5f94;
}

.data-table__column-cell--sortable {
  cursor: pointer;
}

.data-table__column-label {
  display: inline-block;
}

.data-table__sort-indicator {
  /* Column-header sort icon: ascending/descending arrow inline with
     an optional small blue corner-overlap badge showing the position
     in multi-column sort. Mirrors React BadgeIcon in DataColumn.js. */
  margin-left: 6px;
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: #1a4a87;
  vertical-align: middle;
}
.data-table__sort-indicator-svg {
  width: 16px;
  height: 16px;
  display: block;
}
.data-table__sort-indicator-badge {
  position: absolute;
  top: -4px;
  right: -7px;
  min-width: 14px;
  height: 14px;
  padding: 0 3px;
  border-radius: 7px;
  /* 26.1.1.19: lighter blue (#4ba3e3) to match the corner sort
     badge — both badges now read as the same colour for sort, and
     differ from the dark-blue filter badge. Was #1a4a87. */
  background: #4ba3e3;
  color: #fff;
  font-size: 9px;
  line-height: 14px;
  font-weight: 700;
  text-align: center;
  border: 1px solid #fff;
  box-sizing: border-box;
}

.data-table__column-menu {
  z-index: 2000;
  min-width: 184px;
  background: #ffffff;
  border: 1px solid #bfc9d4;
  box-shadow: 0 8px 18px rgba(0,0,0,0.15);
}

.data-table__column-menu-item {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 34px;
  padding: 0 12px;
  border: none;
  background: #ffffff;
  text-align: left;
  font: inherit;
  color: #1d2d44;
  cursor: pointer;
}

.data-table__column-menu-item:hover:not([disabled]) {
  background: #f4f7fb;
}

.data-table__column-menu-item[disabled] {
  color: #70859c;
  cursor: default;
}

/* Horizontal divider between the main filter/sort cluster and the
   admin-gated cluster (Edit Column Properties, Batch Edit). Rendered
   only when at least one admin item is visible, so non-admin users
   don't see an orphan line. */
.data-table__column-menu-sep {
  height: 1px;
  background: #e3e8ee;
  margin: 4px 0;
}

.data-table__column-menu-icon {
  width: 18px;
  text-align: center;
  /* Inherit from the parent button's color (which is the normal enabled
     text color #1d2d44). Previously this was hardcoded to #6a7d92 so
     every icon looked greyed-out regardless of whether the item was
     actually enabled — now disabled items still dim via the
     [disabled] rule, but enabled items show a crisp dark-blue icon. */
  color: inherit;
  flex: 0 0 18px;
}


.data-table-toolbar__icon-svg--pager {
  width: 14px;
  height: 14px;
  display: block;
}


.data-table__menu-icon-svg {
  /* 26.1.0.73: was 18×18. Standardised to 16×16 per user request
     so the corner buttons (hamburger settings + sort dots) align
     visually with the toolbar icon cluster on the right (info,
     export, inspector toggle, drill-in — all 16×16) and with the
     row-form icon (also 16×16 since .70). At 16px the SVG paths'
     viewBox math (16-unit viewBox → 1:1 pixel mapping) renders
     sharp at 1× DPI, same reasoning as the .70 form-icon bump. */
  width: 16px;
  height: 16px;
  display: block;
}

/* 26.1.0.70: row-form icon size bumped from 14×14 (the .69 value)
   to 16×16. The 14px size was an attempt to make the icon "thinner"
   per user feedback that the icon read as too chunky, but at that
   render size the 512-unit viewBox maps non-integer-cleanly onto
   pixels (32 viewBox units → 0.875 CSS pixels per stroke-equivalent),
   which produces visible blur on the path edges. The DevTools
   tooltip on the old (Windows/UI5) app's same icon shows "16x16",
   which is the size the UI5 icon collection is authored for —
   32 viewBox units = exactly 1 CSS pixel = sharp at 1× DPI. The
   path data is byte-for-byte identical between our SVG and the
   old app's; the difference in apparent thickness/blur is purely
   the render-size mismatch. Going to 16 puts our render at exact
   parity with the reference. The corner-button icons keep 18px
   because they're standalone toolbar glyphs sized for that
   container, not row-action chips next to text. */
.data-table__row-form .data-table__menu-icon-svg {
  width: 16px;
  height: 16px;
}

.data-table__column-menu-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
}












/* 26.1.0.68: was background:#f1f1f1 (matched the column-header
   row's panel-grey chrome). Flipped to white per user request so
   the entire leftmost column reads as one white surface — the
   utility body cells were already white (.data-table__utility-cell
   below), and the user wants the two header cells (the hamburger
   menu corner + the pencil corner above the form-button column)
   to match that, instead of looking like part of the grey column-
   header band. The column-header text columns to the right of the
   corner keep their grey chrome — this only applies to the two
   sticky leftmost cells. Same change applied to the duplicate
   rule further down (line ~7629) and the !important variant
   (line ~9828) so the override winning by specificity is also
   white. */
.data-table__corner-cell {
  background: #ffffff;
}

.data-table__utility-cell {
  background: #ffffff;
  white-space: nowrap;
  padding: 0 6px;
  vertical-align: middle;
  width: 64px;
  min-width: 64px;
  max-width: 64px;
  border-right: 1px solid #d0d0d0;
  border-bottom: 1px solid #d0d0d0;
}

.data-table__utility-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 4px;
  width: 100%;
  min-height: 26px;
}

.data-table__row-number {
  display: inline-block;
  min-width: 22px;
  text-align: right;
  color: #111827;
  font-size: 12px;
  line-height: 1;
  flex: 0 0 auto;
}

/* v84e: row-action buttons toned down. Previously these had a solid
   #7aa0cf border + white fill + strong #2860a8 icon which read as
   primary-action chips and competed with the data cells for attention
   on every row. Now they're near-invisible in idle state (transparent
   bg, no border, muted icon), gain a soft tint on the hovered row so
   the user sees where the actions live without the buttons ever
   demanding focus, and show a full hover state only on direct pointer
   hover over the button itself. This mirrors the data-table's own
   chrome-grey palette and keeps the row data as the primary content.

   26.1.0.67: form-button always highlighted per user request. Idle
   state is now full opacity + brand-blue (#1a4a87) — same as the
   prior selected-row variant. Reasoning: the form-open button is
   the row's primary action (the user's reference is the Windows
   app where the form icon sits prominent on every row), and the
   .55-opacity-until-hover behaviour buried it. Edit button keeps
   the toned-down behaviour because it's a secondary action.
   Hover/selected variants below still apply additional emphasis
   (background tint, border) so the active row + direct hover still
   read distinct from the resting state. */
.data-table__row-form,
.data-table__row-edit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  margin-right: 0;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 3px;
  background: transparent;
  cursor: pointer;
  flex: 0 0 22px;
  transition: opacity 120ms ease, background-color 120ms ease, border-color 120ms ease, color 120ms ease;
}
/* Form button: always-highlighted (no fade). */
.data-table__row-form {
  opacity: 1;
  color: #1a4a87;
}
/* Edit button: keeps the toned-down behaviour — fades in idle, lifts
   on row-hover and selected-row. Pencil is a secondary action and
   doesn't need permanent emphasis. */
.data-table__row-edit {
  opacity: 0.55;
  color: #6a8bb3;
}

/* When the parent row is hovered, pull the buttons up to full opacity
   so the user sees them where their cursor is. Still subtle — no
   background or border change at the row level. */
.data-table__row:hover .data-table__row-form,
.data-table__row:hover .data-table__row-edit {
  opacity: 1;
  color: #3c6fa5;
}

/* v84n: selected-row variant. On the currently active record, the
   action buttons stay at full opacity with a firmer brand-blue color
   so the user clearly reads them as live controls rather than the
   muted idle state that other rows carry. Mirrors the :hover state
   but persistent — no mouse required, because the selected row is
   already the one the user just interacted with and any follow-up
   action (open-form, edit) naturally targets it. */
.data-table__row--selected .data-table__row-form,
.data-table__row--selected .data-table__row-edit {
  opacity: 1;
  color: #1a4a87;
  /* Subtle blue-tinted background so the buttons read as a paired
     control-cluster on the selected row rather than floating icons.
     Lighter than the direct-hover tint below so direct hover still
     visually bumps up. */
  background: rgba(42, 109, 179, 0.08);
  border-color: rgba(42, 109, 179, 0.22);
}

/* Direct hover on the button itself: soft blue-tint background +
   slightly firmer icon color. No visible border — the bg tint is
   enough to signal "clickable" without the old hard outline. */
.data-table__row-form:hover,
.data-table__row-edit:hover {
  background: rgba(42, 109, 179, 0.12);
  color: #2860a8;
  border-color: transparent;
}

.data-table__row-form:focus-visible,
.data-table__row-edit:focus-visible {
  outline: 2px solid #2a6db3;
  outline-offset: -2px;
}

/* 26.1.0.68: see the matching note at the earlier duplicate of
   this rule (~line 7514). Both rules are now white. */
.data-table__corner-cell {
  background: #ffffff;
}


.text-tab-content {
  padding: 8px 12px;
  font-size: 13px;
}

.text-row {
  display: flex;
  gap: 12px;
  padding: 4px 0;
  border-bottom: 1px solid #e5e7eb;
}

.text-label {
  width: 140px;
  font-weight: 600;
  color: #374151;
}

.text-value {
  flex: 1;
  color: #111827;
}


.data-table__tab-panel {
  /* No top border — the tab strip above already has its own
     border-bottom, and the active tab's border-bottom-color:#fff covers
     it so the tab visually merges with the panel below. Adding a top
     border here would draw a line right through that merge. */
  background: #ffffff;
  padding: 8px 12px;
}


.data-table__text-splitter {
  position: relative;
  height: 8px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  background: linear-gradient(to bottom, #f4f4f4, #ededed);
  cursor: row-resize;
  flex: 0 0 auto;
}

.data-table__text-splitter:focus-visible {
  outline: 2px solid #2d74c4;
  outline-offset: -2px;
}

.data-table__text-splitter-grip {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 34px;
  height: 4px;
  background: repeating-linear-gradient(90deg, #9db0c4 0 3px, transparent 3px 5px);
  opacity: 0.9;
}

.data-table__tab-panel--text {
  font-size: 13px;
  height: var(--text-panel-height, 260px);
  overflow: auto;
  padding: 8px 14px 10px;
  flex: 0 0 auto;
}

.data-table-shell--text-collapsed .data-table__tab-panel--text {
  height: 0 !important;
  min-height: 0;
  padding: 0 14px;
  overflow: hidden;
  border-top: 0;
}

.data-table__text-panel-inner {
  max-width: 1650px;
}

.data-table__text-row {
  display: grid;
  grid-template-columns: 124px minmax(0, 1fr);
  gap: 4px;
  align-items: start;
  padding: 2px 0 10px;
}

.data-table__text-row + .data-table__text-row {
  border-top: 1px solid #e8edf3;
  padding-top: 10px;
}

.data-table__text-label {
  font-weight: 600;
  color: #243b53;
  position: sticky;
  top: 0;
  background: #ffffff;
}

.data-table__text-value {
  color: #111827;
  white-space: pre-wrap;
  word-break: break-word;
  line-height: 1.45;
  max-width: 115ch;
}

/* HTML-mode variant: the backend sends snippets like "...calibration.<br>Gas
   detection loops..." — so white-space: pre-wrap would double-space every
   line (line-break from the source + <br>). Switch to normal white-space so
   only the explicit tags control wrapping. Block elements inherit the usual
   margins that make multi-paragraph descriptions legible. */
.data-table__text-value--html {
  white-space: normal;
}
.data-table__text-value--html p { margin: 0 0 0.6em; }
.data-table__text-value--html p:last-child { margin-bottom: 0; }
.data-table__text-value--html ul,
.data-table__text-value--html ol { margin: 0.2em 0 0.6em 1.4em; padding: 0; }
.data-table__text-value--html a { color: #3f78ad; }
.data-table__text-value--html br { display: block; content: ""; }

.data-table__text-empty {
  color: #6b7280;
}

body.data-table--resizing-text {
  cursor: row-resize;
  user-select: none;
}













/* Selected row - stable (no layout shift).
   Cells flagged with data-has-bg carry a backend-assigned colour (status
   codes like "Level 2" amber, "Closed" green). Those must remain visible
   on the selected row or the colour coding disappears whenever a row is
   focused — we keep only the top/bottom border for the focus frame, and
   leave the cell's inline-style background alone. */
.data-table__row--selected td:not([data-has-bg]) {
  background: #e9ecef !important;
  box-shadow:
    inset 0 1px 0 #4b5563,
    inset 0 -1px 0 #4b5563;
}
.data-table__row--selected td[data-has-bg] {
  box-shadow:
    inset 0 1px 0 #4b5563,
    inset 0 -1px 0 #4b5563;
}

.data-table__row--selected .data-table__utility-cell {
  background: #ffffff !important;
  box-shadow:
    inset 1px 0 0 #4b5563,
    inset 0 1px 0 #4b5563,
    inset 0 -1px 0 #4b5563;
}

.data-table__row--selected td:last-child {
  box-shadow:
    inset 0 1px 0 #4b5563,
    inset 0 -1px 0 #4b5563,
    inset -1px 0 0 #4b5563;
}


.data-table-toolbar__right {
  display: flex;
  align-items: center;
  gap: 10px;
}

.data-table-toolbar__action-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 30px;
  padding: 0 12px;
  /* v85a: action buttons now blend with the toolbar — transparent
     background and border by default, soft blue tint + border on
     hover. Previously every icon button had a permanent blue frame
     which visually competed with the content below (the user called
     these out as "standing out" against the otherwise flat toolbar).
     The New button — a call-to-action with a text label — gets its
     framed look restored via an override lower down. */
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: #1d4f91;
  font: inherit;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}

.data-table-toolbar__action-button:hover {
  background: #eaf2fb;
  border-color: #b9cfe6;
}

.data-table-toolbar__action-button--export {
  width: 32px;
  min-width: 32px;
  padding: 0;
}

.data-table-toolbar__new-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
}

.data-table-toolbar__new-icon-image {
  display: block;
  width: 16px;
  height: 16px;
  object-fit: contain;
}

.data-table-toolbar__export-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.data-table-toolbar__new-icon-box {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 3px;
  border-radius: 2px;
  background: #e88916;
  color: #fff;
  font-size: 9px;
  font-weight: 700;
  line-height: 1;
}

.data-table__body-cell--inline-editable {
  cursor: pointer;
  position: relative;
}

.data-table__row--selected .data-table__body-cell--inline-editable:hover {
  outline: 1px solid #2b6cb0;
  outline-offset: -1px;
  background: #eef6ff !important;
}

.data-table-dialog__overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(15, 23, 42, 0.28);
}

.data-table-dialog {
  width: min(460px, calc(100vw - 32px));
  background: #ffffff;
  border: 1px solid #d0d0d0;
  border-radius: 10px;
  box-shadow: 0 18px 40px rgba(15, 23, 42, 0.22);
}

.data-table-dialog__header,
.data-table-dialog__body,
.data-table-dialog__footer {
  padding: 14px 16px;
}

.data-table-dialog__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  border-bottom: 1px solid #e2e2e2;
  /* v86w: don't start a text selection when the user grabs the title
     bar for dragging. The cursor is set to grab/grabbing by the JS
     drag service (services/dialog-drag-service.js). */
  user-select: none;
  -webkit-user-select: none;
}

.data-table-dialog__title {
  margin: 0;
  flex: 1 1 auto;
  font-size: 16px;
  color: #18324d;
}

.data-table-dialog__field {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.data-table-dialog__label {
  font-size: 13px;
  font-weight: 600;
  color: #334155;
}

.data-table-dialog__input {
  width: 100%;
  height: 36px;
  padding: 0 10px;
  border: 1px solid #b8c4d3;
  border-radius: 6px;
  font: inherit;
  color: #0f172a;
}

.data-table-dialog__input:focus {
  outline: none;
  border-color: #2b6cb0;
  box-shadow: 0 0 0 3px rgba(43, 108, 176, 0.15);
}

.data-table-dialog__footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  border-top: 1px solid #e2e2e2;
}

.data-table-dialog__button {
  min-width: 60px;
  height: 28px;
  padding: 0 12px;
  border-radius: 5px;
  border: 1px solid #cbd5e1;
  background: #ffffff;
  color: #18324d;
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}

.data-table-dialog__button--primary {
  border-color: #2b6cb0;
  background: #2b6cb0;
  color: #ffffff;
}

.data-table-dialog__button--secondary:hover {
  background: #f5f5f5;
}

.data-table-dialog__button--primary:hover {
  background: #1f5c9b;
}

.data-table-dialog--lookup {
  width: min(520px, calc(100vw - 32px));
}

/* v85v: richtext inline-edit dialog — wider than the default dialog
   so multi-paragraph text has room to breathe. The editor itself
   comes with its own toolbar + 240-ish-px content area; we just
   size the wrapper to match the old Bicycle app's dialog footprint
   where these fields originate. */
.data-table-dialog--richtext {
  width: min(720px, calc(100vw - 32px));
}

.data-table-dialog__field--richtext {
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: stretch;
}

.data-table-dialog__field--richtext .rich-text-editor {
  width: 100%;
}

.data-table-dialog__field--richtext .rich-text-editor__content {
  min-height: 220px;
  max-height: 50vh;
  overflow: auto;
}

.data-table-dialog__lookup-list {
  display: flex;
  flex-direction: column;
  max-height: 320px;
  overflow: auto;
  border: 1px solid #d8d8d8;
  border-radius: 8px;
  background: #ffffff;
}

.data-table-dialog__option {
  min-height: 40px;
  padding: 0 16px;
  border: none;
  border-bottom: 1px solid #e5ebf2;
  background: #ffffff;
  text-align: left;
  font: inherit;
  color: #18324d;
  cursor: pointer;
}

.data-table-dialog__option:last-child {
  border-bottom: none;
}

.data-table-dialog__option:hover {
  background: #eef6ff;
}

.data-table-dialog__option--selected {
  background: #8db8e2;
  color: #0f172a;
}

.data-table-dialog__lookup-empty {
  padding: 16px;
  color: #64748b;
}


.account-menu__trigger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: min(260px, 48vw);
  padding: 0;
  color: #fff;
  background: transparent;
  border: 0;
  cursor: pointer;
}

.account-menu__trigger:hover .account-menu__label {
  text-decoration: underline;
}

.account-menu__trigger.is-open .account-menu__chevron {
  /* v875: inversed chevron — base state now points up to signal
     "menu pops up from here" (this trigger sits at the bottom of
     the sidebar and the popover opens above it). When the menu is
     open, chevron points down to indicate "click to collapse". */
  transform: none;
}

.account-menu__avatar {
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.9);
  color: var(--brand-dark);
  font-weight: 700;
  flex: 0 0 auto;
}

.account-menu__avatar--large {
  width: 40px;
  height: 40px;
  font-size: 16px;
  background: var(--soft-bg);
}

.account-menu__label {
  max-width: 180px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
}

.account-menu__chevron {
  font-size: 12px;
  opacity: 0.8;
  transition: transform .18s ease;
  /* v875: chevron points up by default (base SVG renders as ∨,
     rotating 180° turns it into ∧). When the menu opens, the
     is-open rule above cancels this rotation. */
  transform: rotate(180deg);
}

.account-menu__header {
  display: flex;
  align-items: center;
  gap: 12px;
}

.account-menu__identity {
  min-width: 0;
}

.account-menu__name {
  font-size: 15px;
  /* v85j: was 700 — reads too loud for the primary identity line.
     500 keeps it clearly the heading but without shouting at the
     user every time the popover opens. Role value + group list
     below were similarly dropped from <strong> 700 to 500 weight. */
  font-weight: 500;
  word-break: break-word;
}

.account-menu__meta {
  margin-top: 2px;
  color: var(--muted);
  font-size: 13px;
}

.account-menu__actions {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.account-menu__action {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 6px 10px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: #fff;
  color: #18324d;
  font: inherit;
  cursor: pointer;
  text-align: left;
}

.account-menu__action:hover {
  background: var(--soft-bg);
  border-color: var(--line);
}

.account-menu__action:disabled {
  opacity: .6;
  cursor: wait;
}

/* ── System Admin submenu (admin-only) ── */
.account-menu__admin {
  /* No extra padding — the parent `.user-popover__section + …` margin
     already gives the block its breathing room.  Used to be 6px top/
     bottom which stacked with the section margin and felt airy. */
  padding-top: 0;
  padding-bottom: 0;
}
.account-menu__admin-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 6px 10px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: #fff;
  color: #18324d;
  font: inherit;
  /* v85j: was 600 — dropped to match the Reset-to-Default / Sign
     out actions below so "System Admin" reads as just another
     menu entry rather than a header. Chevron still rotates when
     open to show disclosure state. */
  font-weight: 400;
  cursor: pointer;
  text-align: left;
}
.account-menu__admin-toggle:hover {
  background: var(--soft-bg);
  border-color: var(--line);
}
.account-menu__admin-label {
  flex: 1 1 auto;
  min-width: 0;
}
.account-menu__admin-icon {
  flex: 0 0 auto;
  color: #64748b;
  /* v85j: inline-flex so the SVG child centers cleanly inside the
     icon slot. The emoji version of this used raw text and took
     its size from the font; SVGs need an explicit container size
     to avoid collapsing to 0 width. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
}
/* Shared sizing for every SVG glyph inside the user popover — keeps
   the System Admin toggle, its six submenu items, and any future
   admin action on one consistent 16×16 icon grid. Stroke colour
   comes from the parent via currentColor. */
.account-menu__admin-svg {
  width: 16px;
  height: 16px;
  display: block;
  flex: 0 0 16px;
}
.account-menu__action-icon {
  flex: 0 0 auto;
  color: #64748b;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
}
.account-menu__admin-caret {
  flex: 0 0 auto;
  color: #64748b;
  transition: transform 0.15s ease;
  /* v85k: was 11px — too small to register as a disclosure
     affordance next to the full-size row text. 14px reads as a
     deliberate chevron the user can aim at. */
  font-size: 14px;
  line-height: 1;
}
.account-menu__admin.is-open .account-menu__admin-caret {
  transform: rotate(90deg);
}
.account-menu__admin-items {
  /* Nested items indent slightly from the toggle, using a left border so
     the visual hierarchy matches Bicycle's cascading submenu look. */
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin: 2px 0 0 14px;
  padding-left: 8px;
  border-left: 2px solid #e2e2e2;
}
.account-menu__admin-items[hidden] {
  display: none;
}
.account-menu__admin-items .account-menu__action {
  padding: 6px 10px;
  font-size: 13.5px;
}

.data-table__connected-tabs--empty {
  display: none;
}

/* ── Footer ── */
.site-footer {
  border-top: 1px solid var(--line);
  margin-top: 8px;
  padding: 24px 0 8px;
  display: grid;
  grid-template-columns: minmax(220px, 1.3fr) 2fr;
  gap: 24px;
}

.site-footer__title {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--brand-dark);
}

.site-footer__legal {
  /* v876: was `margin: 10px 0 0` to separate from the title above,
     but the title was removed. Flush-top now since there's nothing
     above it inside the brand column. */
  margin: 0;
  color: var(--muted);
  font-size: 14px;
}

.site-footer__links {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 20px;
}

.site-footer__column h3 {
  margin: 0 0 10px;
  font-size: 14px;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
}

.site-footer__column ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.site-footer__column li + li {
  margin-top: 8px;
}

.site-footer__column a {
  color: var(--brand-dark);
  text-decoration: none;
  font-weight: 500;
}

.site-footer__column a:hover {
  text-decoration: underline;
}

@media (max-width: 900px) {
  .site-footer {
    grid-template-columns: 1fr;
  }
}


.page-skeleton {
  padding: 2rem 1rem 3rem;
}

.page-card {
  max-width: 960px;
  margin: 0 auto;
  background: #fff;
  border: 1px solid #d9e2ec;
  border-radius: 16px;
  padding: 1.5rem;
  box-shadow: 0 4px 16px rgba(15, 23, 42, 0.04);
}

.page-card__eyebrow {
  display: inline-block;
  margin-bottom: 0.75rem;
  padding: 0.35rem 0.7rem;
  border-radius: 999px;
  background: #eef4fb;
  color: #1d4e89;
  font-size: 0.8rem;
  font-weight: 700;
}

.page-card__muted {
  color: #52606d;
}


/* ── Authenticated home sections ── */
.home-insight-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(280px, 1fr));
  gap: 18px;
}

.home-insight-card {
  border: 1px solid #dbdbdb;
  border-radius: 14px;
  padding: 18px 20px;
  background: #ffffff;
}

.home-insight-card h3 {
  margin: 0 0 10px;
  font-size: 18px;
  color: var(--brand-dark);
}

.home-insight-card p {
  margin: 0;
  font-size: 14px;
  line-height: 1.5;
  color: #2a4260;
}

@media (max-width: 900px) {
  .home-insight-grid {
    grid-template-columns: 1fr;
  }
}


.report-settings-bar__status--button {
  /* Outlined on light bg — matches the refactored .toggle rule. */
  border: 1px solid #cecece;
  background: #ffffff;
  color: var(--brand-dark);
  cursor: pointer;
}

.report-settings-bar__status--button .report-settings-bar__status-label,
.report-settings-bar__status--button .report-settings-bar__status-icon {
  color: var(--brand-dark);
}


.content-context-dialog {
  position: fixed;
  z-index: 2100;
  width: min(360px, calc(100vw - 24px));
  background: #ffffff;
  border: 1px solid #bfbfbf;
  box-shadow: 0 14px 32px rgba(0, 0, 0, 0.18);
  border-radius: 4px;
  overflow: hidden;
}

.content-context-dialog__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 38px;
  padding: 0 10px;
  border-bottom: 1px solid #d7e3f0;
  background: #f7fbff;
  color: #2f5f93;
  font-weight: 600;
}

.content-context-dialog__header button {
  border: 0;
  background: transparent;
  color: #2f5f93;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}

.content-context-dialog__body {
  max-height: min(420px, calc(100vh - 160px));
  overflow: auto;
  padding: 8px 0;
  background: #fff;
}

.content-context-dialog__body--tree {
  padding: 8px 10px;
}

.content-context-dialog__footer {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 10px;
  border-top: 1px solid #d7e3f0;
  background: #fbfdff;
}

.content-context-dialog__footer button {
  min-width: 72px;
  height: 32px;
  border: 1px solid #2f78ad;
  background: #fff;
  color: #2f78ad;
  border-radius: 4px;
  cursor: pointer;
}

.content-context-dialog__footer button[data-action="apply"] {
  background: #2f78ad;
  color: #fff;
}

.context-tree {
  list-style: none;
  margin: 0;
  padding: 0;
}

.context-tree .context-tree {
  padding-left: 18px;
}

.context-tree__item {
  margin: 0;
  padding: 0;
}

.context-tree__row {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;
}

.context-tree__toggle,
.context-tree__toggle-spacer {
  width: 18px;
  min-width: 18px;
  display: inline-flex;
  justify-content: center;
  align-items: center;
}

.context-tree__toggle {
  border: 0;
  background: transparent;
  color: #2f5f93;
  cursor: pointer;
  padding: 0;
}

.context-tree__select {
  border: 0;
  background: transparent;
  color: #173a63;
  padding: 3px 0;
  text-align: left;
  cursor: pointer;
}

.context-tree__select.is-selected {
  font-weight: 700;
  color: #0f4fa8;
}

.context-tree__check {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: #173a63;
  cursor: pointer;
}

.report-time-window-dialog {
  position: fixed;
  inset: 0;
  z-index: 2000;
}

.report-time-window-dialog__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.2);
}

.report-time-window-dialog__surface {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(550px, calc(100vw - 32px));
  background: #fff;
  border: 1px solid #bfbfbf;
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.2);
}

.report-time-window-dialog__header {
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  min-height: 40px;
  border-bottom: 1px solid #bfbfbf;
  color: #2f5f93;
  font-size: 26px;
}

.report-time-window-dialog__title {
  font-size: 16px;
  font-weight: 500;
}

.report-time-window-dialog__close {
  position: absolute;
  right: 10px;
  top: 6px;
  border: 0;
  background: transparent;
  color: #2f5f93;
  font-size: 24px;
  cursor: pointer;
}

.report-time-window-dialog__body {
  padding: 24px 16px 10px;
}

.report-time-window-dialog__row {
  display: grid;
  grid-template-columns: 90px 1fr 1fr;
  gap: 12px;
  align-items: center;
  margin-bottom: 20px;
}

.report-time-window-dialog__row span {
  color: #666;
}

.report-time-window-dialog__row select {
  min-width: 0;
  height: 32px;
  border: 1px solid #aebfd5;
  padding: 0 8px;
  font: inherit;
}

.report-time-window-dialog__footer {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 0 16px 16px;
}

.report-time-window-dialog__button {
  min-width: 72px;
  height: 34px;
  border: 1px solid #2f78ad;
  background: #fff;
  color: #2f78ad;
  border-radius: 4px;
  cursor: pointer;
}

.report-time-window-dialog__button--primary {
  background: #2f78ad;
  color: #fff;
}

.report-time-window-dialog__warning {
  margin-top: 8px;
  padding: 6px 10px;
  border-radius: 4px;
  background: #fff3cd;
  border: 1px solid #ffc107;
  color: #856404;
  font-size: 0.85em;
  line-height: 1.4;
}


.data-table-dialog--content-info {
  width: min(560px, calc(100vw - 48px));
}

.data-table-dialog__close {
  border: 0;
  background: transparent;
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  color: #1f4b8f;
}

.data-table-dialog__body--info {
  padding: 0;
}

.data-table-dialog__info-row {
  display: grid;
  grid-template-columns: 180px 1fr;
  border-top: 1px solid #d6e0ec;
}

.data-table-dialog__info-row:first-child {
  border-top: 0;
}

.data-table-dialog__info-label,
.data-table-dialog__info-value {
  padding: 10px 14px;
}

.data-table-dialog__info-label {
  background: #eff5fb;
  color: #244a7c;
  font-weight: 600;
}


.data-table-dialog__info-label,
.data-table-dialog__info-value {
  display: flex;
  align-items: center;
  gap: 8px;
}

.data-table-dialog__info-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
  color: #2d5f9e;
}

.data-table-dialog__info-icon-image,
.data-table-dialog__info-icon-svg {
  width: 16px;
  height: 16px;
  display: block;
}


.data-table-dialog__info-value {
  background: #ffffff;
  color: #24364d;
  word-break: break-word;
}

/* Multi-line content-info values from contentInformation API (DT/FLT
   types). Each selected item gets its own line of the form:
     <leaf>  (<parent1 - parent2>)
   with the lineage portion in a muted/italic style — mirrors React's
   PrevLevelsSpan in ContentInfoFilterItem. */
.data-table-dialog__info-value-line {
  display: block;
}

.data-table-dialog__info-value-lineage {
  color: #6b7a8e;
  font-style: italic;
  margin-left: 2px;
}


.kpi-chart {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-height: 520px;
  padding: 12px;
  background: #fff;
  border: 1px solid #d8e0ea;
  border-radius: 10px;
}

.kpi-chart--fill-viewport {
  min-height: var(--kpi-shell-height, 520px);
}

.kpi-chart__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.kpi-chart__title {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
  color: #1f3b57;
}

.kpi-chart__toolbar,
.kpi-chart__header .content-toolbar__right {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.kpi-chart__action-button {
  min-width: 30px;
  min-height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  /* v85a: KPI chart toolbar buttons (info / export / full-screen /
     display-toggle) follow the same blend-in pattern as the data-
     table toolbar buttons below. Transparent by default, soft blue
     tint + border on hover. */
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: #1c56a4;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}

/* `display: inline-flex` above would otherwise override the UA `[hidden]`
   rule (display:none). Without this, JS setting `.hidden = true` on these
   buttons has no visible effect — which breaks the bundle-mode hide of
   info/export/full-screen on multi-content cells. */
.kpi-chart__action-button[hidden] {
  display: none;
}

/* .64/.65: toolbar slot for the page-level sidebar controls
   (favorite-menu + time-window pill). The slot lives in
   content-toolbar__left, between the title and any context controls.
   Hidden by default (display:none); revealed when the page root has
   .analysis-page--toolbar-controls (set by setKpiToolbarControlsState
   for single-content kpi subHeadings). When visible, layout matches
   the sidebar top-bar so users see the familiar star + pill pairing
   in the new location.
   .65: target via [data-role="kpi-toolbar-controls"] so the same
   rule covers both kpi-chart's slot (.kpi-chart__sidebar-controls)
   and data-table's slot (.data-table-toolbar__sidebar-controls) —
   kpi displayType 1/2 routes to <data-table>, not <kpi-chart>, and
   needed its own slot (see .65 comment in components/data-table.js
   where the slot is rendered). */
[data-role="kpi-toolbar-controls"] {
  display: none;
  align-items: center;
  gap: 6px;
  margin-left: 8px;
}
.analysis-page--toolbar-controls [data-role="kpi-toolbar-controls"] {
  display: inline-flex;
}
[data-role="kpi-toolbar-controls"] > favorite-menu {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  position: relative; /* anchor for the dropdown panel */
}
[data-role="kpi-toolbar-controls"] > favorite-menu[hidden] {
  display: none;
}
.kpi-chart__time-window,
.data-table-toolbar__time-window {
  /* Inherits report-sidebar__time-window for the base pill (border,
     padding, label ellipsis, hover/focus). margin-left tweak only
     for layout — sidebar version uses flex:auto-pushed-right inside
     its top-bar; the toolbar version sits inline next to the
     favorite star.
     .67: square it off so it reads as a button rather than a pill,
     and lift it slightly with a subtle 1px shadow + softer border
     palette. The sidebar copy keeps the v916 pill look so the
     sidebar header (when it's the active surface) stays recognisable
     against the v963 grey band.
     .68: light grey (#f0f0f0) default fill matching the sidebar top
     bar palette — pulls the button visually in line with the rest
     of the page chrome. The base rule sets background:#fff which
     made the button stand out as starkly white against the toolbar;
     the grey reads as "interactive surface" without competing. */
  flex: 0 0 auto;
  border-radius: 4px;
  border-color: #b8b8b8;
  background: #f0f0f0;
  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
}
.kpi-chart__time-window:hover,
.data-table-toolbar__time-window:hover {
  background: #eef5fd;
  border-color: #6a96c5;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
}

/* Page-level toggle: when the analysis page promotes the toolbar
   copies, hide the sidebar copies. The class is set by
   setKpiToolbarControlsState in analysis-page.js based on
   isSingleKpiContent(subHeading, content). The kpi-chart / data-
   table copies are shown by the rule above. */
.analysis-page--toolbar-controls #report-sidebar-controls-host {
  display: none;
}

.kpi-chart__action-button:hover {
  background: #eaf2fb;
  border-color: #b9cfe6;
}

.kpi-chart__action-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* 26.1.0.75: was 14×14. Same standardisation pass as the
   sibling .data-table-toolbar__icon-svg / .content-toolbar__icon-svg
   rules — 16×16 across the icon palette. */
.kpi-chart__icon-svg {
  width: 16px;
  height: 16px;
  display: block;
}

.kpi-chart__canvas {
  position: relative;
  min-height: 460px;
  width: 100%;
  padding: 0;
  overflow: hidden;
}

.kpi-chart--fill-viewport .kpi-chart__canvas {
  min-height: 320px;
}

.kpi-chart__canvas > svg {
  display: block;
  max-width: 100%;
}


.underlying-page {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.underlying-page__header {
  border: 1px solid #dbdbdb;
  border-radius: 12px;
  overflow: hidden;
  background: #fff;
  box-shadow: 0 10px 28px rgba(15, 23, 42, 0.06);
}

.underlying-page__topbar {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 16px;
  padding: 12px 16px;
  background: #ffffff;
}

.underlying-page__back {
  /* v85p: dark neutral text (not brand-blue), subtle hairline
     border for a quieter outline, and the standard soft-blue hover
     used across toolbar buttons. The previous v85n treatment
     borrowed the carousel back-button's bold brand-blue-on-white
     pill, which read louder than the inline "Back" buttons used
     elsewhere. Matching those instead keeps the underlying page's
     back control quiet next to the centered title. */
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 32px;
  padding: 0 12px;
  background: #ffffff;
  border: 1px solid #d4dde8;
  border-radius: 4px;
  color: #18324d;
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  flex-shrink: 0;
  transition: background 0.15s, border-color 0.15s;
}
.underlying-page__back:hover {
  background: #eaf2fb;
  border-color: #b9cfe6;
}

.underlying-page__title {
  text-align: center;
  font-size: 1.15rem;
  font-weight: 600;
  color: #1f2a44;
}

.underlying-page__title-count {
  color: #607086;
  font-weight: 500;
}

.underlying-page__title-spacer {
  min-width: 80px;
}

/* v85n: blue settings-bar with chips is removed from the underlying
   page chrome — the same information now lives in the collapsible
   Settings panel under the grid. The CSS rules (`.underlying-page__
   settingbar`, `.underlying-page__chip`, `.underlying-page__chips`)
   that styled the old top-of-page bar are deleted below; anything
   that references them either no longer exists in the markup or is
   covered by the .report-content-meta styles. */

@media (max-width: 900px) {
  .underlying-page__topbar {
    grid-template-columns: 1fr;
  }
  .underlying-page__title {
    text-align: left;
  }
}


/* v5 data page polish */
.underlying-page {
  display: flex;
  flex-direction: column;
  gap: 0;
}

.underlying-page__header {
  margin: 0;
  padding: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  border: 0;
  overflow: visible;
}

.underlying-page__topbar {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 16px;
  margin: 0;
  padding: 0 16px;
  min-height: 56px;
  background: #ffffff;
  border-bottom: 1px solid rgba(11, 76, 140, 0.12);
}

/* v85q: the duplicate .underlying-page__back / :hover / back-chevron
   rules that used to live here (transparent bg, brand-blue text,
   orange chevron) are deleted — they kept overriding the primary
   definition near line 5740 via cascade order, and were the reason
   the v85n/p style changes weren't taking effect visually. The
   body.route-kpi overrides further down are similarly pruned. */

.underlying-page__title {
  text-align: center;
  font-size: 1.05rem;
  font-weight: 700;
  color: #12315a;
}

.container:has(.underlying-page) {
  width: 100%;
  max-width: none;
  margin: 0;
  padding: 0;
}

.container .underlying-page + * {
  margin-top: 0;
}


/* v6 viewport/layout polish */
html, body {
  height: 100%;
}

#app-content {
  min-height: 0;
}

body.route-analysis {
  /* v83l: flex-column at body level so app-header takes its natural
     rendered height (not an assumed constant) and #app-content flexes
     into exactly the remaining viewport. The prior fixed
     `#app-content { height: calc(100vh - 48px) }` assumed a 48px
     header, but .report-top-bar is min-height:42px + 1px border = 43px,
     leaving a 5px dead strip at the viewport bottom. Flex eliminates
     the constant entirely and is self-correcting if the toolbar ever
     grows or shrinks. */
  overflow: hidden;
  display: flex;
  flex-direction: column;
  height: 100vh;
}

body.route-kpi {
  overflow: hidden;
}

/* app-header in the analysis route sizes to its own content
   (min-height:42px on .report-top-bar + 1px border). #app-content
   now flexes into the remaining viewport below — no fixed-height
   constant to drift. */
body.route-analysis #app-content {
  flex: 1 1 auto;
  min-height: 0;
}

body.route-kpi #app-content {
  height: 100vh;
  overflow: hidden;
}

body.route-analysis .container,
body.route-kpi .container {
  max-width: none;
  margin: 0;
  padding: 0;
}

/* v85r: kpi route's <main class="container"> needs to be full-height
   flex-column so the chain below it (.underlying-page → content-
   section → data-table / meta-panel) can reach the viewport bottom.
   Without this, .container is its natural block height and the
   panel settles just under the data-table with empty space below. */
body.route-kpi .container {
  height: 100%;
  display: flex;
  flex-direction: column;
}
body.route-kpi .underlying-page {
  flex: 1 1 auto;
  min-height: 0;
}

body.route-analysis .analysis-page {
  height: 100%;
  display: flex;
  flex-direction: column;
}

body.route-analysis .report-workspace {
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}

/* v960: Windows-style fixed-bottom status strip — lives inside
   .analysis-page (see mountAnalysisPage template) as a sibling of
   .report-workspace. The workspace is flex:1 and this bar is
   flex:0 (default), so the bar naturally pins to the bottom of
   the route-analysis column layout WITHOUT needing position:fixed
   or body padding-bottom tricks. That side-steps the v951 footgun
   where html,body{height:100%} defeated body padding-bottom for
   fixed overlays.
   Four sections (user / time-window / data-tree / filters), each
   separated by a hairline matching the v938 chrome palette. */
body.route-analysis .analysis-status-bar {
  flex: 0 0 auto;
  height: 28px;
  background: #f0f0f0;
  border-top: 1px solid #b8b8b8;
  display: flex;
  align-items: stretch;
  font-size: 12px;
  color: #333;
  z-index: 5;
}
.analysis-status-bar__section {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 14px;
  border-right: 1px solid #b8b8b8;
  min-width: 0;
  /* Each section caps its text width so long filter lists don't
     push the bar off screen — overflow:hidden + text-overflow
     ellipsis on the label below trims gracefully. The sections
     divide available space proportionally via flex. */
  max-width: 36%;
}
/* 26.1.0.90: the user / time-window / data-tree sections are now
   <button> elements (so they're keyboard-accessible and dispatch
   click events natively). Reset native button chrome — transparent
   background, no border (preserve the section's right-border
   divider), inherit font, normal cursor — so they read as flat
   status-bar segments. The --clickable variant adds hover/focus
   feedback so users discover the interactivity. */
.analysis-status-bar__section--clickable {
  background: transparent;
  border-top: 0;
  border-bottom: 0;
  border-left: 0;
  /* keep border-right from the base rule for the section divider */
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  height: 100%;
}
.analysis-status-bar__section--clickable:hover {
  background: #e6eef9;
  color: #1a4a87;
}
.analysis-status-bar__section--clickable:hover .analysis-status-bar__icon {
  color: #1a4a87;
}
.analysis-status-bar__section--clickable:focus-visible {
  outline: 2px solid #1a4a87;
  outline-offset: -2px;
}
.analysis-status-bar__section--clickable[disabled] {
  cursor: default;
  opacity: 0.6;
}
.analysis-status-bar__section--clickable[disabled]:hover {
  background: transparent;
  color: inherit;
}
.analysis-status-bar__section:last-child {
  border-right: 0;
}
/* v967: the filter section expands to fill available space. Was
   previously keyed off :last-child (which required filters to be
   the last child of the status bar). Now that the toggle section
   sits to its right, key off [data-section="filters"] explicitly
   so the grow behaviour survives the reordering. */
.analysis-status-bar__section[data-section="filters"] {
  flex: 1 1 auto;
  max-width: none;
}
/* v967: toggle section hosts the meta-panel expand chevron. Compact —
   just enough padding to give the 24×24 button breathing room on
   either side, and a natural width so the preceding filter section's
   border-right serves as a clean divider. When hidden (no meta panel
   to expand) the section collapses out of the flex layout entirely
   thanks to the [hidden] override above. */
.analysis-status-bar__section--toggle {
  flex: 0 0 auto;
  padding: 0 8px;
  min-width: 0;
  max-width: none;
}
/* v965: explicit [hidden] override — `display: flex` on the base
   selector (same specificity as the UA rule `[hidden] { display: none }`
   but defined later in the cascade) would otherwise win over the
   hidden attribute, so the visibility toggles in
   applyContentInfoToStatusBar had no visual effect. User flagged
   "Database Content" still showing in the data-tree section even
   after the v965 JS-side hide ran. Explicit rule here + !important
   — for defence in depth against any later rule that might also
   set display on the section (the filter section's last-child
   variant is one that does). */
.analysis-status-bar__section[hidden] {
  display: none !important;
}
.analysis-status-bar__icon {
  width: 14px;
  height: 14px;
  flex: 0 0 14px;
  color: #555;
}
.analysis-status-bar__label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
/* Filter section: icon wrapper with optional badge pinned to its
   top-right corner. Matches the .data-table__corner-badge palette
   from the v956 corner button — same red-pill filter-count styling
   across the app for visual consistency. */
.analysis-status-bar__filter-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
}
.analysis-status-bar__filter-badge {
  position: absolute;
  top: -6px;
  right: -8px;
  min-width: 14px;
  height: 14px;
  padding: 0 3px;
  border-radius: 7px;
  /* v981: color unified with the filter-tree panel badge (#1a4a87)
     which also renders on the rail-mode filter button (see
     .report-settings-bar__toggle-badge) and the sidebar panel-
     header badge (.report-sidebar__panel-badge). Was #d64545
     (red) — user requested parity with the panel badge so the
     three filter-count indicators across the analysis page read
     as the same semantic. */
  background: #1a4a87;
  color: #ffffff;
  font-size: 10px;
  font-weight: 600;
  line-height: 14px;
  text-align: center;
}

/* v962: meta-panel expand chevron. v967: wrapped in a section div
   so it picks up the standard section-chrome divider; margin-left
   auto + margin-right removed since the section-toggle wrapper
   handles positioning + spacing. Button bumped 22→24px and SVG
   14→16px per user request — more tappable target and the icon
   reads more clearly against the status-bar's 28px band. */
.analysis-status-bar__toggle {
  flex: 0 0 auto;
  align-self: center;
  width: 24px;
  height: 24px;
  padding: 2px;
  border: 0;
  border-radius: 3px;
  background: transparent;
  color: #555;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 0.12s, color 0.12s;
}
.analysis-status-bar__toggle:hover {
  background: #e4e4e4;
  color: #333;
}
.analysis-status-bar__toggle:focus-visible {
  outline: 2px solid #4a7dbf;
  outline-offset: 1px;
}
.analysis-status-bar__toggle[hidden] {
  display: none;
}
.analysis-status-bar__toggle-icon {
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
}

body.route-analysis .report-main,
body.route-analysis .report-nav,
body.route-analysis report-subheading-tabs,
body.route-analysis .subheading-tabs,
body.route-analysis .report-content-area {
  min-height: 0;
}

body.route-analysis .report-content-area {
  flex: 1 1 auto;
  overflow: auto;
  padding: 4px 8px 8px;
}

/* Higher-specificity override of the generic padding above — needed
   because that rule was winning over the plain
   `.report-content-area--single-content { padding: 0 }`. Single-content
   subHeadings sit flush against the subheading-tab row and sidebar. */
body.route-analysis .report-content-area.report-content-area--single-content {
  padding: 0;
}

body.route-analysis .report-content-area.report-content-area--single-content,
body.route-analysis .report-content-area.report-content-area--single-content.report-content-area--with-meta,
body.route-analysis .report-content-area:not(.report-content-area--single-content) {
  overflow: hidden;
}

body.route-analysis .kpi-chart {
  height: 100%;
  min-height: 0;
  padding: 0;
  gap: 0;
  border: 1px solid #d6dde8;
  border-radius: 0;
  box-shadow: none;
}

body.route-analysis .kpi-chart__header {
  min-height: 32px;
  height: 32px;
  padding: 0 8px 0 10px;
  border-bottom: 1px solid #bbbbbb;
  background: #eeeeee;
}

body.route-analysis .kpi-chart__title {
  font-size: 15px;
  font-weight: 400;
  color: #16395f;
}

body.route-analysis .kpi-chart__toolbar,
body.route-analysis .kpi-chart__header .content-toolbar__right {
  gap: 6px;
}

body.route-analysis .kpi-chart__action-button,
body.route-analysis .data-table-toolbar__action-button {
  /* v86m: unify content-toolbar buttons across chart + grid, matching
     the top-bar .rtb-action-btn 32x32 sizing. Previously chart was
     30x30 (rule at 6263 below) and grid was 28x28 (rule at 7405), so
     the same button was a different size depending on contentType.
     Single selector here keeps them from drifting again. */
  min-width: 32px;
  width: 32px;
  height: 32px;
  min-height: 32px;
  padding: 0;
  border-radius: 6px;
}

/* v86s: the "New" button carries a text label ("New", "New DC Inspection
   Report…"), not just an icon, so it must keep auto width + horizontal
   padding. The unified 32x32 rule above would otherwise clip it to icon
   width and hide the label. Same min-width floor (32px) so it blends
   visually with the icon-only buttons when the label is short. */
body.route-analysis .data-table-toolbar__action-button--new {
  width: auto;
  min-width: 32px;
  max-width: 200px;
  padding: 0 10px;
}

body.route-analysis .kpi-chart__icon-svg,
body.route-analysis .data-table-toolbar__icon-svg,
body.route-analysis .content-toolbar__icon-svg {
  /* v86m: 16x16 icons inside 32x32 buttons. */
  /* v86q: bumped to 20x20. The export modifier below used to be the
     only "larger" icon (20x20) while display-toggle / info / etc.
     stayed 16x16 — that made chart toolbars look smaller than the
     grid toolbar's export icon alongside them. Flat 20x20 for every
     content-toolbar icon keeps things consistent regardless of
     content-type or displayType.
     26.1.0.75: returned to 16x16 per user request — the corner
     buttons (form/edit/settings) and toolbar buttons (info,
     export, inspector, drill-in) were standardised at 16x16 in
     .73. The 20x20 outlier here was making content-toolbar icons
     look oversized next to the rest. The HTML width=16/height=16
     attributes I set on the SVG elements in .71 / .72 were being
     overridden by THIS rule; flipping the CSS gets the actual
     rendered size to match the user's 16x16 ask. */
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
}

body.route-analysis .content-toolbar__icon-svg--export {
  /* 26.1.0.75: was 20x20 to match the .v86q base bump above; now
     16x16 alongside the base. No-op-feel selector kept in case
     a future per-icon override is wanted. */
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
}

body.route-analysis .kpi-chart__canvas {
  min-height: 0;
  height: calc(100% - 32px);
  padding: 0;
  box-sizing: border-box;
  overflow: hidden;
}

body.route-analysis .kpi-chart__canvas > svg {
  display: block;
  overflow: visible;
}

body.route-analysis .report-multi-content .kpi-chart {
  min-height: 0;
}

body.route-analysis .report-multi-content .kpi-chart__canvas {
  min-height: 0;
}

body.route-kpi .underlying-page {
  height: 100%;
  gap: 0;
}

body.route-kpi .underlying-page__header {
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

body.route-kpi .underlying-page__topbar {
  min-height: 48px;
  padding: 0 10px 0 8px;
  border-bottom: 1px solid #dadada;
}

/* v85q: route-kpi overrides for .underlying-page__back / __back-chevron
   are deleted — they reset border/color/font-weight and were defeating
   the primary back-button rule. The v85p base rule (dark neutral text,
   hairline border, soft hover) now wins cleanly. */

body.route-kpi .underlying-page__title {
  font-size: 18px;
  font-weight: 700;
  color: #173a63;
}

/* v85q: route-kpi layout — the underlying-page is full-viewport
   height (height: 100% above). Make its content section a flex
   column that fills the space below the top header so the data-
   table occupies the upper region and the bottom meta panel sticks
   to the bottom edge instead of leaving a big empty rectangle
   below. Without this the content-section was its natural height
   (data-table + panel) and any extra viewport height showed as
   dead space between panel and viewport bottom. */
body.route-kpi .underlying-page .analysis-page__content-section {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  /* v85s: clip anything that wants to grow past the content-section's
     flex allocation. Without this, the data-table-shell (which
     expands when a connected grid opens inline) bleeds downward
     past its allocation and overlaps the bottom Settings panel. */
  overflow: hidden;
}
body.route-kpi .underlying-page .analysis-page__content-section > data-table {
  flex: 1 1 auto;
  min-height: 0;
  /* v85s: the <data-table> custom element is display:block by default,
     which means its `height` is determined by content rather than
     the flex allocation. Making it a flex column lets its internal
     .data-table-shell resolve `height: 100%` against the flex-sized
     <data-table>, so the shell can't grow taller than the space
     reserved for it above the Settings panel. */
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
body.route-kpi .underlying-page .analysis-page__content-section > .report-content-meta {
  flex: 0 0 auto;
}

body.route-kpi .underlying-page .data-table-shell {
  /* v85q: was `calc(100% - 100px)` — compensating for the old top
     settings-bar. That bar is gone (v85n), so reclaim the full
     height. The flex:1 on .analysis-page__content-section ensures
     .data-table-shell gets the right vertical allocation.
     v85s: `!important` so we beat the .data-table-shell--fill-
     viewport rule (line 3294) which resolves --table-shell-height
     from syncViewportLayout's window.innerHeight math — that math
     targets the viewport bottom and ignores the Settings panel
     sitting below. `height: 100%` keeps the shell inside its
     flex-sized parent instead. max-height override prevents the
     base `calc(100vh - 290px)` cap from sneaking in. */
  height: 100% !important;
  max-height: 100% !important;
  border-left: 0;
  border-right: 0;
  border-bottom: 0;
  border-radius: 0;
}

body.route-kpi .underlying-page .data-table__path {
  display: none;
}

body.route-kpi .underlying-page .data-table-toolbar {
  padding: 6px 8px;
  background: #f2f2f2;
}

body.route-kpi .underlying-page .data-table-toolbar__title {
  color: #1c56a4;
  font-size: 14px;
  font-weight: 400;
  white-space: nowrap;
}

body.route-kpi .underlying-page .data-table-toolbar__back {
  border: 0;
  background: transparent;
  padding: 0;
  color: #1c56a4;
}

body.route-kpi .underlying-page .data-table-toolbar__search-wrap {
  max-width: 360px;
}

body.route-kpi .underlying-page .data-table-toolbar__action-button {
  min-width: 30px;
  min-height: 30px;
}

body.route-kpi .underlying-page .data-table-shell--with-text {
  min-height: 0;
  height: calc(100vh - 144px);
}

body.route-kpi .underlying-page .data-table__table-wrap {
  min-height: 0;
}

body.route-kpi .underlying-page .data-table__connected-tabs {
  padding: 6px 2px 4px;
}

body.route-kpi .underlying-page .data-table__text-panel {
  min-height: 64px;
}



/* v7 data page header polish */
body.route-kpi .underlying-page__back-chevron {
  /* v85p: inherit button's text color so the chevron matches the
     dark neutral label. Previously #1c56a4 brand-blue, which paired
     with the old blue-on-white back button. */
  color: inherit;
  font-size: 1.4rem;
  margin-right: 4px;
}

body.route-kpi .underlying-page__topbar {
  padding: 0 10px;
  min-height: 44px;
}


/* v8 KPI displayType 1 analysis-grid support */
.data-table__body-cell--tree-label {
  white-space: nowrap;
}

/* v957: first-visible-row bold (Total row) now scoped OUT of KPI
   grids. User asked to match the Windows reference's regular-weight
   tree labels at every level. Inline-style bold on hasChildren /
   !TOTAL rows was already skipped for KPI in the v957 data-table.js
   edit; this CSS rule was the remaining source. Non-KPI
   hierarchical grids keep the bold-on-first-row styling intact. */
.data-table-shell:not(.data-table-shell--hide-row-numbers) .data-table__row:first-child .data-table__body-cell--tree-label {
  font-weight: 700;
}


/* v9 sticky first columns for analysis/data tables */
.data-table__table-wrap {
  overflow: auto;
  position: relative;
}

.data-table__table {
  border-collapse: separate;
  border-spacing: 0;
}

.data-table__table th,
.data-table__table td {
  background-clip: padding-box;
}

.data-table__table thead th:nth-child(1),
.data-table__table tbody td:nth-child(1) {
  position: sticky;
  left: 0;
  z-index: 4;
  background: #f3f5f8;
}

/* The 2nd-child sticky/styling rules below used to apply unconditionally,
   which pinned the first data column in FLAT content too (wrong — flat
   mode pins only the explicit fixed columnGroup via --fixed classes).
   Scoped to hierarchical mode so flat tables let the first column scroll. */
.data-table-scroll[data-mode="hierarchical"] .data-table__table thead th:nth-child(2),
.data-table-scroll[data-mode="hierarchical"] .data-table__table tbody td:nth-child(2) {
  position: sticky;
  left: 64px;
  z-index: 3;
  background: #ffffff;
}

.data-table-scroll[data-mode="hierarchical"] .data-table__table thead tr:first-child th:nth-child(1),
.data-table-scroll[data-mode="hierarchical"] .data-table__table thead tr:first-child th:nth-child(2),
.data-table-scroll[data-mode="hierarchical"] .data-table__table thead tr:nth-child(2) th:nth-child(1),
.data-table-scroll[data-mode="hierarchical"] .data-table__table thead tr:nth-child(2) th:nth-child(2) {
  z-index: 6;
}

.data-table__table thead th:nth-child(1),
.data-table__table tbody td:nth-child(1) {
  min-width: 64px;
  width: 64px;
  max-width: 64px;
}

.data-table-scroll[data-mode="hierarchical"] .data-table__table thead th:nth-child(2),
.data-table-scroll[data-mode="hierarchical"] .data-table__table tbody td:nth-child(2) {
  min-width: 280px;
}

.data-table__table tbody td:nth-child(1) {
  box-shadow: 1px 0 0 #bdbdbd;
}

.data-table-scroll[data-mode="hierarchical"] .data-table__table tbody td:nth-child(2),
.data-table-scroll[data-mode="hierarchical"] .data-table__table thead th:nth-child(2) {
  box-shadow: 1px 0 0 #cbcbcb;
}

.data-table__table thead th:nth-child(1) {
  background: #f1f1f1;
}

.data-table-scroll[data-mode="hierarchical"] .data-table__table thead th:nth-child(2) {
  background: #c0c0c0;
}

/* Removed: position-based zebra stripe on td:nth-child(1/2).
   Zebra is now handled only on scrollable body cells via the row class. */

body.route-analysis .data-table__table thead th:nth-child(1),
body.route-analysis .data-table__table tbody td:nth-child(1) {
  background: #efefef;
}

body.route-analysis .data-table__table thead th:nth-child(2) {
  background: #c0c0c0;
}



/* v10 real sticky columns for rendered grid */
.data-table-shell .data-table__grid-wrap,
.data-table-shell .data-table__table-wrap,
.data-table-shell .data-table__body-wrap {
  overflow: auto;
  position: relative;
}

.data-table-shell table {
  border-collapse: separate;
  border-spacing: 0;
}

.data-table-shell table th,
.data-table-shell table td {
  background-clip: padding-box;
}

/* ── Column 1 (utility / row-number) ─────────────────────────────────────────
   The utility cell is always sticky regardless of mode. It is rendered
   separately from the data column group pipeline so its stickiness is
   unconditional: it is never part of a column group and never inherits
   group.fixed logic.
   ─────────────────────────────────────────────────────────────────────────── */
/* Utility body cell sticky — tbody only.
   The thead uses the corner cell (rowspan=2) which is handled by the
   .data-table__corner-cell rule (v13). We must NOT use thead tr > *:nth-child(1)
   because in flat grids the column-row has no corner cell, so nth-child(1)
   would hit the first data column and incorrectly pin it. */
.data-table-shell table tbody tr > *:nth-child(1) {
  position: sticky;
  left: 0;
  z-index: 7;
}

/* Utility column sizing — tbody */
.data-table-shell table tbody tr > *:nth-child(1) {
  min-width: 64px;
  width: 64px;
  max-width: 64px;
  background: #ededed;
  box-shadow: 1px 0 0 #bdbdbd;
}

/* Utility column (col 1) — uniform background, no zebra */
.data-table-shell table tbody tr:nth-child(even) > *:nth-child(1) {
  background: #ededed; /* same as odd rows — intentionally not zebra */
}

/* ── Column 2 (first real data column) ───────────────────────────────────────
   In HIERARCHICAL mode the first data column is always the tree-label column
   and must remain sticky. We keep the nth-child(2) rule scoped to the
   [data-mode="hierarchical"] scroll wrapper.

   In FLAT mode we DO NOT use nth-child position. Stickiness is driven
   exclusively by the --fixed / --scrollable modifier classes that the
   renderer emits based on group.fixed from the layout payload.
   ─────────────────────────────────────────────────────────────────────────── */

/* Hierarchical mode only: stick the 2nd column (tree label) */
.data-table-scroll[data-mode="hierarchical"] table tr > *:nth-child(2) {
  position: sticky;
  left: 64px;
  z-index: 6;
}

.data-table-scroll[data-mode="hierarchical"] table thead tr > *:nth-child(2) {
  z-index: 9;
  min-width: 280px;
  background: #c0c0c0;
  box-shadow: 1px 0 0 #cbcbcb;
}

.data-table-scroll[data-mode="hierarchical"] table tbody tr > *:nth-child(2) {
  min-width: 280px;
  background: #ffffff;
  box-shadow: 1px 0 0 #cbcbcb;
}

/* Hierarchical col 2 even rows — no zebra, same as odd rows */
.data-table-scroll[data-mode="hierarchical"] table tbody tr:nth-child(even) > *:nth-child(2) {
  background: #ffffff;
}

/* Analysis route colour overrides (hierarchical only) */
body.route-analysis .data-table-shell table tbody tr > *:nth-child(1) {
  background: #ededed;
}
body.route-analysis .data-table-scroll[data-mode="hierarchical"] table tbody tr > *:nth-child(2) {
  background: #ffffff;
}


/* v11/v12 sticky header rows — apply in all modes.
   top values reinforced inline by stickyHeaders() JS for variable scroll containers.
   Opaque backgrounds prevent body rows bleeding through sticky headers. */
.data-table-shell table thead {
  position: sticky;
  top: 0;
  z-index: 8;
}

.data-table-shell table thead tr:nth-child(1) > * {
  position: sticky;
  top: 0;
  z-index: 10;
  background: #f1d9c0;
}

.data-table-shell table thead tr:nth-child(2) > * {
  position: sticky;
  top: 28px;
  z-index: 9;
  background: #f6f7f9;
}

/* Corner cell (group row nth-child(1)) is always pinned left:0.
   The column row (nth-child(2)) has NO corner cell in flat grids — its
   nth-child(1) is the first data column which must NOT be pinned horizontally.
   Horizontal pinning for column headers is handled by --fixed class rules only. */
.data-table-shell table thead tr:nth-child(1) > *:nth-child(1) {
  left: 0;
  z-index: 12;
}

/* 2nd header cell left position: hierarchical only (nth-child approach) */
.data-table-scroll[data-mode="hierarchical"] table thead tr:nth-child(1) > *:nth-child(2),
.data-table-scroll[data-mode="hierarchical"] table thead tr:nth-child(2) > *:nth-child(2) {
  left: 64px;
  z-index: 11;
}


.data-table-shell--hide-row-numbers .data-table-scroll[data-mode="hierarchical"] table tr > *:nth-child(2),
.data-table-shell--hide-row-numbers .data-table-scroll[data-mode="hierarchical"] table thead tr:nth-child(1) > *:nth-child(2),
.data-table-shell--hide-row-numbers .data-table-scroll[data-mode="hierarchical"] table thead tr:nth-child(2) > *:nth-child(2) {
  left: 0 !important;
}

/* ── KPI analysis grid column-row fix ──────────────────────────────────────
   In the column row (<tr class="data-table__column-row">) the corner cell is
   NOT present — it only lives in the group row with rowspan="2". So in the
   column row nth-child positions are shifted by one compared to body rows:
     column-row nth-child(1) = Description column header (no utility header)
     column-row nth-child(2) = first scrollable column (e.g. Inventory)
   The general [data-mode="hierarchical"] thead rule applies min-width:280px
   and position:sticky to *:nth-child(2), which means Inventory wrongly gets
   the same width and stickiness as the Description column.
   These rules (scoped to hide-row-numbers = KPI analysis grids only) correct
   the column row without touching body rows or contentType:data grids.    */

/* Description column header: sticky at left:0, content-driven width (no hard 280px) */
.data-table-shell--hide-row-numbers
  .data-table-scroll[data-mode="hierarchical"]
  table thead tr:nth-child(2) > *:nth-child(1) {
  position: sticky;
  left: 0 !important;
  z-index: 11;
  min-width: 0;
  background: #c0c0c0;
  box-shadow: 1px 0 0 #cbcbcb;
}

/* First scrollable column header: undo the wrong sticky + min-width */
.data-table-shell--hide-row-numbers
  .data-table-scroll[data-mode="hierarchical"]
  table thead tr:nth-child(2) > *:nth-child(2) {
  position: static !important;
  left: auto !important;
  z-index: auto;
  min-width: 0;
}

/* KPI analysis grids: description column cells must not be capped at 300px.
   Bold parent rows and deeply-indented leaf rows (padding-left up to 66px+)
   plus the toggle button/spacer can exceed 300px and would be clipped by the
   base .data-table__body-cell rule. Remove the cap; the column already sizes
   to max-content so this just lets every cell show its full content.
   contentType:data grids are unaffected — they never use hide-row-numbers. */
.data-table-shell--hide-row-numbers
  .data-table-scroll[data-mode="hierarchical"]
  .data-table__body-cell--tree-label {
  max-width: none;
}

/* Body description cells: undo the hard 280px min-width from the general
   hierarchical rule so the column is content-driven, not forced to 280px. */
.data-table-shell--hide-row-numbers
  .data-table-scroll[data-mode="hierarchical"]
  table tbody tr > *:nth-child(2) {
  min-width: 0;
}

/* Group row description cell (nth-child(2) of group row = Description group header)
   is correctly addressed by the general rule since group row DOES have the corner
   cell as nth-child(1). Override its min-width too for content-driven sizing. */
.data-table-shell--hide-row-numbers
  .data-table-scroll[data-mode="hierarchical"]
  table thead tr:nth-child(1) > *:nth-child(2) {
  min-width: 0;
}

.data-table__body-cell--drillthrough {
  cursor: pointer;
  color: inherit;
  text-decoration: none !important;
  transition: background-color 120ms ease, color 120ms ease;
}

.data-table__body-cell--drillthrough:hover {
  background: #eaf3ff !important;
  color: #0f4fa8;
}

.data-table__body-cell--drillthrough:active {
  background: #dcecff !important;
}


/* v13 sticky corner merged cell — unconditional, applies in all modes.
   v83p: background unified with the chrome shelf grey (#f1f1f1) so the
   corner reads as an extension of the subheading-tab strip above it,
   not a darker block.
   26.1.0.68: flipped from grey to white per user request — the
   leftmost column (form button + row number) is white in body, and
   the user wants the two corner header cells above it to match.
   The cell still reads as merged with the column-header band
   visually because of the box-shadow hairline below; only the fill
   colour changes. The !important here is what was actually winning
   over the duplicate non-important rules at lines ~7514 / ~7642
   (now also white), so this is the rule that needed to flip for
   the visible change to take effect. */
.data-table__corner-cell {
  position: sticky !important;
  top: 0 !important;
  left: 0 !important;
  z-index: 13 !important;
  min-width: 64px;
  width: 64px;
  max-width: 64px;
  background: #ffffff !important;
  box-shadow: 1px 0 0 #bdbdbd;
}

.data-table__group-row .data-table__corner-cell {
  vertical-align: middle;
}

.data-table__corner-button {
  position: relative;
  z-index: 14;
}


/* v14 — class-based sticky rules (replaces :first-of-type position hacks)
   ────────────────────────────────────────────────────────────────────────
   The renderer now emits:
     data-table__group-cell--fixed / --scrollable   on <th> in group row
     data-table__column-cell--fixed / --scrollable  on <th> in column row
     data-table__body-cell--fixed / --scrollable    on <td> in body rows

   The corner cell (utility column header) is handled separately by v13.
   The utility body cell (.data-table__utility-cell) is always sticky via
   the nth-child(1) rule above.

   HIERARCHICAL MODE: the tree-label column always gets --fixed because
   isTreeLabelCell sets isCellFixed=true in the renderer regardless of
   group.fixed. The nth-child(2) rules above also cover it as a belt-
   and-suspenders for header rows.

   FLAT MODE: only columns whose group.fixed===true receive --fixed, so
   :first-of-type is never the deciding factor.
   ──────────────────────────────────────────────────────────────────── */

/* Corner cell in group row */
.data-table__group-row > .data-table__corner-cell {
  position: sticky !important;
  top: 0 !important;
  left: 0 !important;
  z-index: 20 !important;
}

/* Fixed group header cell — left set inline by renderer */
.data-table__group-row > .data-table__group-cell--fixed {
  position: sticky !important;
  top: 0 !important;
  z-index: 19 !important;
}

/* Fixed column header cell — left set inline by renderer.
   Background comes from the column layout (inline style), not overridden here. */
.data-table__column-row > .data-table__column-cell--fixed {
  position: sticky !important;
  top: 28px !important;
  z-index: 18 !important;
  box-shadow: 1px 0 0 #cbcbcb;
}

/* Utility body cell — always sticky at left:0.
   v83q: background unified with the chrome shelf grey (#f1f1f1) so the
   row-number column reads as an extension of the corner button above
   it, not a distinct light-blue strip. No-zebra treatment preserved.
   26.1.0.68: flipped to white per user request — the user wants the
   entire leftmost column white. The corner header cells at top of
   this column (.data-table__corner-cell) were also flipped to white
   in the same pass; the column now reads as a single white surface
   from header through every body row, with the form-button + row-
   number content sitting on it. The hairline separator to the right
   (box-shadow elsewhere) keeps the column visually distinct from
   the data cells next to it. */
.data-table__row > .data-table__utility-cell {
  position: sticky !important;
  left: 0 !important;
  z-index: 8 !important;
  background: #ffffff !important;
}

/* Utility cell — same background on all rows, no zebra. Was
   #f1f1f1 to match the corner above; now #ffffff for the same
   reason as the rule above. */
.data-table__row:nth-child(even) > .data-table__utility-cell {
  background: #ffffff !important;
}

/* Fixed body cell — left set inline by renderer.
   Background defaults to white so scrolling content does not bleed through,
   but no !important so a cell-level inline background can override it. */
.data-table__row > .data-table__body-cell--fixed {
  position: sticky !important;
  z-index: 7 !important;
  background: #ffffff;
  box-shadow: 1px 0 0 #cbcbcb;
}

/* Fixed body cells — uniform white on all rows */
.data-table__row:nth-child(even) > .data-table__body-cell--fixed {
  background: #ffffff;
}

/* No zebra striping — all scrollable body cells use default background */



/* v16 analysis grid improvements */
body.route-analysis .report-content-area {
  padding: 4px 4px 4px;
}

/* Single-content flush (also overriding the 4px padding above) */
body.route-analysis .report-content-area.report-content-area--single-content {
  padding: 0;
}

body.route-analysis .data-table-shell {
  height: auto;
  max-height: calc(100vh - 170px);
  min-height: 0;
  border-radius: 0;
  overflow: hidden;
}

body.route-analysis .data-table-shell--fill-viewport {
  max-height: none !important;
  height: var(--table-shell-height, 100%);
}

body.route-analysis .data-table-shell--fill-viewport .data-table-scroll {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none !important;
  height: auto !important;
  overflow: auto;
}

body.route-analysis .data-table-scroll {
  max-height: calc(100vh - 250px);
  height: auto;
}

.data-table__group-row > .data-table__corner-cell {
  min-width: 64px;
  width: 64px;
  max-width: 64px;
}

.data-table__tree-cell {
  display: flex;
  align-items: center;
  gap: 6px;
}

.data-table__tree-toggle,
.data-table__tree-toggle-spacer {
  flex: 0 0 14px;
  width: 14px;
  min-width: 14px;
  height: 14px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.data-table__tree-toggle {
  border: 1px solid #7aa0cf;
  background: #fff;
  color: #1c56a4;
  border-radius: 2px;
  line-height: 1;
  padding: 0;
  cursor: pointer;
  font-size: 12px;
}

.data-table__tree-toggle:hover {
  background: #eef5ff;
}

.data-table__tree-toggle-spacer {
  content: '';
}

.data-table__drill-link {
  color: inherit;
  text-decoration: none;
  display: block;
}

.data-table__body-cell--drillthrough {
  cursor: pointer;
}

.data-table__body-cell--drillthrough:hover {
  background: #eaf3ff !important;
}

.data-table__body-cell--drillthrough:hover .data-table__drill-link {
  color: #0f4fa8;
}


.data-table-dialog__overlay {
  position: fixed;
  inset: 0;
  background: rgba(12, 25, 42, 0.18);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
}

.data-table-dialog {
  min-width: 420px;
  max-width: calc(100vw - 32px);
  background: #fff;
  border: 1px solid #d0d0d0;
  border-radius: 4px;
  box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18);
}

.data-table-dialog__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 16px;
  border-bottom: 1px solid #d8dee7;
}

.data-table-dialog__title {
  margin: 0;
  font-size: 15px;
  font-weight: 500;
  color: #1c56a4;
}

.data-table-dialog__close {
  border: none;
  background: none;
  color: #1c56a4;
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
}

.data-table-dialog__body {
  padding: 16px;
}

.data-table-dialog__export-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.data-table-dialog__select {
  min-width: 128px;
  min-height: 32px;
  padding: 0 10px;
  border: 1px solid #b7c7d8;
  border-radius: 2px;
  background: #fff;
  color: #18324d;
  font: inherit;
  /* .43: was inheriting body's ~14-16px, so the Equals dropdown
     sat visibly larger than every other 13px control in the dialog
     (button row, checkbox labels, footer buttons). Lock to 13px to
     match. */
  font-size: 13px;
}

/* v86w: align data-table dialog footer + primary button with the
   Export Report dialog's style (.erd-* classes) so the three dialogs
   the user sees most often — Export Content, Export Report, and
   Select Business Category — share a consistent look. The duplicate
   rules below previously set footer bg to navy (#223548) and base
   button bg to white with a blue hover, which caused the "hover on
   OK makes the whole thing blue" bug on the Export Content dialog. */
.data-table-dialog__footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 12px 18px;
  background: #f1f1f1;
  border-top: 1px solid #dddddd;
}

.data-table-dialog__button {
  height: 34px;
  padding: 0 20px;
  border: 1px solid #ccc;
  border-radius: 4px;
  background: #fff;
  color: #333;
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.data-table-dialog__button:hover {
  background: #f0f2f5;
}

.data-table-dialog__button--primary {
  background: var(--brand-dark, #1a5276);
  color: #fff;
  border-color: var(--brand-dark, #1a5276);
  font-weight: 600;
}
.data-table-dialog__button--primary:hover {
  background: #154360;
}


/* v15.1 pragmatic corner fix
   Replace the merged rowspan corner with two explicit sticky corner header cells.
   This avoids rowspan + sticky + measured-left drift at the seam between:
   - top-left corner header
   - utility/body sticky column
   - first fixed data/group column
*/
.data-table__group-row > .data-table__corner-cell,
.data-table__column-row > .data-table__corner-cell {
  position: sticky !important;
  left: 0 !important;
  min-width: 64px;
  width: 64px;
  max-width: 64px;
  box-sizing: border-box;
}

.data-table__group-row > .data-table__corner-cell {
  z-index: 20 !important;
  /* 26.1.0.68: was #f1f1f1; now white per the same user request
     that flipped the base .data-table__corner-cell rule. Whole
     leftmost column is white now. */
  background: #ffffff !important;
}

.data-table__column-row > .data-table__corner-cell {
  z-index: 18 !important;
  /* 26.1.0.68: same flip — see group-row above. */
  background: #ffffff !important;
  border-right: 1px solid #cbcbcb;
  border-bottom: 1px solid #cbcbcb;
}

.data-table__corner-cell--bottom {
  padding: 0;
}

.data-table-dialog--filter {
  width: min(430px, calc(100vw - 32px));
}

.data-table-dialog__body--filter {
  display: grid;
  gap: 14px;
}

/* .42: subheader bar wrapping the +Add Operation / Or-And segmented
   control — visual weight match for the React reference's
   <ui5-bar design="Subheader">. Light grey background with a thin
   border-bottom delimits the action row from the criteria area, same
   as the React equivalent. */
.data-table-dialog__filter-subheader {
  margin: -16px -16px 0;
  padding: 8px 16px;
  background: #f1f1f1;
  border-bottom: 1px solid #dddddd;
}

.data-table-dialog__filter-actions {
  display: flex;
  align-items: center;
  /* .42: was space-between (Add on left, Or/And on right). React's
     <ui5-bar slot="endContent"> right-aligns both controls as a single
     cluster, with empty space on the left. Match that grouping. */
  justify-content: flex-end;
  gap: 10px;
}

.data-table-dialog__button-plus {
  display: inline-block;
  margin-right: 2px;
  font-weight: 600;
}

.data-table-dialog__segmented {
  display: inline-flex;
  border: 1px solid #0b63ce;
  border-radius: 8px;
  overflow: hidden;
}

.data-table-dialog__segmented-button,
.data-table-dialog__icon-button {
  border: 0;
  background: #fff;
  color: #0b63ce;
  cursor: pointer;
}

.data-table-dialog__segmented-button {
  padding: 8px 14px;
  font: inherit;
  /* .43: same as __select above — was inheriting ~14-16px so the
     Or/And buttons sat larger than the rest of the dialog. */
  font-size: 13px;
}

.data-table-dialog__segmented-button.is-active {
  background: #0b63ce;
  color: #fff;
}

.data-table-dialog__filter-criterion {
  display: grid;
  gap: 10px;
  padding: 10px 0 0;
  border-top: 1px solid #d6dde8;
}

.data-table-dialog__filter-criterion:first-of-type {
  border-top: 0;
  padding-top: 0;
}

.data-table-dialog__filter-criterion-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.data-table-dialog__icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: 1px solid #bfd0e7;
  border-radius: 6px;
}

.data-table-dialog__field.is-hidden {
  display: none;
}

/* Lookup-column filter UI: scrollable list of checkboxes that toggle
   distinct values in/out of the value field as a `;`-joined list. */
.data-table-dialog__filter-checkbox-list {
  list-style: none;
  margin: 4px 0 0;
  padding: 6px 8px;
  border: 1px solid #bfd0e7;
  border-radius: 6px;
  max-height: 220px;
  overflow-y: auto;
  background: #fff;
}

.data-table-dialog__filter-checkbox-row {
  display: block;
  padding: 2px 0;
}

/* .42: dim items whose code is not in the active scope's distinct
   values. Matches React LookupItem.js's grayedOut class (color
   #AAAAAA). The checkbox itself is left enabled — clicking adds the
   code to the criterion value and the row becomes "selected", which
   takes precedence over non-relevance in the renderer. */
.data-table-dialog__filter-checkbox-row.is-non-relevant
  .data-table-dialog__filter-checkbox-label > span {
  color: #AAAAAA;
}

.data-table-dialog__filter-checkbox-label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: 13px;
  color: var(--ink, #1a2737);
  user-select: none;
}

.data-table-dialog__filter-checkbox-label input[type="checkbox"] {
  margin: 0;
  cursor: pointer;
}

/* .42: "Hide non-relevant items" toggle above the checkbox list.
   Mirrors React FilterItem's chkHideNonRelevant. Sits in the same
   vertical rhythm as the list — small font, inline gap, no border —
   to read as a list-level option rather than a separate field. */
.data-table-dialog__filter-hide-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 4px 0 0;
  font-size: 12px;
  color: #5b6c80;
  user-select: none;
  cursor: pointer;
}

.data-table-dialog__filter-hide-toggle input[type="checkbox"] {
  margin: 0;
  cursor: pointer;
}

.data-table-dialog__filter-distinct-hint {
  margin: 4px 0 0;
  font-size: 12px;
  color: #8896a8;
  font-style: italic;
}

.data-table-dialog__filter-loading,
.data-table-dialog__filter-empty,
.data-table-dialog__filter-error {
  margin: 4px 0 0;
  font-size: 13px;
  color: #5b6c80;
  font-style: italic;
}

.data-table-dialog__filter-error {
  color: #b03030;
  font-style: normal;
}


.data-table__column-cell--filtered {
  color: #0f4c81;
}

.data-table__filter-indicator {
  display: inline-flex;
  width: 14px;
  height: 14px;
  margin-left: 6px;
  vertical-align: middle;
  color: #0f4c81;
}

.data-table__filter-indicator .data-table__menu-icon-svg {
  width: 14px;
  height: 14px;
}


/* v2.1 filter follow-up: remove split-corner experiment and restore one merged corner cell */
.data-table__corner-cell {
  vertical-align: middle;
}

.data-table__column-row > .data-table__corner-cell,
.data-table__corner-cell--bottom {
  display: none !important;
}

.data-table__group-row > .data-table__corner-cell[rowspan="2"] {
  position: sticky !important;
  left: 0 !important;
  top: 0 !important;
  z-index: 20 !important;
  min-width: 64px;
  width: 64px;
  max-width: 64px;
  /* 26.1.0.68: see base .data-table__corner-cell — whole column white. */
  background: #ffffff !important;
  border-right: 1px solid #cbcbcb;
  border-bottom: 1px solid #cbcbcb;
}

/* Invisible in-flow corner cell — kept in the table only for column-0
   width/alignment. Content is rendered by .data-table__corner-overlay
   below, which is a regular <div> where position:sticky works on both
   axes (the <th rowspan=2> has Chromium bug crbug 702927). */
.data-table__corner-cell--invisible {
  /* Hide content but reserve the cell's column-0 space. Still sticky
     via the rule above — stays aligned with scrolling so adjacent
     header cells don't reflow. */
  visibility: hidden;
}

/* Floating corner overlay. First child of .data-table-scroll. Sits as
   an absolutely-sized sticky block at the scroll origin, so it pins
   reliably on both axes without the rowspan=<th> bug.

   position:sticky on a DIV works for both horizontal and vertical axes
   with no quirks — unlike sticky on <th rowspan=2> which Chromium has
   known rendering bugs for (crbug 702927).

   The overlay is the first child of the scroll container, but has
   width:0 via self-clipping so it doesn't push the <table> down.
   Instead its inner button is sized + painted. Inner button has the
   real 90x57 footprint, rendered at the overlay's origin. */
.data-table__corner-overlay {
  position: sticky;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  z-index: 30;
  pointer-events: auto;
}

.data-table__corner-overlay > .data-table__corner-inner {
  /* Paint-only box at the overlay's sticky origin. Width/height match
     the table's column-0 cell: 64px wide (utility column, v86z
     narrowed from 90px after the per-row edit pencil was removed in
     v86y), 57px tall (two 28px header rows + borders). JS remeasures
     the real height post-render in case of font/zoom variance.

     v86y: when rowActions.showPen is true the inner becomes a column
     so we can stack a top row (settings + sort) and a bottom row
     (Edit button) matching the two header rows. The --two-rows
     modifier gates that layout; without it the inner keeps its
     legacy single-row flex (settings + optional sort). */
  position: absolute;
  top: 0;
  left: 0;
  width: 64px;
  height: 57px;
  background: #f1f1f1;
  border-right: 1px solid #7a8b9e;
  border-bottom: 1px solid #7a8b9e;
  box-sizing: border-box;
  display: flex;
  flex-direction: row;
  align-items: stretch;
  justify-content: space-around;
  padding: 0 4px;
  gap: 2px;
}

.data-table__corner-overlay > .data-table__corner-inner--two-rows {
  flex-direction: column;
  padding: 0;
  gap: 0;
}

/* Single-row layout (legacy / read-only): the top row fills the whole
   overlay. Pad matches the previous padding on the inner itself. */
.data-table__corner-inner > .data-table__corner-row {
  display: flex;
  flex-direction: row;
  align-items: stretch;
  justify-content: space-around;
  gap: 2px;
  min-height: 0;
  padding: 0 4px;
  flex: 1 1 50%;
}

/* Two-row layout: each row takes half the 57px height; a thin divider
   between them mirrors the column-header / group-header separator. */
.data-table__corner-inner--two-rows > .data-table__corner-row--top {
  border-bottom: 1px solid #d0d0d0;
}
.data-table__corner-inner--two-rows > .data-table__corner-row--bottom {
  justify-content: center;
}

/* The Edit button fills the bottom row so its tap target is large
   enough on touch devices. v86z: dropped max-width cap — the column
   is now 64px and the button should use the full row width. */
.data-table__corner-button--edit {
  width: 100%;
}
.data-table__corner-button--edit[disabled] {
  opacity: 0.35;
  cursor: not-allowed;
}
/* v86z: corner edit pen was previously sized to 22x22 to read
   stronger when standing alone on its own row. 26.1.0.73:
   standardised back to 16×16 per user request — the corner
   buttons should be aligned in size to the toolbar action buttons
   (info, export, inspector toggle, drill-in), so all corner +
   toolbar icons are now the same 16×16. The previous "looks
   stronger at 22" reasoning is superseded by the consistency
   request; the icon still reads clearly at 16, and the new UI5
   `edit` path geometry is denser than the old hand-drawn pencil
   so it carries presence at the smaller size. */
.data-table__corner-button--edit > .data-table__menu-icon-svg {
  width: 16px;
  height: 16px;
}

/* Override the generic .data-table__corner-button rule when rendered
   inside .data-table__corner-row. The generic rule was written when
   the corner held a single full-sized button; inside the flex row we
   want each button compact and side-by-side.
   v86z: width reduced from 38px → 26px so two of them fit in the
   narrower 64px utility column (56px available after 4px left/right
   padding = 2×26px + 4px gap). */
.data-table__corner-row > .data-table__corner-button {
  flex: 0 1 auto;
  min-height: 0;
  width: 26px;
  padding: 0;
}

.data-table__corner-button--sort {
  /* Distinguish Define Sorting with the same blue accent the column-
     sort menu item uses — signals its tie to sort state. */
  color: #1a4a87;
}

/* Override the generic red filter-count badge style for the Sort
   button: bicycle uses a small blue circle overlapping the icon's
   top-right corner. Specificity beats the generic
   `.data-table__corner-button .data-table__corner-badge` rule.

   Positioning math: the button is 38px wide × 57px tall; the 18px
   icon centres at roughly x=10..28, y=19..37. The badge sits at
   top:10px right:3px so its left-top is (21,10) and bottom-right
   (35,24) — overlapping the icon's top-right quadrant (23..28,19..24).
   That gives the visible "badge pokes out of the icon corner" look
   without needing negative offsets (which would be clipped by the
   data-table-scroll container's overflow:auto). */
.data-table__corner-button--sort .data-table__corner-badge {
  top: 10px;
  right: 3px;
  min-width: 14px;
  height: 14px;
  padding: 0 4px 0 2px;
  border-radius: 7px;
  /* 26.1.1.18: swapped with the corner-filter badge — sort takes
     the lighter #4ba3e3, filter takes the darker #1a4a87. Three
     filter-count indicators (corner badge, status-bar badge, panel
     badge) now share the dark blue; sort takes the lighter blue.
     26.1.1.19: the sort badge now also carries an inline SVG (the
     three-bar sort glyph the user supplied), so we keep the base
     rule's flex layout + gap and don't override justify-content. */
  background: #4ba3e3;
  color: #fff;
  font-size: 9px;
  line-height: 14px;
  font-weight: 700;
  border: 1px solid #fff;
  box-sizing: border-box;
}

.data-table__corner-button--filters {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border: none;
  background: transparent;
  color: #2563eb;
  cursor: pointer;
}

.data-table__corner-badge {
  position: absolute;
  top: -5px;
  right: -6px;
  min-width: 12px;
  height: 12px;
  padding: 0 3px;
  border-radius: 999px;
  background: #2563eb;
  color: #fff;
  font-size: 9px;
  line-height: 12px;
  font-weight: 700;
}

.data-table__corner-menu {
  min-width: 164px;
}

/* ══════════════════════════════════════════════════════════════════
   Column menu — submenu chevron + submenu popover (v83a)
   Sort Asc / Sort Desc items grow a right-edge chevron when another
   column is already sorted; hover/click opens a Replace / Append
   submenu. Mirrors React DataColumn.js:223-230 createSortMenuSubItems.
   ══════════════════════════════════════════════════════════════════ */

.data-table__column-menu-item--has-submenu {
  /* Parent items with a nested submenu — make room on the right for
     the chevron indicator. The base column-menu-item is display:flex
     with gap already, so we only add the chevron slot. */
  padding-right: 10px;
}

.data-table__column-menu-chevron {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: #64748b;
  width: 14px;
  height: 14px;
}

.data-table__column-menu-chevron svg {
  width: 12px;
  height: 12px;
}

.data-table__column-submenu {
  /* Same chrome as the parent menu; position:fixed set inline by
     _openSortSubmenu(). z-index stays 2000 — same stacking as the
     parent so it sits alongside, not above, per menu-ladder rules. */
  min-width: 220px;
}


/* v10 content toolbar + bottom settings/comments parity */
.kpi-chart__header {
  min-height: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 0 8px 0 10px;
  background: #eeeeee;
  border-bottom: 1px solid #bbbbbb;
}

.kpi-chart__title {
  margin: 0;
  font-size: 14px;
  font-weight: 400;
  color: #16395f;
}

.kpi-chart__toolbar,
.kpi-chart__header .content-toolbar__right {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 4px;
}

.kpi-chart__action-button,
.data-table-toolbar__action-button {
  min-width: 28px;
  width: 28px;
  height: 28px;
  min-height: 28px;
  padding: 0;
  border-radius: 4px;
}

.data-table-toolbar {
  min-height: 32px;
  padding: 2px 8px;
}

.data-table-toolbar__left,
.data-table-toolbar__right {
  align-items: center;
}

.data-table-toolbar__right {
  gap: 4px;
}

.data-table-toolbar__action-button--new {
  width: auto;
  min-width: 28px;
  /* Cap the button width so long labels like "New Finding Comments" don't
     push the QuickFind/pagination off the toolbar. The icon stays visible
     and the label truncates with an ellipsis; full text is available via
     the button's title tooltip. */
  max-width: 200px;
  padding: 0 10px;
  overflow: hidden;
  /* v85d: the New button now blends with the toolbar like every other
     toolbar button — user preference is full consistency, not a
     preserved-CTA framed variant. v85a had framed this one deliberately
     as a call-to-action; that distinction is dropped here. The coloured
     icon (via .data-table-toolbar__new-icon) and the "New" label still
     identify it as the primary action. */
}
.data-table-toolbar__action-button--new:hover {
  background: #eaf2fb;
  border-color: #b9cfe6;
}

.data-table-toolbar__new-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
  font-size: 14px;
}

.report-content-meta {
  margin-top: 12px;
  border: 1px solid #bdbdbd;
  background: #ffffff;
  flex: 0 0 auto;
  min-height: 0;
  max-height: min(40vh, 320px);
  overflow: auto;
}

/* v83k: collapsed modifier — only the header strip remains visible.
   The body is hidden (flex redistribution reclaims the space for the
   table above), the redundant header border-bottom is dropped so it
   doesn't double up with the panel's own bottom border, and the
   scroll/height caps that only matter for the expanded body are
   relaxed so a collapsed panel never shows a stray scrollbar. */
.report-content-meta--collapsed {
  max-height: none;
  overflow: visible;
}

.report-content-meta--collapsed > .report-content-meta__body {
  display: none;
}

/* v962: hide the collapsed panel's header strip entirely. Previously
   the collapsed state still rendered a "Settings / Comments" bar with
   its own ^ chevron — now that the analysis-page status bar carries
   an expand chevron (see .analysis-status-bar__toggle), the panel's
   own collapsed-state header is redundant. When uncollapsed the
   header renders normally (its own v chevron handles collapsing
   back). Was `border-bottom: 0` in v83k; fully hidden in v962. */
.report-content-meta--collapsed > .report-content-meta__header {
  display: none;
}

.report-content-meta__header {
  min-height: 26px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 0 8px;
  /* v83n: unified chrome-strip grey — see .subheading-tabs. The dark
     navy text (#173a63) has ample contrast against #f1f1f1 to stay
     readable in bold uppercase. */
  background: #f1f1f1;
  color: #173a63;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.01em;
  border-bottom: 1px solid #bdbdbd;
  /* v914: removed text-transform:uppercase — the JS title string is
     already "Settings / Comments" in mixed case, and the all-caps
     rendering competed visually with the uppercase-by-convention
     chrome-strip labels elsewhere. */
}

.report-content-meta__body {
  display: flex;
  flex-direction: column;
}

/* v83k: collapse/expand toggle replaces the v83j `×` close button.
   Same 24×24 hit box + hover treatment so the header strip feels
   unchanged, but the glyph is now a chevron that rotates 180° via
   CSS when the panel is in --collapsed state. No JS SVG mutation. */
.report-content-meta__collapse-toggle {
  width: 24px;
  height: 24px;
  border: none;
  background: transparent;
  color: #173a63;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}

.report-content-meta__collapse-toggle:hover {
  background: rgba(23, 58, 99, 0.08);
}

.report-content-meta__collapse-toggle:focus-visible {
  outline: 2px solid #4a7dbf;
  outline-offset: -2px;
}

.report-content-meta__collapse-icon {
  width: 14px;
  height: 14px;
  display: block;
  transition: transform 0.15s ease;
}

.report-content-meta--collapsed .report-content-meta__collapse-icon {
  transform: rotate(180deg);
}

.report-content-meta__section + .report-content-meta__section {
  border-top: 1px solid #d8e3ee;
}

.report-content-meta__section-header {
  min-height: 28px;
  display: flex;
  align-items: center;
  padding: 0 10px;
  background: #efefef;
  color: #173a63;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.02em;
  border-bottom: 1px solid #dde6ef;
  /* v917.1: no text-transform — JS already emits "Comments / Insights"
     in title case. Matches v914's removal of uppercase from
     .report-content-meta__header ("Settings / Comments"). */
}

.report-content-meta__table {
  display: flex;
  flex-direction: column;
}

.report-content-meta__settings {
  display: flex;
  flex-direction: column;
}

.report-content-meta__row {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: 0;
  align-items: stretch;
  border-bottom: 1px solid #FFFFFF;
}

.report-content-meta__label,
.report-content-meta__value {
  font-size: 12px;
  line-height: 1.4;
  padding: 5px;
}

.report-content-meta__label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 600;
  color: #173a63;
  background-color: #BBDDFF;
  border-right: 1px solid #FFFFFF;
}

.report-content-meta__value {
  color: #243b53;
  background-color: #DDEEFF;
  white-space: pre-wrap;
}

.report-content-meta__icon,
.report-content-meta__icon-image {
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
}

.report-content-meta__comments {
  padding: 12px 10px;
  font-size: 13px;
  line-height: 1.5;
  color: #243b53;
  background: #f8fbff;
}

.report-content-meta__text {
  white-space: normal;
}

.report-content-meta__comments p:first-child {
  margin-top: 0;
}

.report-content-meta__comments p:last-child {
  margin-bottom: 0;
}

/* ── Export Report Dialog ──────────────────────────────────────── */
.erd-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.45);
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
}
.erd-dialog {
  position: relative;
  background: #fff;
  border-radius: 6px;
  box-shadow: 0 8px 32px rgba(0,0,0,0.22);
  min-width: 520px;
  max-width: 600px;
  width: 90vw;
  max-height: 85vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.erd-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid #dddddd;
  background: #f1f1f1;
}
.erd-title { font-size: 15px; font-weight: 600; color: #18324d; }
.erd-close {
  background: none; border: none; font-size: 16px; cursor: pointer;
  color: #666; padding: 2px 6px; border-radius: 3px;
}
.erd-close:hover { background: #e0e4ea; color: #18324d; }
.erd-toolbar {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 18px; border-bottom: 1px solid #dddddd;
  background: #fafbfc; flex-wrap: wrap;
}
.erd-select {
  height: 32px; border: 1px solid #ccc; border-radius: 4px;
  padding: 0 8px; font-size: 13px; font-family: inherit;
  background: #fff; cursor: pointer; min-width: 100px;
}
.erd-fav-label {
  display: flex; align-items: center; gap: 5px;
  font-size: 13px; color: #444; cursor: pointer; margin-left: auto;
}
.erd-tree { flex: 1; overflow-y: auto; padding: 8px 0; }
.erd-loading { padding: 24px; text-align: center; color: #888; font-size: 13px; }
.erd-row {
  display: flex; align-items: center; gap: 6px;
  padding-top: 3px; padding-bottom: 3px; padding-right: 12px;
}
.erd-row:hover { background: #f4f6f8; }

/* v84y: expand/collapse chevron on rows with children. Matches the
   filter-tree chevron: right-pointing arrow when collapsed, rotated
   90° down when expanded. Leaf rows use the .erd-toggle--spacer
   variant to keep labels aligned. */
.erd-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
  color: #6a7384;
  cursor: pointer;
  border-radius: 3px;
  transition: color 120ms ease;
}
.erd-toggle--spacer { cursor: default; pointer-events: none; }
.erd-toggle:hover { color: #2860a8; background: rgba(42, 109, 179, 0.08); }
.erd-chevron {
  width: 12px;
  height: 12px;
  display: block;
  transition: transform 120ms ease;
}
.erd-chevron--open { transform: rotate(90deg); }

.erd-check {
  background: none; border: none; cursor: pointer;
  padding: 0; flex-shrink: 0;
  width: 20px; height: 20px;
  display: inline-flex; align-items: center; justify-content: center;
  line-height: 1;
}
.erd-check--checked  { color: #1a4a87; }
.erd-check--partial  { color: #1a4a87; }
.erd-check--unchecked { color: #aab4be; }
.erd-check:hover { opacity: 0.75; }
.erd-icon { width: 16px; height: 16px; flex-shrink: 0; object-fit: contain; }
.erd-label {
  font-size: 13px; color: #222;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.erd-footer {
  display: flex; justify-content: flex-end; gap: 8px;
  padding: 12px 18px; border-top: 1px solid #dddddd; background: #f1f1f1;
}
.erd-btn {
  height: 34px; padding: 0 20px; border: 1px solid #ccc;
  border-radius: 4px; font-size: 13px; font-family: inherit;
  cursor: pointer; background: #fff; color: #333;
}
.erd-btn:hover { background: #f0f2f5; }
.erd-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.erd-btn--primary {
  background: var(--brand-dark, #1a5276);
  color: #fff; border-color: var(--brand-dark, #1a5276);
}
.erd-btn--primary:hover { background: #154360; }
.erd-busy {
  position: absolute; inset: 0; background: rgba(255,255,255,0.7);
  display: flex; align-items: center; justify-content: center; border-radius: 6px;
}
.erd-spinner {
  width: 32px; height: 32px; border: 3px solid #dddddd;
  border-top-color: var(--brand-dark, #1a5276);
  border-radius: 50%; animation: erd-spin 0.7s linear infinite;
}
@keyframes erd-spin { to { transform: rotate(360deg); } }


.content-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.content-toolbar__left,
.content-toolbar__right {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
}

.content-toolbar__left {
  flex: 1 1 auto;
}

.content-toolbar__title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.content-toolbar__action-button {
  min-width: 30px;
  min-height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  /* v85b: this is the CANONICAL toolbar button class — emitted by
     renderToolbarButton() in content-toolbar.js and applied to every
     icon button in the data-table and kpi-chart toolbars (export,
     info, full-screen, meta-toggle, display-toggle, bundle menu,
     etc.). v85a only updated the per-surface specialisations
     (.data-table-toolbar__action-button, .kpi-chart__action-button)
     — but THIS base rule kept its hard white frame and won the
     specificity battle for buttons emitted via renderToolbarButton.
     The result was visible in the screenshot: the download (export)
     button still showed a blue frame while the meta-toggle and
     others around it were flat. Applying the blend treatment here
     fixes every call site at once. */
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: #1c56a4;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}

.content-toolbar__action-button:hover {
  background: #eaf2fb;
  border-color: #b9cfe6;
}

/* See matching kpi-chart rule — `display: inline-flex` would otherwise
   defeat the UA `[hidden]` (display:none) behaviour. */
.content-toolbar__action-button[hidden] {
  display: none;
}

.content-toolbar__action-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* 26.1.0.75: was 14×14, with --export overridden to 18×18.
   Standardised to 16×16 per user request — same target as the
   data-table corner/toolbar icons (per .73). The --export
   modifier rule below is collapsed to also be 16×16 so it no
   longer needs special-case sizing. The class is kept in CSS
   as a no-op hook (in case a future per-icon size tweak is
   wanted) and the .--export modifier class is still applied
   on the SVG markup so it remains targetable. */
.content-toolbar__icon-svg {
  width: 16px;
  height: 16px;
  display: block;
  flex: 0 0 16px;
}

/* 26.1.0.75: was 18×18 (v85c upsize for the old download-arrow
   glyph). Now 16×16 alongside the base — see the rationale on
   the base rule above. */
.content-toolbar__icon-svg--export {
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
}

/* Bundle (cogwheel) popup menu. Opens under the gear button on
   multi-content cells and lists Information / Export / Full screen. */
.content-toolbar__bundle-menu {
  position: absolute;
  z-index: 40;
  min-width: 160px;
  background: #fff;
  border: 1px solid var(--line, #d4dde8);
  border-radius: 6px;
  box-shadow: 0 4px 14px rgba(20, 40, 70, 0.12);
  padding: 4px 0;
  display: flex;
  flex-direction: column;
}
.content-toolbar__bundle-menu-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 12px;
  background: transparent;
  border: 0;
  font: inherit;
  font-size: 13px;
  color: var(--ink, #1a2737);
  cursor: pointer;
  text-align: left;
  width: 100%;
}
.content-toolbar__bundle-menu-item:hover {
  background: var(--soft-bg, #eef5ff);
}
/* 26.1.0.76: page-size menu item — the currently-active size
   gets a slightly stronger color and the check-icon column shows
   the tick (rendered inline in _syncPageSizeMenu). The hover
   rule above still applies on top of this. */
.content-toolbar__bundle-menu-item--active {
  color: #1a4a87;
  font-weight: 500;
}
/* Page-size dropdown trigger — inherits the pager-button base style
   (existing rules) + adds a pressed-state highlight when the menu
   is open, matching the inspector-toggle button's pressed look. */
.data-table-toolbar__pager-button--pressed,
.data-table-toolbar__pager-button--size.data-table-toolbar__pager-button--pressed {
  background: #e6eef9;
  color: #1a4a87;
}
.content-toolbar__bundle-menu-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  color: var(--brand-dark, #18324d);
}

/* ── 26.1.0.87: display-type popup menu ─────────────────────────────
   Used when a content has 3 valid display types (chart + grid +
   transposed grid). Reuses the bundle-menu chrome (same panel
   shadow, item padding, active state) so the two popups feel
   like a set. Trigger is the existing display-toggle toolbar
   button; data-display-toggle-popover-open on its parent
   button shows the menu via display:flex.
   ────────────────────────────────────────────────────────────── */
.content-toolbar__display-menu {
  position: absolute;
  /* 26.1.0.90: right-anchored — the display-toggle button sits on
     the right edge of every toolbar that hosts it (chart toolbar,
     data-table toolbar). The default left:0 (absolute children's
     fallback) opens the menu rightward and overflows the viewport
     on narrow windows + on charts whose toolbar reaches the right
     screen edge (user reported this with the 3-type menu in the
     Nelson Aalen chart). top: calc(100% + 2px) drops the menu just
     below the toolbar with a 2px gap so the panel shadow doesn't
     overlap the trigger button. */
  top: calc(100% + 2px);
  right: 0;
  left: auto;
  z-index: 40;
  min-width: 180px;
  background: #fff;
  border: 1px solid var(--line, #d4dde8);
  border-radius: 6px;
  box-shadow: 0 4px 14px rgba(20, 40, 70, 0.12);
  padding: 4px 0;
  display: none;
  flex-direction: column;
}
.content-toolbar__display-menu--open {
  display: flex;
}
.content-toolbar__display-menu-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 12px;
  background: transparent;
  border: 0;
  font: inherit;
  font-size: 13px;
  color: var(--ink, #1a2737);
  cursor: pointer;
  text-align: left;
  width: 100%;
}
.content-toolbar__display-menu-item:hover {
  background: var(--soft-bg, #eef5ff);
}
/* Active item — current display type. Same treatment as the
   page-size active row so users can tell at a glance which
   type is currently selected. */
.content-toolbar__display-menu-item--active {
  color: #1a4a87;
  font-weight: 500;
}
.content-toolbar__display-menu-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  color: var(--brand-dark, #18324d);
}
.content-toolbar__display-menu-label {
  flex: 1 1 auto;
  min-width: 0;
}
/* Pressed state for the toggle button when the popover is open — same
   treatment as the page-size dropdown trigger so the open menu has
   a clear anchor visual. */
.data-table-toolbar__action-button--display-toggle-pressed,
.kpi-chart__action-button--display-toggle-pressed {
  background: #e6eef9;
  color: #1a4a87;
}

body.route-analysis .data-table-toolbar,
body.route-analysis .kpi-chart__header {
  min-height: 32px;
  height: 32px;
  padding: 0 8px 0 10px;
  border-bottom: 1px solid var(--line);
  /* v83n: unified chrome-strip grey — see .subheading-tabs. */
  background: #f1f1f1;
}

/* ── Report Info Popup ─────────────────────────────────────────────────── */
report-info-popup {
  position: fixed;
  top: 0; left: 0;
  width: 0; height: 0;
  z-index: 2000;
  overflow: visible;
}

.rip-panel {
  position: fixed;
  min-width: 420px;
  max-width: 560px;
  background: #ffffff;
  border: 1px solid #cecece;
  border-radius: 10px;
  box-shadow: 0 8px 32px rgba(23,63,107,0.18);
  overflow: hidden;
}

.rip-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px;
  background: #f0f5fa;
  border-bottom: 1px solid #dde8f0;
}

.rip-title {
  font-size: 14px;
  font-weight: 600;
  color: #1f3b57;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.rip-close {
  background: none;
  border: none;
  font-size: 16px;
  color: #5a7a99;
  cursor: pointer;
  padding: 0 2px;
  line-height: 1;
  flex-shrink: 0;
}
.rip-close:hover { color: #18324d; }

.rip-body {
  padding: 12px 16px 14px;
}

.rip-description {
  font-size: 13px;
  color: #2d3f52;
  line-height: 1.5;
  white-space: pre-wrap;
  margin-bottom: 12px;
  padding-bottom: 12px;
  border-bottom: 1px solid #ebebeb;
}

.rip-table {
  width: 100%;
  border-collapse: collapse;
}

.rip-table tr + tr td {
  border-top: 1px solid #ebebeb;
}

.rip-label {
  padding: 7px 16px 7px 0;
  font-size: 13px;
  color: #2563a8;
  font-weight: 500;
  white-space: nowrap;
  width: 120px;
  vertical-align: top;
}

.rip-value {
  padding: 7px 0;
  font-size: 13px;
  color: #18324d;
  vertical-align: top;
}

/* ── Content Info Popup ────────────────────────────────────────────────── */
content-info-popup {
  position: fixed;
  top: 0; left: 0;
  width: 0; height: 0;
  z-index: 2000;
  overflow: visible;
}

.cip-panel {
  position: fixed;
  min-width: 400px;
  max-width: 540px;
  background: #ffffff;
  border: 1px solid #cecece;
  border-radius: 10px;
  box-shadow: 0 8px 32px rgba(23,63,107,0.18);
  overflow: hidden;
}

.cip-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px;
  background: #f0f5fa;
  border-bottom: 1px solid #dde8f0;
}

.cip-title {
  font-size: 14px;
  font-weight: 600;
  color: #1f3b57;
}

.cip-close {
  background: none;
  border: none;
  font-size: 16px;
  color: #5a7a99;
  cursor: pointer;
  padding: 0 2px;
  line-height: 1;
  flex-shrink: 0;
}
.cip-close:hover { color: #18324d; }

.cip-tabs {
  display: flex;
  border-bottom: 1px solid #dde8f0;
  padding: 0 14px;
  background: #f5f5f5;
  gap: 0;
}

.cip-tab {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 8px 14px;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  color: #6b7f96;
  cursor: pointer;
  position: relative;
  top: 1px;
}
.cip-tab:hover { color: #1f3b57; }
.cip-tab--active {
  color: #1c56a4;
  font-weight: 600;
  border-bottom-color: #1c56a4;
}

.cip-body {
  padding: 8px 0;
  max-height: 360px;
  overflow-y: auto;
}

.cip-empty {
  padding: 12px 16px;
  font-size: 13px;
  color: #6b7f96;
}

.cip-table {
  width: 100%;
  border-collapse: collapse;
}

.cip-table tr + tr td {
  border-top: 1px solid #ebebeb;
}

.cip-label {
  padding: 8px 12px 8px 14px;
  font-size: 13px;
  color: #2563a8;
  font-weight: 500;
  white-space: nowrap;
  width: 160px;
  vertical-align: top;
}

.cip-label-inner {
  display: flex;
  align-items: flex-start;
  gap: 6px;
}

.cip-value {
  padding: 8px 14px 8px 0;
  font-size: 13px;
  color: #18324d;
  vertical-align: top;
  white-space: pre-wrap;
}

.cip-row-icon {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  margin-top: 1px;
  margin-right: 5px;
}

.cip-row-icon svg { width: 13px; height: 13px; }
.cip-row-icon-img { width: 13px; height: 13px; object-fit: contain; }

/* Indicator groups */
.cip-ind-group {
  padding: 6px 14px 2px;
}

.cip-ind-group + .cip-ind-group {
  border-top: 1px solid #ebebeb;
  margin-top: 2px;
  padding-top: 8px;
}

.cip-ind-group-header {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #7a94ad;
  padding-bottom: 4px;
}

.cip-ind-list {
  list-style: none;
  margin: 0;
  padding: 0 0 6px 0;
}

.cip-ind-item {
  font-size: 13px;
  color: #18324d;
  padding: 3px 0;
}

/* =========================================================================
   Carousel page  (#/carousel/...)
   ========================================================================= */

body.route-carousel {
  overflow: hidden;
}

body.route-carousel #app-content {
  height: 100vh;
  overflow: hidden;
}

body.route-carousel .container {
  max-width: none;
  margin: 0;
  padding: 0;
}

/* Page shell */
.carousel-page {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: #fff;
}

/* ── Header bar ── */
.carousel__header {
  display: flex;
  align-items: center;
  height: 48px;
  min-height: 48px;
  padding: 0 12px 0 4px;
  background: #ffffff;
  color: #1c4f82;
  gap: 8px;
  flex-shrink: 0;
  border-bottom: 1px solid #d0d0d0;
}

.carousel__back-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 32px;
  padding: 0 12px;
  /* v84z: back button flipped to white background with brand-blue
     text+border so it doesn't dominate the top bar visually. Matches
     the pattern used elsewhere in the app (report-toolbar back
     buttons, dataview back control). */
  background: #ffffff;
  border: 1px solid #1c4f82;
  border-radius: 4px;
  color: #1c4f82;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  flex-shrink: 0;
  transition: background 0.15s, color 0.15s;
}

.carousel__back-btn:hover {
  background: #eaf2fb;
}

.carousel__back-label {
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.carousel__header-center {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  /* v84z: prev/next nav buttons now live inside this flex container
     alongside the subheading label + counter. Small gap keeps them
     visually grouped without crowding. */
  gap: 4px;
}

.carousel__subheading-label {
  font-size: 17px;
  font-weight: 600;
  color: #1c4f82;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.carousel__counter {
  font-size: 13px;
  font-weight: 400;
  color: #5b6e85;
  white-space: nowrap;
  margin-left: 6px;
  flex-shrink: 0;
}

.carousel__header-end {
  flex-shrink: 0;
  width: 100px; /* balances the back button width so title stays centred */
}

/* ── Body (content + nav arrows) ── */
.carousel__body {
  flex: 1 1 auto;
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: stretch;
}

.carousel__content {
  flex: 1;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* Content fills the carousel body */
.carousel__content kpi-chart,
.carousel__content kpi-chart .kpi-chart {
  height: 100%;
}

.carousel__content data-table,
.carousel__content data-table .data-table-shell {
  height: 100%;
}

/* ── Navigation arrows ──
   v84z: prev/next buttons now sit in the top header flanking the
   subheading label. Previously these were 48×48 circular buttons
   absolutely-positioned over the body at left/right, which overlapped
   chart / table content and were always visible at 35% opacity even
   when there was only one content to show. In-header placement keeps
   them compact, next to the "N of M" counter, and lets the body area
   stay uncluttered. */
.carousel__nav {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  cursor: pointer;
  color: #1c4f82;
  flex-shrink: 0;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}

.carousel__nav:hover {
  background: #eaf2fb;
  border-color: #b9cfe6;
}

.carousel__nav:active {
  background: #d4e5f5;
}

.carousel__nav[hidden] { display: none; }

.carousel__nav--prev {
  margin-right: 4px;
}

.carousel__nav--next {
  margin-left: 4px;
}

/* Make kpi-chart fill the carousel content area properly */
body.route-carousel .kpi-chart {
  height: 100%;
  display: flex;
  flex-direction: column;
  padding: 0;
  gap: 0;
  background: transparent;
  border: none;
  border-radius: 0;
  box-shadow: none;
}

body.route-carousel .kpi-chart__canvas {
  flex: 1 1 auto;
  min-height: 0;
  background: #ffffff;
}

body.route-carousel .kpi-chart__header {
  flex-shrink: 0;
  min-height: 32px;
  height: 32px;
  padding: 0 8px 0 10px;
  justify-content: space-between;
  background: #eeeeee;
  border-bottom: 1px solid #bbbbbb;
}

body.route-carousel .kpi-chart__title {
  display: block;
  font-size: 14px;
  font-weight: 400;
  color: #0f3b67;
}

body.route-carousel .kpi-chart__header .content-toolbar__right {
  gap: 6px;
}

body.route-carousel .data-table-shell {
  height: 100%;
  display: flex;
  flex-direction: column;
}

body.route-carousel .data-table-shell .data-table-scroll {
  flex: 1 1 auto;
  overflow: auto;
}

/* Fix 2: In the carousel, table columns should size to their content rather
   than stretching to fill the full viewport width. Removing min-width:100%
   keeps columns compact — matching the original BICycle React behaviour.
   Horizontal scrolling still works via data-table-scroll overflow:auto. */
body.route-carousel .data-table {
  min-width: 0;
}


.content-toolbar__action-label {
  white-space: nowrap;
}
.kpi-chart__context-controls {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: 8px;
}
.kpi-chart__action-button--context {
  min-width: auto;
  padding-inline: 8px;
}
.kpi-chart__action-button--context .content-toolbar__action-label {
  font-size: 12px;
}

/* ── GroupBy / SplitBy inline toolbar controls ── */
.kpi-chart__group-controls {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-right: 6px;
  padding-right: 8px;
  border-right: 1px solid var(--line, #d6dde8);
  flex-shrink: 0;
}
.kpi-chart__group-label {
  font-size: 13px;
  color: var(--muted, #5b6e85);
  white-space: nowrap;
}
.kpi-chart__group-select {
  height: 26px;
  border: 1px solid #b9cde0;
  border-radius: 4px;
  background: #ffffff;
  font: inherit;
  font-size: 13px;
  color: #18324d;
  padding: 0 4px;
  cursor: pointer;
  max-width: 160px;
}
.kpi-chart__group-select:focus {
  outline: 2px solid #2d74c4;
  outline-offset: 1px;
}


/* ══════════════════════════════════════════════════════════════════════════
   Report page — AN (tiles) and DOC (document) modes
   ══════════════════════════════════════════════════════════════════════════ */

.report-page {
  display: flex;
  flex-direction: column;
  min-height: calc(100dvh - 0px);
  background: #f2f6fb;
}

/* Top bar: home button · centered title · info/export actions. White background
   with a subtle border — this is the page's own chrome, visually distinct from
   the app-level nav.
   26.1.0.49: outer top-bar is now a full-width chrome band only —
   the actual layout (grid columns + horizontal padding) lives on
   the new `.report-page__top-bar-inner` wrapper, which mirrors
   `.report-doc`'s max-width: 1200px + auto margins. This pulls the
   right-side action buttons in to align with the document's right
   edge (and reserves the left slot for the future dataTree button
   which will appear at the document's left edge). */
.report-page__top-bar {
  display: flex;
  align-items: stretch;
  height: 56px;
  background: #ffffff;
  color: #18324d;
  border-bottom: 1px solid var(--line);
}

/* 26.1.0.65: top-bar de-centered. The .49 design constrained the
   inner wrapper to max-width:1200px + auto margins so the right-
   edge action buttons aligned with the document panel's right
   edge. Looked fine on the document view where the panel itself
   was centered, but on the tiles view (which is also flush-left
   in .65) it left a visibly-empty band on both sides of the top
   bar. Flipped to flush-left: the inner wrapper now spans the
   full top-bar width with the same horizontal padding (16px)
   the content area uses, so the title and action buttons line
   up with the tile grid below. */
.report-page__top-bar-inner {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 0 16px;
}

.report-page__top-bar-left {
  justify-self: start;
  /* 26.1.0.55: row of context buttons (dataTree, filter, time-window)
     when the report has filterSettings configured. inline-flex with
     small gaps gives a compact toolbar look in the empty slot. */
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-wrap: nowrap;
  min-width: 0;
}

/* 26.1.0.55: context buttons in the report-page top-bar-left. Same
   chrome as the .rtb-back-to-report button (transparent until
   hover, accent-blue on hover) so the left edge of the top bar
   reads as one row of left-aligned controls when the analysis-page
   user navigates back. Icon + truncating label. */
.report-context-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 10px 0 8px;
  height: 32px;
  border: 1px solid #b8c4d2;
  background: #ffffff;
  color: var(--brand-dark);
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  line-height: 1;
  border-radius: 6px;
  cursor: pointer;
  max-width: 220px;
  transition: background-color 120ms ease, border-color 120ms ease;
}
.report-context-btn:hover {
  background: rgba(42, 109, 179, 0.08);
  border-color: #8fa9c6;
}
.report-context-btn:focus-visible {
  outline: 2px solid #2a6db3;
  outline-offset: -2px;
}
.report-context-btn__icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  display: block;
  color: #2a6db3;
}
/* 26.1.0.154: when a domain has an active count (e.g. filter
   button with N applied filters), the icon is wrapped so a small
   numeric badge can sit on its top-right corner. Mirrors the
   React app's filter-button affordance (small blue circle with
   white digit overlaid on the funnel). */
.report-context-btn__icon-wrap {
  position: relative;
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.report-context-btn__icon-wrap .report-context-btn__icon {
  width: 100%;
  height: 100%;
}
.report-context-btn__badge {
  position: absolute;
  top: -6px;
  right: -8px;
  min-width: 14px;
  height: 14px;
  padding: 0 3px;
  border-radius: 7px;
  background: #2a6db3;
  color: #ffffff;
  font-size: 10px;
  font-weight: 600;
  line-height: 14px;
  text-align: center;
  pointer-events: none;
  box-shadow: 0 0 0 1.5px #ffffff;
  letter-spacing: 0;
}
.report-context-btn__label {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

/* 26.1.0.55: info-message banner. Soft blue background (matches
   the active-report highlight in the panel for visual continuity)
   with an info icon, the message text, and a dismiss × button.
   Sits at the top of #report-page-content so it appears above
   either the document body or the tile grid. */
.report-page__info-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 16px;
  /* 26.1.0.65: was max-width:1200px + margin:0 auto. Dropped to
     match the flush-left layout the rest of the report-page
     content uses now (top-bar title and tile grid, also
     de-centered in .65). The banner spans the full width of its
     container; the surrounding .report-page__content padding
     supplies the gutters. */
  padding: 10px 14px;
  background: #cfe1f7;
  color: #18324d;
  border: 1px solid #9bb8d8;
  border-radius: 4px;
  font-size: 13.5px;
}
.report-page__info-banner__icon {
  display: inline-flex;
  flex-shrink: 0;
  color: #2a6db3;
}
.report-page__info-banner__text {
  flex: 1 1 auto;
  min-width: 0;
}
.report-page__info-banner__close {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  border: 0;
  background: transparent;
  color: inherit;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  border-radius: 3px;
  transition: background-color 120ms ease;
}
.report-page__info-banner__close:hover {
  background: rgba(42, 109, 179, 0.16);
}

/* 26.1.0.55: floating popover shell anchored to a context button.
   Lives in document.body (the report-context-popover module
   appends it there directly so it can escape any clipping
   ancestor in the report-page top bar). Width fits a typical
   data-tree / filter-tree comfortably; tall trees scroll
   internally via the body's overflow:auto. */
.report-context-popover {
  position: fixed;
  width: min(420px, 90vw);
  max-height: min(560px, 80vh);
  display: flex;
  flex-direction: column;
  background: #ffffff;
  border: 1px solid #b8b8b8;
  border-radius: 6px;
  box-shadow: 0 8px 24px rgba(16, 36, 64, 0.18);
  z-index: 1100;
}
.report-context-popover__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 10px 12px;
  border-bottom: 1px solid #e1e1e1;
  font-weight: 600;
  color: #18324d;
}
.report-context-popover__close {
  border: 0;
  background: transparent;
  font-size: 18px;
  width: 24px;
  height: 24px;
  cursor: pointer;
  color: inherit;
  border-radius: 3px;
}
.report-context-popover__close:hover {
  background: rgba(42, 109, 179, 0.10);
}
.report-context-popover__body {
  flex: 1 1 auto;
  overflow: auto;
  padding: 8px 8px;
  min-height: 120px;
}
.report-context-popover__body--tree {
  /* The mountDataTree / mountFilterTree views render their own
     row chrome — keep the popover body padding minimal so their
     full width shows. */
  padding: 4px;
}
.report-context-popover__footer {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 8px 12px;
  border-top: 1px solid #e1e1e1;
}
.report-context-popover__btn {
  height: 28px;
  padding: 0 12px;
  border-radius: 4px;
  border: 1px solid #b8c4d2;
  background: #ffffff;
  color: var(--brand-dark);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.report-context-popover__btn:hover {
  background: #eaf2fb;
  border-color: #8fa9c6;
}
.report-context-popover__btn--primary {
  background: #2a6db3;
  border-color: #2a6db3;
  color: #ffffff;
}
.report-context-popover__btn--primary:hover {
  background: #1f5da8;
  border-color: #1f5da8;
}
.report-context-popover__btn--ghost {
  background: transparent;
  border-color: transparent;
  color: #2a6db3;
}
.report-context-popover__btn--ghost:hover {
  background: rgba(42, 109, 179, 0.10);
  border-color: transparent;
}

/* 26.1.0.65: was justify-self:center per the .49 grid layout
   (1fr auto 1fr) which centered the title between two equal
   side gutters. Now flush-left to match the de-centered tile
   view; the title sits at the left of the middle grid column,
   which itself flexes to fill space between the left-slot and
   the right-action buttons. The flex column will be much wider
   than the title text, so justify-content:flex-start inside is
   what produces the visible left-flush. */
.report-page__top-bar-center {
  justify-self: start;
  min-width: 0;
  max-width: 100%;
  display: flex;
  justify-content: flex-start;
}
.report-page__top-bar-right {
  justify-self: end;
}

.report-page__back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 58px;
  height: 36px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: #ffffff;
  color: var(--brand-dark);
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.report-page__back:hover { background: var(--soft-bg); border-color: #b9cde0; }
.report-page__back svg { display: block; }

.report-page__title {
  font-size: 16px;
  font-weight: 700;
  color: var(--brand-dark);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: block;
  max-width: 100%;
}

.report-page__actions {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
/* Buttons reuse the .rtb-action-btn styling already defined for the analysis
   toolbar, so info / export look identical across both pages. */

/* Content area wraps either the tile host or the document TOC */
.report-page__content {
  flex: 1 1 auto;
  /* 26.1.0.56: bottom padding bumped from 16px to 40px (16px
     normal padding + 24px to clear the fixed status bar that now
     hovers over the viewport bottom). Without this the very last
     row of content (a heading bar at the end of a long document,
     or the bottom edge of a tile grid) would scroll under the
     status bar instead of resting just above it. */
  padding: 16px 16px 40px;
  min-height: 0;
  overflow: auto;
}

.report-page__content--tiles .report-tiles {
  /* 26.1.0.65: was max-width:1200px + margin:0 auto per .50 to
     center the tile column inside the content area. Looked fine
     when every row had 4+ tiles, but rows with 1-2 tiles read as
     "lonely cluster floating in empty space" because the centered
     column itself sat in a wide empty surround on big viewports
     (see user's image 1). Flipped to flush-left to match the
     Windows app's approach (user's image 2): tiles flow from the
     content area's left padding, rows wrap when they run out of
     room, and short rows just look like short rows instead of
     adrift. The .report-page__content padding (16px sides)
     supplies the gutter. Background stays transparent (the parent
     .report-page__content--tiles paints #f0f0f0 panel grey). */
  width: 100%;
  min-height: 320px;
}

/* ── Document TOC ── */
.report-page__content--document {
  /* 26.1.0.49: was #ffffff. Flipped to the same #f0f0f0 chrome grey
     as the reports panel so the panel and document area read as one
     continuous surface. The document content (heading bars at
     #f0f5fb / #e9f1fa) still has enough contrast against this grey
     to read cleanly — the bars are tinted blue, not grey-on-grey. */
  background: #f0f0f0;
}

.report-doc {
  max-width: 1200px;
  margin: 0 auto;
  /* 26.1.0.50: white background on the central document panel only,
     leaving the side gutters (the parent .report-page__content--
     document at #f0f0f0) showing the panel-grey chrome. The
     document reads as paper centred on a grey page surround. The
     horizontal padding gives heading bars + body text a visible
     gutter from the white panel edge so content doesn't run into
     the corners. Vertical padding kept as the section gap that
     was already implicit in .report-page__content's padding. */
  padding: 16px 16px;
  background: #ffffff;
  /* Hairline border so the white edge against the grey chrome
     is crisp, matching the v938 sidebar separator colour. */
  border: 1px solid #b8b8b8;
  border-radius: 4px;
}

.doc-heading + .doc-heading { margin-top: 6px; }

.doc-heading__bar {
  background: #f0f5fb;
  border: 1px solid #dddddd;
  border-radius: 6px;
  padding: 0 8px;
}
.doc-heading__bar--level-1 {
  background: #e9f1fa;
  margin-top: 8px;
}
.doc-heading__bar--level-2 {
  background: #f4f8fc;
  margin-top: 4px;
  margin-left: 12px;
}

.doc-heading__toggle {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 10px 6px;
  border: 0;
  background: transparent;
  color: #18324d;
  text-align: left;
  cursor: pointer;
  font: inherit;
  font-weight: 600;
}
.doc-heading__bar--level-2 .doc-heading__toggle { font-weight: 500; }

.doc-heading__chevron {
  flex-shrink: 0;
  width: 16px;
  display: inline-block;
  color: var(--muted);
  text-align: center;
}

.doc-heading__title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.doc-heading__children {
  padding: 2px 0 4px;
}

.doc-heading__body {
  margin: 8px 0 12px 28px;
  padding: 16px 18px;
  border: 1px dashed #c9d7e4;
  border-radius: 8px;
  background: #fafcff;
}

.doc-heading__stub-text {
  margin: 0 0 10px;
  color: var(--muted);
  font-size: 13.5px;
}

.doc-heading__open-analysis {
  border: 1px solid var(--line);
  background: #ffffff;
  color: var(--brand-dark);
  font-weight: 600;
  padding: 6px 12px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 13px;
}
.doc-heading__open-analysis:hover { background: var(--soft-bg); }

/* ── Settings bar filter popover ── */
.report-page__popover {
  position: fixed;
  z-index: 2000;
  min-width: 280px;
  max-width: 360px;
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 14px 34px rgba(16,36,64,0.22);
  color: #18324d;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* Tree popovers (dataTree / filterTree) are wider and scrollable */
.report-page__popover--tree {
  width: min(420px, calc(100vw - 24px));
  max-width: 420px;
  max-height: min(560px, calc(100vh - 120px));
}

.report-page__popover-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px;
  background: var(--soft-bg);
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
}
.report-page__popover-title {
  font-weight: 700;
  font-size: 14px;
  color: var(--brand-dark);
}
.report-page__popover-close {
  border: 0;
  background: transparent;
  font-size: 18px;
  line-height: 1;
  padding: 2px 8px;
  color: var(--muted);
  cursor: pointer;
  border-radius: 4px;
}
.report-page__popover-close:hover { background: rgba(16,36,64,0.08); color: var(--brand-dark); }

.report-page__popover-body {
  padding: 14px;
  font-size: 13.5px;
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
}
.report-page__popover-body--tree {
  padding: 0;
}
.report-page__popover-tree-host {
  padding: 8px 6px;
  min-height: 200px;
}

.report-page__popover-footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  padding: 10px 14px;
  border-top: 1px solid var(--line);
  background: #fbfcfe;
  flex-shrink: 0;
}
.report-page__popover-btn {
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  padding: 6px 14px;
  border-radius: 6px;
  border: 1px solid var(--line);
  background: #ffffff;
  color: var(--brand-dark);
  cursor: pointer;
}
.report-page__popover-btn:hover { background: var(--soft-bg); }
.report-page__popover-btn--primary {
  background: var(--brand-dark);
  border-color: var(--brand-dark);
  color: #ffffff;
}
.report-page__popover-btn--primary:hover { background: #1f4770; }

.report-page__popover-lede {
  margin: 0 0 8px;
  color: #18324d;
}
.report-page__popover-note {
  margin: 4px 0;
  color: var(--muted);
}
.report-page__popover-note code {
  background: var(--soft-bg);
  padding: 1px 6px;
  border-radius: 3px;
  font-size: 12.5px;
}

/* 26.1.0.45: removed the previous `body.route-report { padding-left: 0 !important; }`
   + `body.route-report .reports-panel { display: none !important; }`
   block. That rule was added when the report route deliberately had
   no left rail — its job was to suppress any leftover panel chrome
   from a prior home navigation. The 26.1.0.45 flow pins the reports
   panel on /report, so this rule has to go: it was overriding the
   `body.has-reports-rail` padding/transform rules from line ~1059
   and forcing the panel to display:none regardless of the rail state.
   Removed entirely (rather than negated) so there is no surviving
   route-report-specific override; the standard has-reports-rail
   rules now apply uniformly to /home and /report. */


/* ══════════════════════════════════════════════════════════════════════════
   Inline connected panel (Turn B)
   Renders a nested <data-table> below the main table when a non-text
   connected tab is active. Shares height/splitter with the text panel.
   ══════════════════════════════════════════════════════════════════════════ */

.data-table__panel-loading,
.data-table__panel-error {
  padding: 24px;
  color: var(--muted);
  text-align: center;
  font-size: 13.5px;
}
.data-table__panel-error { color: #b02a37; }

.data-table__tab-panel--connected {
  display: flex;
  flex-direction: column;
  height: var(--text-panel-height, 260px);
  min-height: 0;
  overflow: hidden;
  background: #ffffff;
  /* Lock the panel at its declared height so the main-table scroll
     (flex: 1 1 auto, content-sized flex-basis) doesn't cause the panel
     to shrink via flex-shrink: 1. Without this, height: 260px becomes
     a basis hint rather than a floor, and the panel renders at half-size
     when the main table has many rows. */
  flex-shrink: 0;
}
.data-table__tab-panel--connected > data-table[data-connected-subtable] {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  height: 100%;
}

/* Nested sub-table lives inside the panel. It doesn't have its own tab
   strip or breadcrumb — those are scoped to the outer table. We also trim
   some of its chrome to make the inline context read more like a detail
   view than a full page. */
.data-table-shell--sub .data-table__path { display: none; }
.data-table-shell--sub .data-table__connected-tabs { display: none; }
.data-table-shell--sub .data-table__text-splitter { display: none; }
.data-table-shell--sub {
  border: 0;
  border-radius: 0;
  overflow: hidden;
  /* Break out of the parent .data-table-shell caps so the shell fills the
     flex-sized panel instead of asserting a viewport-relative height. */
  max-height: none !important;
  min-height: 0 !important;
  flex: 1 1 auto !important;
  display: flex !important;
  flex-direction: column !important;
}
/* The scroll region inside the sub-shell should grow to fill available space
   and enable its own vertical scrollbar when rows overflow. */
.data-table-shell--sub .data-table-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
}
.data-table__tab-panel--connected {
  padding: 0;
}

/* ══════════════════════════════════════════════════════════════════════════
   Photo panel — thumbnail grid + lightbox
   Mounted inside .data-table__tab-panel--connected when a Photo-kind tab is
   active. Matches the bicycle React PhotoConnectedControl layout: a 3-up
   grid on desktop that scales down on narrow viewports, each tile showing
   a preview image with an optional description caption. Clicking a tile
   opens a full-viewport overlay.
   ════════════════════════════════════════════════════════════════════════ */

photo-panel {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
}

.photo-panel {
  padding: 12px 16px 16px;
  flex: 1 1 auto;
  min-height: 0;
}

.photo-panel--empty {
  display: flex;
  align-items: center;
  justify-content: center;
  color: #6a7a8f;
  font-size: 13px;
  padding: 24px;
}

.photo-panel__empty-message {
  margin: 0;
}

.photo-panel__section-title {
  margin: 12px 0 6px;
  font-size: 12px;
  font-weight: 600;
  color: #4a5a70;
  text-transform: uppercase;
  letter-spacing: 0.4px;
}

.photo-panel__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}

@media (max-width: 880px) {
  .photo-panel__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 520px) {
  .photo-panel__grid { grid-template-columns: 1fr; }
}

.photo-panel__tile {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
  background: #f4f4f4;
  border: 1px solid #e2e2e2;
  border-radius: 6px;
  overflow: hidden;
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.photo-panel__tile:hover {
  border-color: #3f78ad;
  box-shadow: 0 2px 6px rgba(63,120,173,0.15);
}

.photo-panel__thumb-wrap {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  background: #ececec;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.photo-panel__thumb {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.photo-panel__thumb-placeholder,
.photo-panel__thumb-error {
  font-size: 12px;
  color: #6a7a8f;
}
.photo-panel__thumb-error {
  color: #a33;
}

.photo-panel__caption {
  padding: 4px 8px 8px;
  font-size: 12px;
  color: #2b3d57;
  line-height: 1.35;
  /* Clamp long descriptions so tiles stay even-sized. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Lightbox overlay — full viewport dim + centred image + close button. */
.photo-panel__lightbox {
  position: fixed;
  inset: 0;
  background: rgba(12, 22, 40, 0.82);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1200;
  padding: 32px;
}

.photo-panel__lightbox-close {
  position: absolute;
  top: 16px;
  right: 20px;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 0;
  background: rgba(255, 255, 255, 0.15);
  color: #fff;
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.photo-panel__lightbox-close:hover { background: rgba(255, 255, 255, 0.28); }

.photo-panel__lightbox-stage {
  max-width: 92vw;
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

.photo-panel__lightbox-image {
  max-width: 100%;
  max-height: 80vh;
  object-fit: contain;
  background: #0c1628;
  border-radius: 4px;
}

.photo-panel__lightbox-caption {
  color: #d7e1f0;
  font-size: 13px;
  text-align: center;
  max-width: 80ch;
}

.photo-panel__lightbox-loading {
  color: #d7e1f0;
  font-size: 14px;
}

/* ══════════════════════════════════════════════════════════════════════════
   Comments panel — stacked comment list
   Mounted inside .data-table__tab-panel--connected when a Comments-kind tab
   is active. Layout is a scrollable column: header with an optional
   "+ Add Comment" button, followed by a list of comment cards. Each card
   carries sanitised HTML body + attribution footer + (conditionally) a
   delete button and/or paperclip icon.
   ════════════════════════════════════════════════════════════════════════ */

comments-panel {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
}

.comments-panel {
  display: flex;
  flex-direction: column;
  padding: 8px 16px 16px;
  flex: 1 1 auto;
  min-height: 0;
}

.comments-panel__toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 0 10px;
  border-bottom: 1px solid #e2e2e2;
  margin-bottom: 10px;
}

.comments-panel__new-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border: 1px solid #3f78ad;
  border-radius: 4px;
  background: #fff;
  color: #3f78ad;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.comments-panel__new-btn:hover {
  background: #3f78ad;
  color: #fff;
}

.comments-panel__new-icon {
  font-size: 16px;
  line-height: 1;
  font-weight: 600;
}

.comments-panel__empty {
  padding: 16px;
  color: #6a7a8f;
  font-size: 13px;
  font-style: italic;
}

.comments-panel__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.comments-panel__item {
  background: #ffffff;
  border: 1px solid #e2e2e2;
  border-radius: 6px;
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.comments-panel__item-body {
  font-size: 13px;
  line-height: 1.5;
  color: #1d2d44;
  /* Bicycle uses DOMPurify to render sanitised rich text; our
     sanitizeRichTextHtml does the same, so we need to allow common
     inline elements to render naturally inside the card body. */
  word-wrap: break-word;
  overflow-wrap: anywhere;
}
.comments-panel__item-body p { margin: 0 0 4px; }
.comments-panel__item-body p:last-child { margin-bottom: 0; }
.comments-panel__item-body ul,
.comments-panel__item-body ol { margin: 4px 0 4px 20px; padding: 0; }
.comments-panel__item-body a { color: #3f78ad; }

.comments-panel__item-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  font-size: 11px;
  color: #6a7a8f;
}

.comments-panel__attribution {
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.3px;
}

.comments-panel__item-actions {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.comments-panel__doc-btn,
.comments-panel__delete-btn {
  width: 24px;
  height: 24px;
  border-radius: 4px;
  border: 0;
  background: transparent;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: #4a5a70;
  transition: background 0.15s ease, color 0.15s ease;
}
.comments-panel__doc-btn:hover { background: #ececec; color: #3f78ad; }
.comments-panel__delete-btn {
  font-size: 18px;
  line-height: 1;
  color: #a33;
}
.comments-panel__delete-btn:hover { background: #fae4e4; }


/* ══════════════════════════════════════════════════════════════════════════
   DataView page (Phase 2) — single-record read-only view + record list
   Route: #/dataview/<datamartId>/<reportId>/<contentId>/<rowId>
   Layout:
     data-view-page              (flex column, full viewport)
       data-view-top-bar         (back + title + export; 44px)
       data-view-split           (2-col grid: list + body)
         data-view-list          (left panel: search + rows + footer)
         data-view-body          (scroll region with cards)
   ══════════════════════════════════════════════════════════════════════════ */

.data-view-page {
  display: flex;
  flex-direction: column;
  height: 100vh;
  min-height: 0;
  background: #ffffff;
}

/* Top bar — bicycle-style: white background, dark icons, centered title */
.data-view-top-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 44px;
  padding: 0 8px;
  background: #ffffff;
  color: var(--ink, #1a2737);
  border-bottom: 1px solid var(--line, #d7d7d7);
  flex: 0 0 auto;
}

.data-view-back {
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  color: #2860a8;
  border-radius: 6px;
  cursor: pointer;
  padding: 0;
}
.data-view-back:hover { background: #eef5ff; }
.data-view-back:focus-visible { outline: 2px solid #2860a8; outline-offset: -2px; }

.data-view-top-bar__title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 14.5px;
  font-weight: 600;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  padding: 0 8px;
  color: var(--ink, #1a2737);
}

.data-view-export {
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  color: #2860a8;
  border-radius: 6px;
  cursor: pointer;
  padding: 0;
}
.data-view-export:hover { background: #eef5ff; }
.data-view-export:focus-visible { outline: 2px solid #2860a8; outline-offset: -2px; }

/* Split layout */
.data-view-split {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: 320px 1fr;
  overflow: hidden;
}
@media (max-width: 720px) {
  /* Mobile: hide the list by default — form only. A future phase adds a
     toggle to bring the list back. */
  .data-view-split { grid-template-columns: 1fr; }
  .data-view-list  { display: none; }
}

/* Record list (left panel) */
.data-view-list {
  display: flex;
  flex-direction: column;
  min-height: 0;
  border-right: 1px solid var(--line, #d7d7d7);
  background: #ffffff;
}

.data-view-list__search-wrap {
  padding: 10px 12px 6px;
  flex: 0 0 auto;
}
.data-view-list__search {
  width: 100%;
  height: 32px;
  padding: 0 10px;
  border: 1px solid var(--line, #d7d7d7);
  border-radius: 6px;
  font-size: 13px;
  background: #ffffff;
  color: var(--ink, #1a2737);
  box-sizing: border-box;
}
.data-view-list__search:focus {
  outline: 2px solid #2860a8;
  outline-offset: -2px;
  border-color: #2860a8;
}

.data-view-list__scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 4px 0 0;
}

.data-view-list__item {
  display: block;
  width: 100%;
  border: 0;
  background: transparent;
  text-align: left;
  padding: 10px 14px;
  cursor: pointer;
  border-bottom: 1px solid #ececec;
  color: var(--ink, #1a2737);
  font: inherit;
}
.data-view-list__item:hover {
  background: #f5f8fc;
}
.data-view-list__item--active {
  background: #d3e2f5;
}
.data-view-list__item--active:hover {
  background: #c7daf0;
}
.data-view-list__item:focus-visible {
  outline: 2px solid #2860a8;
  outline-offset: -2px;
}

.data-view-list__primary {
  font-weight: 600;
  font-size: 14px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.data-view-list__description {
  color: var(--ink, #1a2737);
  font-size: 12.5px;
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.data-view-list__sub {
  color: var(--muted, #6a7384);
  font-size: 12.5px;
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Date-group header above each batch of list items sharing the same
   dateField value. Small, inked, with a subtle underline to visually
   separate groups without competing with the entries themselves. */
.data-view-list__date-header {
  font-size: 12px;
  font-weight: 600;
  color: var(--muted, #6a7384);
  padding: 10px 12px 4px;
  border-bottom: 1px solid var(--border, #dedede);
  background: var(--surface-subtle, #f2f2f2);
  position: sticky;
  top: 0;
  z-index: 1;
}
.data-view-list__date-header:first-child {
  padding-top: 4px;
}

.data-view-list__pager {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 6px 10px;
  border-top: 1px solid var(--line, #d7d7d7);
  background: #f5f5f5;
  color: var(--muted, #6a7384);
  font-size: 12px;
}

.data-view-list__pager-label {
  flex: 1 1 auto;
  text-align: center;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.data-view-list__page-btn {
  flex: 0 0 24px;
  width: 24px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--line, #d7d7d7);
  border-radius: 4px;
  background: #ffffff;
  color: #2860a8;
  cursor: pointer;
  padding: 0;
}
.data-view-list__page-btn:hover:not([disabled]) { background: #eef5ff; }
.data-view-list__page-btn[disabled] {
  opacity: 0.35;
  cursor: default;
  color: var(--muted, #6a7384);
}
.data-view-list__page-btn svg { width: 14px; height: 14px; }

.data-view-list__empty {
  padding: 20px 14px;
  color: var(--muted, #6a7384);
  text-align: center;
  font-size: 13px;
}

.data-view-body {
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
  overflow: auto;
  padding: 14px 16px 24px;
  /* v84o: white form backdrop matching React's DataViewForm, where
     the form container (`DataFormDiv`) has no background color and
     inherits the page's natural white. Cards sit on the same white
     rather than reading as chips floating on a soft-grey. The grey
     wash we had (--soft-bg, #f3f6fa) added visual noise that the
     React app never had. */
  background: #ffffff;
}

.data-view-cards {
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-width: 1100px;
  margin: 0 auto;
}

.data-view-card {
  background: #ffffff;
  border: 1px solid var(--line, #d7d7d7);
  border-radius: 8px;
  /* v84o: shadow removed — it was barely visible against the old
     soft-grey body and is entirely invisible now that the body is
     white (white-on-white). The 1px border alone separates cards
     adequately, matching React's ui5-card visual weight. */
  overflow: hidden;
}

.data-view-card__header {
  padding: 8px 14px;
  /* .60: lightened from #ededed → #f5f5f5. The previous tone read as
     a heavy banner stripe across each group; the form has 5+ groups
     stacked and the dark cumulative weight pulled focus away from
     the field rows. #f5f5f5 still distinguishes the header from the
     white body and the dashed field separators below, but blends
     into the card so the eye lands on the data, not the chrome. */
  background: #f5f5f5;
  border-bottom: 1px solid var(--line, #d7d7d7);
  color: var(--brand-dark, #18324d);
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: 0.01em;
}

.data-view-card__body {
  padding: 10px 14px 12px;
}

/* Two-column layout for key-value fields on wider screens. We use CSS
   multi-column so individual fields stay intact and the browser handles
   balancing. On narrow viewports (< 720px) we fall back to one column. */
.data-view-card__body--two-col {
  column-count: 2;
  column-gap: 28px;
}
@media (max-width: 720px) {
  .data-view-card__body--two-col { column-count: 1; }
}

.data-view-field {
  break-inside: avoid;
  display: grid;
  grid-template-columns: minmax(120px, 36%) 1fr;
  gap: 10px;
  padding: 4px 0;
  align-items: center;
}
.data-view-field + .data-view-field { border-top: 1px dashed #ebebeb; }

.data-view-field__label {
  color: var(--muted, #6a7384);
  font-size: 13px;
  text-align: right;
  padding-right: 2px;
}

/* Red asterisk next to mandatory field labels (only when the field is
   editable — matches Bicycle: locked mandatory fields don't get one). */
.data-view-field__mandatory {
  color: var(--danger, #d43a2f);
  font-weight: 600;
  margin-left: 2px;
}

/* Inline error banner — shown above the form cards when a save fails
   or a validation error is hit. Replaces alert() so the user stays
   in edit mode with their typed values intact. */
.data-view-error-banner {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 10px 12px;
  padding: 10px 12px;
  background: #fff4f3;
  border: 1px solid #f5c2bd;
  border-left: 3px solid var(--danger, #d43a2f);
  border-radius: 4px;
  color: #7a1e18;
  font-size: 13px;
  line-height: 1.4;
  position: sticky;
  top: 0;
  z-index: 2;
}
.data-view-error-banner__icon {
  flex: 0 0 16px;
  margin-top: 1px;
  color: var(--danger, #d43a2f);
}
.data-view-error-banner__text {
  flex: 1 1 auto;
  min-width: 0;
  white-space: pre-wrap;
  word-break: break-word;
}
.data-view-error-banner__close {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  margin: -2px -4px -2px 0;
  background: transparent;
  border: none;
  border-radius: 3px;
  color: #7a1e18;
  cursor: pointer;
}
.data-view-error-banner__close:hover { background: rgba(212, 58, 47, 0.12); }

/* Field row pointed at by the error banner — soft pink wash + left
   accent so the user can find where to fix it. */
.data-view-field--error .data-view-field__label { color: var(--danger, #d43a2f); }
.data-view-field--error .data-view-field__value {
  outline: 2px solid rgba(212, 58, 47, 0.3);
  outline-offset: 2px;
  border-radius: 3px;
}

.data-view-field__value {
  font-size: 13.5px;
  color: var(--ink, #1a2737);
  white-space: pre-wrap;
  word-break: break-word;
  min-width: 0;
}

/* Edit-mode fields render controls (inputs, dropdowns, lookup triggers).
   Override the view-mode `pre-wrap` so template whitespace between tags
   doesn't become real renderable whitespace that inflates row height.
   Also pin line-height so the cell hugs its single 30px child. */
.data-view-field--editing .data-view-field__value {
  white-space: normal;
  line-height: 1;
}

/* Free-text card rows stack label-above-value and span the full width. */
.data-view-field--text {
  display: block;
  padding: 8px 0;
}
.data-view-field--text + .data-view-field--text { border-top: 1px solid #ebebeb; }
.data-view-field--text .data-view-field__label {
  text-align: left;
  margin-bottom: 4px;
  padding-right: 0;
}
.data-view-field__value--text {
  padding: 6px 10px;
  background: #f5f5f5;
  border: 1px solid #ebebeb;
  border-radius: 6px;
  line-height: 1.5;
}

/* Rich-text columns living inside a 2-col card body — span across both
   columns of the CSS multi-column layout, keeping the label-on-left /
   editor-on-right grid but giving the editor the full card width.  Match
   how BICycle's Edit Item dialog renders NOTES / SUMMARY / REVISION_CHANGES
   / OFFSHORE_CLOSE_OUT / ONSHORE_CLOSE_OUT as wide RTE rows sitting below
   the regular fields in the same section. */
.data-view-field--richtext {
  column-span: all;
  -webkit-column-span: all;
  break-inside: auto;
  /* Narrower label column than the 36% used for paired 2-col rows — the
     label is a small badge next to a much wider editor now. */
  grid-template-columns: minmax(100px, 140px) 1fr;
  align-items: start;
  padding: 10px 0 6px;
}

.data-view-loading,
.data-view-empty,
.data-view-error {
  text-align: center;
  padding: 40px 16px;
  color: var(--muted, #6a7384);
  font-size: 14px;
}
.data-view-error { color: #b02a37; }

/* ── Phase 3: edit mode ──────────────────────────────────────────────── */

/* Top-bar actions — horizontal group on the right side of the header. */
.data-view-top-bar__actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
}

/* Text + icon action buttons (Edit, Cancel, Save). */
.data-view-top-bar__action {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 12px;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: #2860a8;
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
}
.data-view-top-bar__action:hover:not([disabled]) { background: #eef5ff; }
.data-view-top-bar__action:focus-visible { outline: 2px solid #2860a8; outline-offset: -2px; }
.data-view-top-bar__action[disabled] { opacity: 0.5; cursor: not-allowed; }

.data-view-top-bar__action--ghost {
  color: var(--ink, #1a2737);
  border-color: var(--line, #d7d7d7);
}
.data-view-top-bar__action--ghost:hover:not([disabled]) { background: #efefef; }

.data-view-top-bar__action--primary {
  background: #2860a8;
  color: #ffffff;
  border-color: #2860a8;
}
.data-view-top-bar__action--primary:hover:not([disabled]) { background: #1f4e8d; border-color: #1f4e8d; }
.data-view-top-bar__action--primary[disabled] { background: #b9c5d6; border-color: #b9c5d6; color: #ffffff; }

/* v84q: edit-mode amber stripe on the top bar (added in a prior
   iteration "so the user can always see they're editing") has been
   removed. The React reference doesn't carry such an indicator, and
   visible Save / Cancel / Save and Next buttons plus the editable
   input fields already signal edit state clearly. The stripe added
   visual noise without adding information. The class is still applied
   by pages/dataview-page.js for backward compatibility; the empty
   rule below ensures nothing breaks if future turns want to reintroduce
   a subtler cue here. */
.data-view-top-bar--editing { /* intentionally empty */ }

/* Editable field slot inside a card. The label stays the same; the value
   column holds an input/textarea/checkbox instead of static text. */
.data-view-field--editing .data-view-field__value {
  padding: 0;       /* let the input fill the slot */
  background: transparent;
  border: 0;
  /* The base .data-view-field__value uses white-space: pre-wrap so
     multi-line TEXT values render with their newlines. In edit mode the
     slot holds nested elements with template-literal whitespace between
     them — pre-wrap would render that whitespace as real line breaks,
     roughly doubling the row height. Reset to normal. */
  white-space: normal;
}

.data-view-field__input {
  width: 100%;
  box-sizing: border-box;
  min-height: 30px;
  padding: 4px 8px;
  border: 1px solid var(--line, #d7d7d7);
  border-radius: 4px;
  font: inherit;
  font-size: 13.5px;
  color: var(--ink, #1a2737);
  background: #ffffff;
}
.data-view-field__input:focus {
  outline: 2px solid #2860a8;
  outline-offset: -2px;
  border-color: #2860a8;
}

/* Native <select> rendered via .data-view-field__input needs a custom
   caret so it matches the text inputs. Using appearance:none + an inline
   SVG background avoids pulling in a third-party library. */
select.data-view-field__input {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  padding-right: 28px;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'><path d='M2 4 L6 8 L10 4' fill='none' stroke='%231a2737' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 8px center;
  cursor: pointer;
}

.data-view-field__textarea {
  min-height: 80px;
  resize: vertical;
  font-family: inherit;
  line-height: 1.4;
}

/* ============================================================
   Rich text editor — used for free-text fields (Notes, Details,
   Findings, etc.). Surface is contenteditable; toolbar buttons
   run document.execCommand against the current selection.
   ============================================================ */
.rich-text-editor {
  border: 1px solid var(--border, #d5dae1);
  border-radius: 4px;
  background: #fff;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.rich-text-editor:focus-within {
  border-color: var(--accent, #2d7ff9);
  box-shadow: 0 0 0 2px rgba(45, 127, 249, 0.15);
}

.rich-text-editor__toolbar {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 4px;
  background: var(--surface-subtle, #f2f2f2);
  border-bottom: 1px solid var(--border, #dedede);
  flex-wrap: wrap;
}
.rich-text-editor__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  height: 26px;
  padding: 0 6px;
  border: 1px solid transparent;
  border-radius: 3px;
  background: transparent;
  color: var(--ink, #1a2737);
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
  line-height: 1;
}
.rich-text-editor__btn:hover {
  background: #fff;
  border-color: var(--border, #d5dae1);
}
.rich-text-editor__btn:active {
  background: #ececec;
}
.rich-text-editor__btn b, .rich-text-editor__btn i, .rich-text-editor__btn u, .rich-text-editor__btn s {
  font-family: Georgia, 'Times New Roman', serif;
}
.rich-text-editor__sep {
  width: 1px;
  height: 16px;
  background: var(--border, #d5dae1);
  margin: 0 4px;
}

.rich-text-editor__content {
  min-height: 100px;
  max-height: 320px;
  overflow: auto;
  padding: 8px 10px;
  font-size: 13.5px;
  line-height: 1.45;
  color: var(--ink, #1a2737);
  outline: none;
}
.rich-text-editor__content:empty::before {
  /* Subtle placeholder when the editor is empty and unfocused. */
  content: attr(data-placeholder);
  color: var(--muted, #9aa3b2);
  pointer-events: none;
}
.rich-text-editor__content p { margin: 0 0 8px; }
.rich-text-editor__content ul, .rich-text-editor__content ol { margin: 0 0 8px; padding-left: 24px; }
.rich-text-editor__content a { color: var(--accent, #2d7ff9); text-decoration: underline; }

/* Read-only (view mode) rendering of stored rich-text. Matches the
   editor's content typography so formatting looks consistent across
   modes. */
.data-view-field__richtext-view {
  font-size: 13.5px;
  line-height: 1.45;
  color: var(--ink, #1a2737);
}
.data-view-field__richtext-view p { margin: 0 0 8px; }
.data-view-field__richtext-view p:last-child { margin-bottom: 0; }
.data-view-field__richtext-view ul, .data-view-field__richtext-view ol { margin: 0 0 8px; padding-left: 24px; }
.data-view-field__richtext-view a { color: var(--accent, #2d7ff9); text-decoration: underline; }

.data-view-field__checkbox-wrap {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 0;
  cursor: pointer;
}
.data-view-field__checkbox { margin: 0; }
.data-view-field__checkbox-label { font-size: 13.5px; }

.data-view-field__editor--readonly {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 8px;
  border: 1px dashed var(--line, #d7d7d7);
  border-radius: 4px;
  background: #f5f5f5;
  color: var(--ink, #1a2737);
  min-height: 30px;
}
.data-view-field__readonly-hint {
  color: var(--muted, #6a7384);
  font-size: 12px;
  font-style: italic;
}

/* Lookup editor — single clickable trigger that looks like an input, plus
   a small clear (×) button on the right. Trigger opens the picker dialog;
   clear resets to empty without opening the dialog. A hidden input carries
   the actual key for save. */
.data-view-field__lookup {
  display: flex;
  align-items: center;
  gap: 4px;
  min-height: 30px;
}
.data-view-field__lookup-trigger {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  height: 30px;
  padding: 0 8px;
  border: 1px solid var(--line, #d7d7d7);
  border-radius: 4px;
  background: #ffffff;
  color: var(--ink, #1a2737);
  font: inherit;
  font-size: 13.5px;
  text-align: left;
  cursor: pointer;
}
.data-view-field__lookup-trigger:hover:not([disabled]) { border-color: #2860a8; }
.data-view-field__lookup-trigger:focus-visible { outline: 2px solid #2860a8; outline-offset: -2px; }
.data-view-field__lookup-trigger[disabled] { opacity: 0.5; cursor: wait; }

.data-view-field__lookup-text {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.data-view-field__lookup-caret {
  flex: 0 0 12px;
  width: 12px;
  height: 12px;
  color: var(--muted, #6a7384);
}
.data-view-field__lookup-placeholder {
  color: var(--muted, #6a7384);
  font-style: italic;
}

.data-view-field__lookup-clear {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  color: var(--muted, #6a7384);
  border-radius: 4px;
  cursor: pointer;
}
.data-view-field__lookup-clear:hover { background: #efefef; color: var(--ink, #1a2737); }
.data-view-field__lookup-clear:focus-visible { outline: 2px solid #2860a8; outline-offset: -2px; }
.data-view-field__lookup-clear svg { width: 12px; height: 12px; }
.data-view-field__lookup-clear[hidden] { display: none; }

/* ── Small-lookup custom dropdown (v78e) ─────────────────────────────── */
/* Replaces native <select> for dimension fields so options can carry a
   status/risk colour dot + optional code suffix.  Visually matches the
   lookup-large trigger; lazy-fetches options via /filterMembers on
   first open. */
.data-view-field__dropdown {
  display: flex;
  align-items: center;
  gap: 4px;
  min-height: 30px;
}
.data-view-field__dropdown-trigger {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  height: 30px;
  padding: 0 8px;
  border: 1px solid var(--line, #d7d7d7);
  border-radius: 4px;
  background: #ffffff;
  color: var(--ink, #1a2737);
  font: inherit;
  font-size: 13.5px;
  text-align: left;
  cursor: pointer;
}
.data-view-field__dropdown-trigger:hover:not([disabled]) { border-color: #2860a8; }
.data-view-field__dropdown-trigger:focus-visible { outline: 2px solid #2860a8; outline-offset: -2px; }
.data-view-field__dropdown-trigger[disabled] { opacity: 0.5; cursor: wait; }
.data-view-field__dropdown-text {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.data-view-field__dropdown-caret {
  flex: 0 0 12px;
  width: 12px;
  height: 12px;
  color: var(--muted, #6a7384);
}
.data-view-field__dropdown-clear {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  color: var(--muted, #6a7384);
  border-radius: 4px;
  cursor: pointer;
}
.data-view-field__dropdown-clear:hover { background: #efefef; color: var(--ink, #1a2737); }
.data-view-field__dropdown-clear:focus-visible { outline: 2px solid #2860a8; outline-offset: -2px; }
.data-view-field__dropdown-clear svg { width: 12px; height: 12px; }
.data-view-field__dropdown-clear[hidden] { display: none; }

/* Popup rendered under document.body at z-index 2020 — above the edit
   dialog (2000) and the lookup picker modal (2010).  Position is set
   inline by the form module based on the trigger's bounding rect. */
.data-view-select-popup {
  position: fixed;
  z-index: 2020;
  max-height: 320px;
  overflow-y: auto;
  min-width: 200px;
  background: #ffffff;
  border: 1px solid #d0d0d0;
  border-radius: 6px;
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.18);
  padding: 4px 0;
  font-size: 13.5px;
}
.data-view-select-option {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 6px 10px;
  background: transparent;
  border: 0;
  color: var(--ink, #1a2737);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.data-view-select-option:hover { background: #eef5ff; }
.data-view-select-option--current { background: #dceaff; }
.data-view-select-option--current:hover { background: #cfe0fa; }
.data-view-select-dot {
  flex: 0 0 12px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 1px solid rgba(0, 0, 0, 0.15);
}
/* When a row has no colour we still reserve the slot so labels line up
   across the list.  Transparent + no border to keep it invisible. */
.data-view-select-dot--empty {
  border-color: transparent;
  background: transparent;
}
.data-view-select-label {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.data-view-select-code {
  flex: 0 0 auto;
  color: var(--muted, #6a7384);
  font-size: 12px;
  margin-left: 8px;
}
.data-view-select-empty {
  padding: 10px 14px;
  color: var(--muted, #6a7384);
  font-style: italic;
}

/* ── Lookup picker modal ──────────────────────────────────────────────── */

.lookup-picker {
  position: fixed;
  inset: 0;
  /* Sits above modal dialogs (2000) so a lookup opened from inside
     the Edit Item / New Item dialogs renders on top, not behind. */
  z-index: 2010;
  display: flex;
  align-items: center;
  justify-content: center;
}
.lookup-picker__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(12, 20, 32, 0.35);
}
.lookup-picker__dialog {
  position: relative;
  display: flex;
  flex-direction: column;
  width: min(560px, 92vw);
  max-height: min(640px, 85vh);
  background: #ffffff;
  border-radius: 8px;
  box-shadow: 0 20px 44px rgba(12, 20, 32, 0.28);
  overflow: hidden;
}
.lookup-picker__header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--line, #d7d7d7);
  background: #ffffff;
}
.lookup-picker__title {
  flex: 1 1 auto;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--ink, #1a2737);
}
.lookup-picker__close {
  width: 28px;
  height: 28px;
  border: 0;
  background: transparent;
  color: var(--muted, #6a7384);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  border-radius: 4px;
}
.lookup-picker__close:hover { background: #efefef; color: var(--ink, #1a2737); }

.lookup-picker__toolbar {
  padding: 10px 12px;
  border-bottom: 1px solid var(--line, #d7d7d7);
  background: #fafbfd;
}
.lookup-picker__search {
  width: 100%;
  height: 32px;
  box-sizing: border-box;
  padding: 0 10px;
  border: 1px solid var(--line, #d7d7d7);
  border-radius: 6px;
  font: inherit;
  font-size: 13px;
  background: #ffffff;
}
.lookup-picker__search:focus { outline: 2px solid #2860a8; outline-offset: -2px; border-color: #2860a8; }

.lookup-picker__list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 4px 0;
}
.lookup-picker__item {
  display: flex;
  width: 100%;
  gap: 10px;
  align-items: center;
  padding: 8px 14px;
  border: 0;
  background: transparent;
  text-align: left;
  font: inherit;
  color: var(--ink, #1a2737);
  cursor: pointer;
  border-bottom: 1px solid #ececec;
}
.lookup-picker__item:hover { background: #f5f8fc; }
.lookup-picker__item--current { background: #d3e2f5; }
.lookup-picker__item--current:hover { background: #c7daf0; }
.lookup-picker__item:focus-visible { outline: 2px solid #2860a8; outline-offset: -2px; }
.lookup-picker__item-dot {
  flex: 0 0 12px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 1px solid rgba(0, 0, 0, 0.15);
}
/* Items without a coloured status keep the slot so labels align. */
.lookup-picker__item-dot--empty {
  border-color: transparent;
  background: transparent;
}
.lookup-picker__item-label {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lookup-picker__item-value {
  flex: 0 0 auto;
  color: var(--muted, #6a7384);
  font-size: 12px;
}

.lookup-picker__loading,
.lookup-picker__empty,
.lookup-picker__error,
.lookup-picker__overflow {
  padding: 20px 14px;
  text-align: center;
  color: var(--muted, #6a7384);
  font-size: 13px;
}
.lookup-picker__error { color: #b02a37; }

.lookup-picker__footer {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 10px 12px;
  border-top: 1px solid var(--line, #d7d7d7);
  background: #fafbfd;
}
.lookup-picker__btn {
  height: 32px;
  padding: 0 14px;
  border: 1px solid var(--line, #d7d7d7);
  border-radius: 6px;
  background: #ffffff;
  color: var(--ink, #1a2737);
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
}
.lookup-picker__btn:hover { background: #efefef; }

/* v84p: primary variant for the OK button in multi-select mode. Blue
   fill with white text to visually anchor the commit action among the
   ghost Cancel / Clear siblings. Matches the primary-button palette
   used elsewhere (data-tree edit pen, toolbar action buttons). */
.lookup-picker__btn--primary {
  background: #2860a8;
  border-color: #2860a8;
  color: #ffffff;
}
.lookup-picker__btn--primary:hover {
  background: #1e4d8f;
  border-color: #1e4d8f;
}
.lookup-picker__btn--primary:focus-visible {
  outline: 2px solid #2860a8;
  outline-offset: 2px;
}

/* Checkbox indicator on each item in multi-mode. Sits at the left
   of the row before the color dot / label. The SVG inside is drawn
   by services/checkbox-svg.js with explicit fills — the wrapper
   here just sets size + layout (color:/hover: are no-ops now but
   harmless). v84x: simplified from earlier versions which toggled
   the wrapper color to drive the mark via currentColor. */
.lookup-picker__item-checkbox {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
}
.lookup-picker__item-checkbox svg {
  width: 16px;
  height: 16px;
  display: block;
}

/* Multi-mode dialog variant: slightly wider + taller list area since
   users typically scan more options than single-select. Optional. */
.lookup-picker__dialog--multi {
  min-width: 420px;
}

/* v84q: tree rendering in the lookup picker (multiFieldHierarchy
   lookups). Rows indent by level via inline padding-left, with an
   expand chevron on parents and the same SVG-checkbox as the flat
   multi mode. Visual language matches the sidebar filter-tree rows —
   hover tint, current-selection tint, muted chevron color. */
.lookup-picker__tree-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding-top: 5px;
  padding-bottom: 5px;
  padding-right: 10px;
  min-height: 28px;
  font-size: 13.5px;
  color: var(--ink, #1a2737);
  cursor: pointer;
  border-radius: 4px;
  user-select: none;
}
.lookup-picker__tree-row:hover {
  background: #eef5ff;
}
.lookup-picker__tree-row:focus-visible {
  outline: 2px solid #2860a8;
  outline-offset: -2px;
}
.lookup-picker__tree-row--current {
  background: rgba(42, 109, 179, 0.08);
  color: #1a4a87;
  font-weight: 500;
}

/* v84u: root row — the hierarchy's top-level label (e.g. "Access
   Group"). It's a header rather than a selectable value, so no hover
   tint and no row-level click handler. In multi-mode the tri-state
   checkbox next to the label is clickable (clears all selections);
   that click is wired directly on the checkbox span. */
.lookup-picker__tree-row--root {
  cursor: default;
  font-weight: 500;
}
.lookup-picker__tree-row--root:hover {
  background: transparent;
}
/* v84x: chevron inside the root header row is purely decorative (no
   click handler) — disable its hover highlight + pointer cursor so
   it doesn't look interactive. */
.lookup-picker__tree-row--root .lookup-picker__tree-chevron {
  cursor: default;
  pointer-events: none;
}
.lookup-picker__tree-row--root .lookup-picker__tree-chevron:hover {
  background: transparent;
  color: #6a7384;
}

.lookup-picker__tree-chevron {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex: 0 0 18px;
  color: #6a7384;
  cursor: pointer;
  border-radius: 3px;
  transition: transform 120ms ease, color 120ms ease;
}
.lookup-picker__tree-chevron svg {
  width: 12px;
  height: 12px;
  display: block;
}
.lookup-picker__tree-chevron:hover {
  color: #2860a8;
  background: rgba(42, 109, 179, 0.08);
}
/* Rotate the right-pointing chevron 90° down when the node is open. */
.lookup-picker__tree-chevron--open {
  transform: rotate(90deg);
}
/* Empty slot on leaf rows keeps alignment consistent with parents. */
.lookup-picker__tree-chevron--empty {
  pointer-events: none;
}

/* v84t: in-flight expand indicator. Replaces the chevron with a
   dashed circle while the child-fetch is awaiting the /filterMembers
   response. Sized identically to the chevron so the row doesn't jump.
   Animates via keyframes to signal progress; pointer-events off so
   a stray click during the ~100-300ms fetch doesn't queue another. */
.lookup-picker__tree-chevron--loading {
  color: #6a7384;
  pointer-events: none;
  animation: lookup-picker-spin 0.9s linear infinite;
}
@keyframes lookup-picker-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* ── v85f: subheading-tile-overlay — modal hosting the bid3 tile
   grid opened from the analysis page top-bar. Same backdrop + panel
   pattern as the export-report dialog so the two modals feel
   coordinated. Sized taller since the tile chart needs vertical
   room to breathe (report headings can stack several rows of tiles). */
.sto-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 1200;
  display: flex;
  align-items: center;
  justify-content: center;
}
.sto-panel {
  position: relative;
  background: #ffffff;
  border-radius: 6px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.22);
  width: min(92vw, 1100px);
  height: min(90vh, 820px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.sto-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 18px;
  border-bottom: 1px solid #dddddd;
  background: #f1f1f1;
  flex-shrink: 0;
}
.sto-title {
  /* v954: 15 → 20px per user request ("dialog title larger font").
     Sits above the v952/v953/v954 section headings (18px/600/
     #696969 mid-grey) so the visual hierarchy now reads:
       dialog title (20px/600/#18324d)     ← report/section name
       section heading (18px/600/#696969)  ← Standard Analyses…
       tile title (13px/600/#2b3648)       ← Database Content, …
     Weight kept at 600 — going 700 would compete with the
     brand-dark colour for visual weight against the #f1f1f1
     header band. */
  font-size: 20px;
  font-weight: 600;
  color: #18324d;
}
.sto-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  color: #1c56a4;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.sto-close:hover {
  background: #eaf2fb;
  border-color: #b9cfe6;
}
.sto-body {
  flex: 1 1 auto;
  /* v85g: horizontal scrollbar was appearing because bid3 renders the
     tile SVG at host.offsetWidth but the content can overshoot by a
     few pixels (padding + SVG stroke width), triggering an x-axis
     scrollbar that then shrinks the available width and can loop
     with the ResizeObserver. Hiding overflow-x prevents both the
     visual noise and the resize feedback loop. Vertical scroll is
     preserved so reports with many rows of tiles still work. */
  overflow-x: hidden;
  overflow-y: auto;
  padding: 16px 12px;
  /* v952: bg flipped #ffffff → #f0f0f0 per user request — matches
     the home-page grey chrome introduced in v939, so the tile
     cards (white, .tiles-tile) read as "cards floating on grey
     chrome" rather than "white cards against a white panel". Same
     visual language as the Windows BI-Cycle reference client's
     tiles dialog. */
  background: #f0f0f0;
}

/* ─────────────────────────────────────────────────────────────────
   v890: subheading-tile-renderer-html.js — plain-HTML tile grid.
   Replaces the bid3 SVG renderer for the "Pick a section" overlay
   and the report-page tile mode. Scoped under .tiles-root so the
   styles don't bleed into other grid/card surfaces.
   ───────────────────────────────────────────────────────────────── */
.tiles-root {
  padding: 8px 16px 24px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* Heading row — the "Open: IEUL …" / "Verification Period: …" labels
   above each grid of tiles. Kept minimal (no background, no icon
   button yet — the bid3 hierarchy-button popover is a follow-up). */
.tiles-heading {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.tiles-heading__row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 4px;
}
.tiles-heading__title {
  margin: 0;
  font-size: 14px;
  font-weight: 500;
  color: #3d4a5d;
  letter-spacing: 0.01em;
}

/* v952: overlay-only override — section heading "somewhat larger"
   font per user request, matching the Windows BI-Cycle reference
   client's tiles dialog. Scoped to .sto-body (the overlay's
   content area) so the report-page tile mode — which reuses the
   same .tiles-heading__title class but may have tighter vertical
   budgets — keeps the base 14px. Weight bumped 500 → 600 to keep
   the larger glyphs reading as chrome-level titles rather than
   body text.
   v953: color flipped from the base #3d4a5d (blue-tinted slate)
   to a neutral grey per user request — the Windows reference
   uses a flat grey for these section labels, and the blue tint
   read as "part of the brand-blue palette" rather than the
   neutral chrome the reference was going for. Kept scoped to
   the overlay so report-page tile mode is unaffected.
   v954: exact value set to #696969 (user picked it in devtools
   and asked to persist). A mid-grey — lighter than the previous
   v953 #2b2b2b so the titles read as a distinct subheading tier
   rather than competing with the near-black tile titles below. */
.sto-body .tiles-heading__title {
  font-size: 18px;
  font-weight: 600;
  color: #696969;
}

/* Grid of tiles. Flex-wrap (rather than CSS grid) mirrors bid3's
   "flow until row is full, then break" behaviour. Tiles have a
   fixed width so partial rows align to the left rather than
   stretching to fill — matches the reference app's image 2 layout. */
.tiles-heading__grid {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

/* Individual tile. Reset button chrome, then style as a card. The
   click affordance (hover/focus states) is on the tile itself since
   the whole tile is clickable. */
.tiles-tile {
  all: unset;
  box-sizing: border-box;
  width: 180px;
  height: 130px;
  padding: 10px 12px;
  background: #ffffff;
  border: 1px solid #d7dde5;
  border-radius: 4px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 6px;
  transition: box-shadow 120ms ease, border-color 120ms ease;
}
.tiles-tile:hover {
  border-color: #9fb8d2;
  box-shadow: 0 2px 6px rgba(0, 32, 80, 0.08);
}
.tiles-tile:focus-visible {
  outline: 2px solid #669ACC;
  outline-offset: 2px;
}
.tiles-tile__title {
  font-size: 13px;
  font-weight: 600;
  color: #2b3648;
  line-height: 1.25;
  /* Two-line clamp — bid3 truncated to 2 lines with "..." so we
     reproduce that behaviour with standard line-clamp. Falls back
     to overflow:hidden on browsers without the -webkit prefix. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 2.5em;
}
.tiles-tile__body {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  min-height: 0;
}

/* ── thumbnail variants ────────────────────────────────────────── */
.tiles-thumb {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  height: 100%;
  color: #4a5a6f;
}

/* KPI indicator: colored dot + number (+ optional label below).
   Matches the reference app's "0 / Finding Open" layout in image 2. */
.tiles-thumb--kpi {
  flex-wrap: wrap;
  align-items: baseline;
}
.tiles-thumb__kpi-dot {
  flex-shrink: 0;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #d1d5dc;
  transform: translateY(2px);
}
.tiles-thumb__kpi-dot--pending {
  background: #e4e7ec;
  /* Subtle pulse so a slow KPI fetch reads as "loading" rather than
     "the server said grey". Matches the fade on .tiles-thumb--image
     when the image hasn't loaded yet. */
  animation: tiles-pulse 1.4s ease-in-out infinite;
}
@keyframes tiles-pulse {
  0%, 100% { opacity: 0.5; }
  50%      { opacity: 1;   }
}
.tiles-thumb__kpi-value {
  font-size: 28px;
  font-weight: 300;
  color: #1f2a3a;
  line-height: 1;
}
.tiles-thumb__kpi-label {
  flex-basis: 100%;
  font-size: 12px;
  color: #6b7586;
  margin-top: 4px;
}

/* Record count: rows-icon + number. */
.tiles-thumb--record {
  align-items: center;
  gap: 10px;
}
.tiles-thumb__icon {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  color: #7b8699;
}
.tiles-thumb--record .tiles-thumb__value {
  font-size: 28px;
  font-weight: 300;
  color: #1f2a3a;
  line-height: 1;
}

/* Chart placeholder (v1). Inline icon centred in the tile body so
   the space doesn't read as "broken" while the chart renderer is
   still a follow-up. */
.tiles-thumb--chart {
  justify-content: center;
}
.tiles-thumb--chart .tiles-thumb__icon {
  width: 32px;
  height: 32px;
  color: #96a3b5;
}

/* Image thumbnail — heading images + table images both land here.
   object-fit: contain prevents tall aspect ratios from cropping; the
   tile body's fixed-height slot does the clipping. */
.tiles-thumb--image {
  justify-content: center;
}
.tiles-thumb--image img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  display: block;
}
.tiles-thumb--image-broken {
  /* img's onerror hides the <img>; the parent div is left in the
     DOM so this rule can show a small visual placeholder so a
     broken src doesn't collapse the tile body. */
  background-image: linear-gradient(135deg, #ececec 25%, transparent 25%),
                    linear-gradient(225deg, #ececec 25%, transparent 25%),
                    linear-gradient(45deg,  #ececec 25%, transparent 25%),
                    linear-gradient(315deg, #ececec 25%, transparent 25%);
  background-size: 8px 8px;
  background-position: 0 0, 4px 0, 4px -4px, 0 4px;
}

/* Generic fallback when no thumbnail type resolved (empty content,
   missing metadata, etc). Keeps the tile looking intentional rather
   than broken. */
.tiles-thumb--fallback {
  justify-content: center;
  color: #c0c8d4;
}
.tiles-thumb--fallback .tiles-thumb__icon {
  width: 36px;
  height: 36px;
}

/* ─────────────────────────────────────────────────────────────────
   v891: additions on top of the v890 tiles namespace — wide tiles,
   heading-row hierarchy button, and real chart mini-hosts.
   ───────────────────────────────────────────────────────────────── */

/* Wide tile: subHeading.tile.wideTile + N thumbnails side-by-side.
   Tile width scales with --tile-cols (set inline per tile) so a
   2-col wide tile is 372px (2×180 + 12 gap), 3-col is 564px, etc.
   Body flips from single-thumb centered layout to a row of equal-
   width thumb slots separated by the same 12px gap. */
.tiles-tile--wide {
  width: calc(180px * var(--tile-cols, 1) + 12px * (var(--tile-cols, 1) - 1));
}
.tiles-tile--wide .tiles-tile__body {
  display: flex;
  flex-direction: row;
  align-items: stretch;
  gap: 12px;
}
.tiles-tile--wide .tiles-tile__body > .tiles-thumb {
  flex: 1 1 0;
  min-width: 0;
  /* Per-thumb dividers so the boundary between thumbnails reads.
     Left-side rule on every thumb except the first — keeps the tile's
     outer border doing the outermost separation. */
}
.tiles-tile--wide .tiles-tile__body > .tiles-thumb + .tiles-thumb {
  border-left: 1px solid #ececec;
  padding-left: 12px;
}

/* Chart thumb host — bid3.Chart mounts its <svg> inside this div,
   so it must be a positioning context + explicit sizing source. The
   chart reads offsetWidth/offsetHeight at mount time (see
   wireChartFetches in subheading-tile-renderer-html.js), so fixing
   the host to fill the thumb slot gives bid3 the right dimensions.
   When the placeholder icon is still showing (data-chart-placeholder=1)
   we centre the icon; once bid3 takes over the attribute is removed
   and the <svg> child occupies the full host. */
.tiles-thumb__chart-host {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
}
.tiles-thumb__chart-host[data-chart-placeholder="1"] {
  color: #96a3b5;
}
.tiles-thumb__chart-host > svg {
  /* bid3's own <svg> is not a .tiles-thumb__icon, so the default
     icon sizing doesn't apply — leave width/height on the svg as
     bid3 set them so the chart scales correctly. */
  max-width: 100%;
  max-height: 100%;
}

/* Heading-row hierarchy button. Small flat square button to the left
   of the heading title; muted by default, slight tint on hover. Fires
   the `tiles-heading-button-click` CustomEvent; no visual state
   change on click (consumer-side popover will take over the affordance). */
.tiles-heading__button {
  all: unset;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 4px;
  color: #6b7586;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
  flex-shrink: 0;
}
.tiles-heading__button:hover {
  background: #ececec;
  color: #1f2a3a;
}
.tiles-heading__button:focus-visible {
  outline: 2px solid #669ACC;
  outline-offset: 1px;
}
.tiles-heading__button-icon {
  width: 16px;
  height: 16px;
  display: block;
}

/* v892: per-table icon image for Data-content tiles. Sized to sit in
   the same spot the generic ICON_RECORDS SVG occupies (22×22, flush
   left of the count value) so swapping between the two doesn't cause
   layout shift. object-fit:contain prevents oddly-shaped table icons
   from stretching. */
.tiles-thumb__icon-img {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  object-fit: contain;
  display: block;
}
/* Hidden-by-default placeholder — kept in the DOM so that if the
   primary <img> 404s, the onerror handler can drop the is-hidden
   class and reveal the generic icon without a re-render. */
.tiles-thumb__icon-placeholder {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: inherit;
}
.tiles-thumb__icon-placeholder.is-hidden {
  display: none;
}
/* When the placeholder is visible it still contains our inline SVG,
   which itself has the .tiles-thumb__icon class (sized 22×22). Ensure
   the wrapper doesn't add spacing around the svg. */
.tiles-thumb__icon-placeholder > .tiles-thumb__icon {
  width: 22px;
  height: 22px;
}

/* v901: content-sub-tabs row
   Rendered directly below the subheading-tabs row when the active
   subHeading has contentDropdown:true AND more than one content.
   Visual tier: secondary — slightly smaller chips than
   .subheading-tab, sits on a lighter band so the primary subheading
   strip remains the dominant navigation surface. Shares the same
   chrome-strip grey palette used by the rest of the chrome so the
   whole top chunk still reads as one coherent band (see
   .subheading-tabs for the palette rationale). */
.content-sub-tabs {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 4px;
  width: 100%;
  min-width: 0;
  padding: 4px 22px;
  min-height: 30px;
  background: #ffffff;
  border-bottom: 1px solid var(--line);
}

.content-sub-tab {
  /* Pill-like chip, flatter than .subheading-tab. Inactive chips are
     outlined on the white strip; active chip inverts to the brand-blue
     fill so the user's pick is unambiguous even without the sunken
     shadow language the primary tabs use. */
  background: transparent;
  border: 1px solid #bdbdbd;
  border-radius: 12px;
  padding: 3px 10px 2px;
  color: var(--muted);
  font-size: 12px;
  cursor: pointer;
  font-family: inherit;
  white-space: nowrap;
  flex: 0 0 auto;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}

.content-sub-tab:hover {
  background: #ececec;
  color: #18324d;
}

.content-sub-tab--active {
  background: #669ACC;
  color: #ffffff;
  border-color: #669ACC;
  font-weight: 600;
}
.content-sub-tab--active:hover {
  background: #5a8cbd;
  color: #ffffff;
}

/* 26.1.0.29: top progress bar reacting to in-flight authFetch
   calls.  Pinned to the top edge of the viewport so it overlays
   without shifting any content.  Uses a brand-blue fill that
   matches the active-tab chip colour. */
page-progress {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  z-index: 9999;
  pointer-events: none;
  /* When inactive, the element is fully transparent so it leaves
     no visual residue between requests. */
  opacity: 0;
  transition: opacity 200ms ease;
}
page-progress.page-progress--active {
  opacity: 1;
}
page-progress.page-progress--complete {
  /* On settle, fade out alongside the snap-to-100% transition. */
  transition: opacity 280ms ease;
  opacity: 0;
}
.page-progress__fill {
  height: 100%;
  width: 0%;
  background: #669ACC;
  /* Width transitions are handled by the JS RAF loop for the
     0→80% animation, but we add a transition for the 80→100%
     completion snap so it doesn't feel jarring. */
  transition: width 180ms ease;
  box-shadow: 0 0 6px rgba(102, 154, 204, 0.6);
}

/* 26.1.0.31: pointer feedback during in-flight authFetch calls.
   Activated by main.js toggling body.has-inflight-fetch in
   response to the same `bic:fetch-count-change` event the
   <page-progress> bar consumes.

   `cursor: progress` shows a "busy + still interactive" hourglass
   beside the pointer (different from `wait`, which signals "the
   page is frozen").  Applied only to <body> — interactive
   elements (buttons, inputs, splitters, drag handles) keep their
   own cursors so the user still sees the right affordance for
   what they're hovering.  The progress cursor surfaces in the
   gaps: tab strips, table cells, the data-tree, panel padding —
   plenty of pointer-real-estate to register the signal. */
body.has-inflight-fetch {
  cursor: progress;
}

/* 26.1.0.32: cold-start overlay.  Fixed full-viewport, hidden by
   default, fades in when activated.  See components/cold-start-
   overlay.js for the activation logic.  Backdrop is a translucent
   light layer rather than full opaque — keeps the user oriented
   (they can still see the page they're loading) while making the
   message unmissable. */
cold-start-overlay {
  position: fixed;
  inset: 0;
  z-index: 9998; /* below page-progress (9999) so the bar still shows on top */
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.82);
  backdrop-filter: blur(2px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 220ms ease;
}
cold-start-overlay.cold-start-overlay--visible {
  opacity: 1;
  pointer-events: auto;
}
.cold-start-overlay__panel {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  padding: 28px 32px;
  max-width: 420px;
  text-align: center;
  font-family: inherit;
}
.cold-start-overlay__spinner {
  width: 40px;
  height: 40px;
  margin: 0 auto 16px;
  color: #669ACC;
  animation: cold-start-spin 1s linear infinite;
}
.cold-start-overlay__spinner svg {
  width: 100%;
  height: 100%;
  display: block;
}
.cold-start-overlay__title {
  font-size: 16px;
  font-weight: 600;
  color: #24304d;
  margin-bottom: 8px;
}
.cold-start-overlay__body {
  font-size: 13px;
  color: var(--muted);
  line-height: 1.5;
}
@keyframes cold-start-spin {
  to { transform: rotate(360deg); }
}

/* ── 26.1.0.81: footer-bar ─────────────────────────────────────────
   External + internal-dialog links shown at the bottom of the report
   page. Mirrors the BICycle React reference: a flex row of small
   labels with vertical dividers between them. The reference uses a
   coloured background (--theme_color_footer); we keep ours neutral
   to fit the new app's chrome palette.

   The dialog inside is a centered modal with header + scrollable
   body + OK button. INTERNAL LINK loads the URL in an <iframe>;
   HTML renders sanitized markup. The iframe is height 100% of the
   body so long policy pages scroll inside the dialog rather than
   blowing out the viewport.
   ────────────────────────────────────────────────────────────── */
footer-bar {
  display: block;
}
footer-bar:empty { display: none; }
.footer-bar__inner {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
  min-height: 32px;
  padding: 4px 12px;
  background: #f5f7fa;
  border-top: 1px solid #d8e3ee;
  font-size: 12px;
  color: #24304d;
}
.footer-bar__item {
  display: inline-flex;
  align-items: center;
  padding: 0 8px;
}
.footer-bar__item:not(:last-child) {
  border-right: 1px solid #c8d3e0;
}
.footer-bar__icon {
  width: 14px;
  height: 14px;
  margin-right: 4px;
}
.footer-bar__link {
  background: transparent;
  border: 0;
  padding: 0;
  color: #1a4a87;
  cursor: pointer;
  font: inherit;
  text-decoration: none;
}
.footer-bar__link:hover,
.footer-bar__link:focus-visible {
  text-decoration: underline;
}

/* Modal overlay + dialog. Stretch-fill viewport so policy / GDPR
   pages have room to render. */
.footer-dialog__overlay {
  position: fixed;
  inset: 0;
  background: rgba(20, 30, 50, 0.45);
  /* 26.1.0.82: was z-index 1000. The reports rail (.reports-panel
     in app-header) sits at z-index 1050 and the report status bar
     at 1100, so the original value left them painting OVER the
     overlay — clicks landed on the rail items rather than the
     overlay, and the rail/bar visually pierced through. Bumped to
     1200 (overlay) / 1201 (dialog) so the modal is unambiguously
     on top of all page chrome. */
  z-index: 1200;
}
.footer-dialog {
  position: fixed;
  top: 5vh;
  /* 26.1.0.83: max-width 900px caps the readable line-length at
     ~70-80 characters at the body font-size, comfortably under
     the 75ch upper bound for comfortable prose. The previous
     left:5vw / right:5vw stretched the dialog edge-to-edge on
     wide displays (1700px+ on a 1920 viewport), forcing eye
     sweeps far past the legibility sweet spot when reading
     GDPR/legal text. The min(900px, 90vw) inline expression
     keeps the dialog clear of viewport edges on small screens
     too — at 1000px viewport the dialog is 900px (45px gutters);
     at 320px viewport it's 288px (16px gutters). Centered via
     auto margins on left/right (`left:0; right:0` + `margin:auto`
     uses standard width-based centering). bottom keeps the 5vh
     to clear the footer status bar. */
  bottom: 5vh;
  left: 0;
  right: 0;
  margin-left: auto;
  margin-right: auto;
  width: min(900px, 90vw);
  background: #fff;
  border-radius: 6px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25);
  z-index: 1201;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.footer-dialog__header {
  display: flex;
  align-items: center;
  padding: 10px 16px;
  border-bottom: 1px solid #d8e3ee;
  background: #f5f7fa;
}
.footer-dialog__title {
  flex: 1;
  font-size: 15px;
  font-weight: 600;
  color: #24304d;
}
.footer-dialog__close {
  background: transparent;
  border: 0;
  font-size: 16px;
  color: #555;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: 4px;
}
.footer-dialog__close:hover { background: #e6eef9; color: #1a4a87; }
.footer-dialog__body {
  flex: 1;
  overflow: auto;
  display: flex;
  flex-direction: column;
}
.footer-dialog__iframe {
  flex: 1;
  width: 100%;
  height: 100%;
  border: 0;
}
.footer-dialog__html {
  padding: 16px;
  line-height: 1.55;
  color: #24304d;
}
.footer-dialog__html h1, .footer-dialog__html h2, .footer-dialog__html h3 { color: #1a4a87; }
.footer-dialog__html a { color: #1a4a87; }
.footer-dialog__footer {
  padding: 10px 16px;
  border-top: 1px solid #d8e3ee;
  display: flex;
  justify-content: flex-end;
  background: #f5f7fa;
}
.footer-dialog__ok {
  background: #1a4a87;
  color: #fff;
  border: 0;
  padding: 6px 18px;
  border-radius: 4px;
  font-size: 13px;
  cursor: pointer;
}
.footer-dialog__ok:hover { background: #143a6b; }

/* ── 26.1.0.81: report description expandable panel ───────────────
   Bottom panel on the report page that surfaces the admin-authored
   reportDescription. The description can contain HTML (since v999
   admin used a rich-text editor for it), so the panel renders
   sanitized markup, scrolls on overflow, and has a fixed
   max-height to keep large content from blowing out the page.
   26.1.0.82: header (chevron + "Report description" label) removed
   per user request — body is always visible. margin-bottom: 24px
   added to clear the fixed status bar at the viewport bottom
   (.report-status-bar is position:fixed so it would otherwise
   overlap the panel's last 24px).
   26.1.0.83: minimal chevron toggle re-added in the top-left
   corner. No label per user request; default state is expanded
   (--open class on the wrapper). Body collapses to 0 height when
   --open is removed.
   ────────────────────────────────────────────────────────────── */
.report-description-panel {
  /* .174: was position: relative; .174 made this position: sticky
     with bottom: 24px so it hovered above the fixed status bar
     while the panel sat below the tiles.
     .177: panel relocated above the tiles in normal flow (see the
     DOM order change in pages/report-page.js). No more sticky/
     bottom/margin-bottom — this is just an in-flow block above the
     report content. The 24px clearance from the fixed status bar
     is no longer needed since the panel never reaches the viewport
     bottom; .report-page__content already has space below it. */
  position: relative;
  border-bottom: 1px solid #d8e3ee;
  background: #fff;
}
/* 26.1.0.83: chevron-only toggle button, absolute-positioned in
   the top-left corner so it floats in the panel chrome without
   reserving a header strip. Compact (20×20) and low-contrast
   (#5b6f86) so it stays out of the way of the description text. */
.report-description-panel__toggle {
  position: absolute;
  top: 4px;
  left: 4px;
  z-index: 1;
  width: 20px;
  height: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: #5b6f86;
  border-radius: 3px;
}
.report-description-panel__toggle:hover {
  background: #eef2f7;
  color: #1a4a87;
}
.report-description-panel__chevron {
  width: 12px;
  height: 12px;
  transition: transform 120ms ease;
}
/* Open state — chevron points down (rotated 90° from its default
   right-facing position). Collapsed state shows the chevron
   pointing right. */
.report-description-panel--open .report-description-panel__chevron {
  transform: rotate(90deg);
}
.report-description-panel__body {
  padding: 12px 16px 12px 32px; /* extra left padding clears the absolute toggle */
  max-height: 320px;
  overflow-y: auto;
  font-size: 13px;
  line-height: 1.55;
  color: #24304d;
  display: none;
}
.report-description-panel--open .report-description-panel__body {
  display: block;
}
.report-description-panel__body h1,
.report-description-panel__body h2,
.report-description-panel__body h3 { color: #1a4a87; margin-top: 0.4em; }
.report-description-panel__body a { color: #1a4a87; }
.report-description-panel__body img { max-width: 100%; height: auto; }
/* Collapsed-state min-height — when the body is hidden, the panel
   would shrink to zero height, leaving the absolute toggle floating
   over the content area below. Hold a 28px chrome height so the
   toggle has a clear surround when collapsed. */
.report-description-panel:not(.report-description-panel--open) {
  min-height: 28px;
}

/* ─────────────────────────────────────────────────────────────────────────
   Entity page — full-page browser for a connected table. Reached from
   the connected sub-table's "Open in main view" button. Layout mirrors
   the dataview page (top bar + flex body) but the body hosts a single
   <data-table> that fills the viewport.
   ───────────────────────────────────────────────────────────────────── */

.entity-page {
  display: flex;
  flex-direction: column;
  height: 100vh;
  min-height: 0;
  background: #ffffff;
}

/* Top bar layout: back button (left) — title (center, flex-1) —
   actions (right). The back button and actions area are both width-
   constrained or width-flexible-but-balanced so the title actually sits
   centered. The actions slot is currently empty (export lives on the
   data-table toolbar, not the page chrome) but is still rendered with a
   minimum width so a future actions area doesn't shift the title. */
.entity-page__top-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 44px;
  padding: 0 8px;
  background: #ffffff;
  color: var(--ink, #1a2737);
  border-bottom: 1px solid var(--line, #d7d7d7);
  flex: 0 0 auto;
}

/* Home + back wrap together so they read as one cluster on the left.
   Width-capped so the centered title stays balanced even when both
   buttons render with full labels. */
.entity-page__nav-cluster {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: 0 0 auto;
  max-width: 240px;
  min-width: 0;
}

.entity-page__home {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 10px 0 8px;
  border: 0;
  background: transparent;
  color: #2860a8;
  border-radius: 6px;
  cursor: pointer;
  font-size: 13px;
  font-weight: 500;
  flex: 0 1 auto;
  min-width: 0;
}
.entity-page__home:hover { background: #eef5ff; }
.entity-page__home:focus-visible { outline: 2px solid #2860a8; outline-offset: -2px; }

.entity-page__home-icon { flex: 0 0 auto; }

.entity-page__home-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.entity-page__back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 10px 0 6px;
  border: 0;
  background: transparent;
  color: #2860a8;
  border-radius: 6px;
  cursor: pointer;
  font-size: 14px;
  font-weight: 500;
  flex: 0 1 auto;
  min-width: 0;
}
.entity-page__back:hover { background: #eef5ff; }
.entity-page__back:focus-visible { outline: 2px solid #2860a8; outline-offset: -2px; }

.entity-page__back-icon { flex: 0 0 auto; }

.entity-page__back-label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.entity-page__title {
  flex: 1 1 auto;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink, #1a2737);
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  /* Visually centered titles can collide with a long back-label; let
     the title cap its own width so it ellipsises gracefully rather
     than pushing the back button or actions area. */
  min-width: 0;
}

/* Right-side actions area is currently empty — kept as a sized slot
   so the centered title sits visually balanced rather than offset
   left when the back button has a long label. Width matches the back
   button's max-width to keep the centring symmetric. */
.entity-page__top-bar-actions {
  flex: 0 0 auto;
  width: 240px;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
}

.entity-page__body {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.entity-page__body > data-table {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.entity-page__error {
  padding: 24px;
  color: #b00020;
  font-size: 14px;
}

.statistics-indicators-overlay {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: flex;
  align-items: flex-start;
  justify-content: flex-end;
  padding: 120px 36px 36px;
  background: rgba(0, 0, 0, 0.18);
}

.statistics-indicators-dialog {
  width: min(420px, calc(100vw - 48px));
  max-height: min(620px, calc(100vh - 160px));
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid #b8c7d9;
  border-radius: 6px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.28);
  color: #002b55;
}

.statistics-indicators-dialog__header,
.statistics-indicators-dialog__footer {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-bottom: 1px solid #d7e0ea;
}

.statistics-indicators-dialog__footer {
  border-top: 1px solid #d7e0ea;
  border-bottom: 0;
}

.statistics-indicators-dialog__close {
  margin-left: auto;
  border: 0;
  background: transparent;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}

.statistics-indicators-dialog__body {
  overflow: auto;
  padding: 8px 10px;
}

.statistics-indicators-dialog__row {
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 5px 0;
  font-size: 13px;
}

.statistics-indicators-dialog__spacer {
  flex: 1;
}

.statistics-indicators-dialog__footer button {
  min-width: 64px;
}


.data-table-toolbar__action-button--statistics-indicators-left {
  gap: 4px;
  padding-inline: 6px;
}
.data-table-toolbar__action-button--statistics-indicators-left .content-toolbar__action-label {
  font-size: 12px;
}


/* 26.1.1.28: Planning chart layout — toolbar above the bid3 chart.
   The wrapper is flex-column so the chart gets the remaining height
   after the toolbar.  height: 100% inherits from .report-content-area
   (the parent that pages/analysis-page.js mounts the chart into)
   so the chart fills the viewport correctly. */
.planning-layout {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  min-height: 0;
}

.planning-layout__chart {
  flex: 1;
  min-height: 0;
  min-width: 0;
  display: block;
}

/* Toolbar — frequency dropdown + four checkboxes. Compact, single
   line, mirrors the React reference's ui5-toolbar layout without
   bringing in a UI5 dependency. */
.planning-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  padding: 8px 12px;
  border-bottom: 1px solid #e0e0e0;
  font-size: 13px;
  background: #fafafa;
  flex: 0 0 auto;
}

.planning-toolbar__interval {
  border: 1px solid #c8c8c8;
  border-radius: 4px;
  padding: 4px 8px;
  font: inherit;
  background: #ffffff;
  cursor: pointer;
}

.planning-toolbar__interval:hover {
  border-color: #1a4a87;
}

.planning-toolbar__check {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  cursor: pointer;
  user-select: none;
}

.planning-toolbar__check input[type="checkbox"] {
  margin: 0;
  cursor: pointer;
}
