/*
 * Boomerang IQ brand palette as CSS custom properties.
 * Source: Boomerang_IQ_Brand_Palette.pdf / boomerang_iq_brand.html.
 *
 * Rule: brand-red is chrome-only (nav, primary actions, rules). Never use
 * --red inside a data view - in data contexts red means critical status
 * (--status-critical) and nothing else.
 */

:root {
  --red: #EF2437;         /* chrome only - nav, primary actions, rules */
  --plane: #110A0F;       /* page background */
  --surface: #160D14;     /* cards, panels */
  --raised: #1E1219;      /* tiles within cards */
  --ink-1: #FFFFFF;       /* primary text */
  --ink-2: #C9C2C5;       /* body text */
  --ink-3: #8E8489;       /* muted text, axes, captions */
  --rule: #2E2229;        /* borders */
  --grid: #2A1F25;        /* gridlines */

  --status-good: #0CA30C;
  --status-warning: #FAB219;
  --status-serious: #EC835A;
  --status-critical: #D03B3B;

  --series-1: #3987E5;
  --series-2: #D95926;
  --series-3: #199E70;
  --series-4: #C98500;
  --series-5: #9085E9;
  --series-6: #008300;

  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--plane);
  color: var(--ink-2);
  font-family: var(--font-sans);
}

a {
  color: var(--red);
}

.surface {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 8px;
}

/* Solid red bar across a card's top edge - currently only
   accessRestricted.js (2026-08-25). Chrome context, not a data view,
   so --red is the correct palette choice per the brand doc. Combine
   with .surface on the same element (border-radius already rounds this
   bar's top corners along with the rest of the card). */
.accent-bar-top {
  border-top: 4px solid var(--red);
}

.raised {
  background: var(--raised);
  border-radius: 6px;
}

.text-primary {
  color: var(--ink-1);
}

.text-muted {
  color: var(--ink-3);
}

/* Shared field-label convention (layout.js's textField/selectField/
   cascadingSelectField) - reserves enough height for 2 lines at this
   form label's actual rendered size (measured: 1.35rem per line at
   16px/normal line-height) so a field whose label wraps to 2 lines
   doesn't push its own input out of alignment with siblings in the
   same flex-wrap row whose labels stay on 1 line. */
.field-label {
  display: block;
  min-height: 2.7rem;
}

.status-good { color: var(--status-good); }
.status-warning { color: var(--status-warning); }
.status-serious { color: var(--status-serious); }
.status-critical { color: var(--status-critical); }

.btn-primary {
  background: var(--red);
  color: var(--ink-1);
  border: none;
  border-radius: 6px;
  padding: 0.6rem 1.2rem;
  font-weight: 600;
  cursor: pointer;
}

.btn-secondary {
  background: transparent;
  color: var(--ink-1);
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 0.6rem 1.2rem;
  font-weight: 600;
  cursor: pointer;
}

.btn-secondary:hover {
  border-color: var(--ink-3);
}

/* =========================================================================
   App shell - sidebar + topbar + tabs. Used by views/returns/*.html.
   Headings use --font-heading (Poppins, a free geometric stand-in for the
   licensed Hurme Geometric Sans per the brand doc). Dense UI/body text
   stays on the system sans stack.
   ========================================================================= */

:root {
  --font-heading: 'Poppins', var(--font-sans);
  --sidebar-width: 240px;
}

.app-shell {
  display: grid;
  grid-template-columns: var(--sidebar-width) 1fr;
  min-height: 100vh;
}

/* --- Sidebar --- */

.sidebar {
  background: var(--surface);
  border-right: 1px solid var(--rule);
  display: flex;
  flex-direction: column;
  padding: 1.25rem 1rem;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(201, 194, 197, 0.35) transparent; /* --ink-2, translucent - no rgba() form of the token exists */
}

.sidebar::-webkit-scrollbar { width: 6px; }
.sidebar::-webkit-scrollbar-track { background: transparent; }
.sidebar::-webkit-scrollbar-thumb {
  background: rgba(201, 194, 197, 0.35);
  border-radius: 3px;
}
.sidebar::-webkit-scrollbar-thumb:hover {
  background: rgba(201, 194, 197, 0.55);
}

.sidebar-logo {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.25rem 0.5rem 1.5rem;
}

.sidebar-logo img {
  width: 28px;
  height: auto;
  display: block;
}

.sidebar-logo .wordmark {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-3);
  line-height: 1.3;
}

.sidebar-nav {
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 1;
}

.sidebar-nav li {
  margin-bottom: 0.15rem;
}

.sidebar-nav a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.55rem 0.75rem;
  border-radius: 6px;
  color: var(--ink-2);
  text-decoration: none;
  font-size: 0.9rem;
  border-left: 3px solid transparent;
}

.sidebar-nav a:hover {
  background: var(--raised);
}

.sidebar-nav a.active {
  color: var(--ink-1);
  border-left-color: var(--red);
  background: var(--raised);
  font-weight: 600;
}

.sidebar-nav a.disabled {
  color: var(--ink-3);
  cursor: default;
  pointer-events: none;
}

.sidebar-nav .nav-group-label {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* Expand-all/collapse-all toggle, above the nav list. Same typography as
   .nav-group-label so it reads as part of the same muted-header family
   rather than competing with the real nav items below it. */
.nav-groups-toggle-all {
  display: block;
  width: 100%;
  margin-bottom: 0.25rem;
  padding: 0.4rem 0.75rem;
  border: none;
  border-radius: 6px;
  background: none;
  font: inherit;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-align: left;
  color: var(--ink-3);
  cursor: pointer;
}

.nav-groups-toggle-all:hover {
  background: var(--raised);
  color: var(--ink-2);
}

/* Collapsible hub group (Products Hub, Regulatory Returns Hub, Settings
   Hub, Config Hub). State lives on .nav-group-toggle[aria-expanded] -
   everything below keys off that attribute, no separate class needed.
   See the "collapsible sidebar groups" comment in public/app.js. */
.nav-group-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  width: 100%;
  background: none;
  border: none;
  padding: 0.9rem 0.75rem 0.35rem;
  font: inherit;
  text-align: left;
  cursor: pointer;
  color: inherit;
}

.nav-group-toggle:hover {
  background: var(--raised);
  border-radius: 6px;
}

.nav-group-chevron {
  color: var(--ink-3);
  flex-shrink: 0;
  transition: transform 0.2s ease;
}

.nav-group-toggle[aria-expanded="true"] .nav-group-chevron {
  transform: rotate(180deg);
}

/* Grid-based auto-height collapse - transitions smoothly without needing
   JS to measure pixel heights. */
.nav-group-collapse {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.2s ease;
}

.nav-group-toggle[aria-expanded="true"] + .nav-group-collapse {
  grid-template-rows: 1fr;
}

.nav-group-items {
  overflow: hidden;
  min-height: 0;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Collapsible content-panel section (e.g. Company/Group "Details") -
   same aria-expanded + CSS-grid auto-height technique as
   .nav-group-toggle/.nav-group-collapse above, but a SEPARATE class
   pair on purpose: the sidebar's "Expand all"/"Collapse all" control
   toggles every .nav-group-toggle on the page indiscriminately, and a
   content section has nothing to do with the sidebar - sharing the
   class would silently couple them. See the matching listener in
   public/app.js (same pattern as the sidebar one, different selector). */
.section-toggle {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  background: none;
  border: none;
  padding: 0;
  margin: 0 0 0.25rem;
  font: inherit;
  text-align: left;
  cursor: pointer;
  color: inherit;
}

.section-toggle-label {
  font-family: var(--font-heading);
  color: var(--ink-1);
  font-size: 1rem;
}

.section-chevron {
  color: var(--ink-3);
  flex-shrink: 0;
  transition: transform 0.2s ease;
}

.section-toggle[aria-expanded="true"] .section-chevron {
  transform: rotate(180deg);
}

.section-collapse {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.2s ease;
}

.section-toggle[aria-expanded="true"] + .section-collapse {
  grid-template-rows: 1fr;
}

.section-collapse > div {
  overflow: hidden;
  min-height: 0;
}

@media (prefers-reduced-motion: reduce) {
  .section-collapse,
  .section-chevron {
    transition: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .nav-group-collapse,
  .nav-group-chevron {
    transition: none;
  }
}

/* Sub-group label (e.g. "SmartHub" inside "Products Hub") - one indent
   step in from a plain group label, and visually quieter so the hierarchy
   reads at a glance: group > subgroup > item. */
.sidebar-nav .nav-subgroup-label {
  padding: 0.4rem 0.75rem 0.2rem 1.5rem;
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-3);
  opacity: 0.7;
}

.sidebar-nav a.nav-lvl-1 {
  padding-left: 1.5rem;
}

.sidebar-nav a.nav-lvl-2 {
  padding-left: 2.25rem;
}

.pill-soon {
  font-size: 0.65rem;
  letter-spacing: 0.05em;
  color: var(--ink-3);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 0.1rem 0.45rem;
}

/* Ticket unread-count badge (layout.js's linkItem badgeCount param,
   currently only the Support Hub's Tickets item) - sidebar navigation is
   chrome, not a data view, so --red is the correct palette choice here
   per the brand doc ("chrome-only... nav, primary actions, rules"), not
   an exception to the "red means critical status in data views" rule -
   this isn't a data view. Never rendered at all when the count is 0
   (linkItem's own badgeCount default), so there's no "0" state to style. */
.nav-badge-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.1rem;
  height: 1.1rem;
  padding: 0 0.35rem;
  border-radius: 999px;
  background: var(--red);
  color: var(--ink-1);
  font-size: 0.65rem;
  font-weight: 700;
  line-height: 1;
}

.sidebar-user {
  border-top: 1px solid var(--rule);
  padding-top: 1rem;
  margin-top: 1rem;
  font-size: 0.85rem;
}

.sidebar-user .name {
  color: var(--ink-1);
  font-weight: 600;
}

.sidebar-user .role {
  color: var(--ink-3);
  font-size: 0.75rem;
}

.logout-form {
  margin-top: 0.75rem;
}

/* Matches .sidebar-nav a's padding/radius/hover treatment so it reads as
   part of the same nav family, but stays muted (--ink-3, not --ink-1/red)
   so it never competes visually with the real nav items above it. */
.logout-button {
  display: flex;
  align-items: center;
  width: 100%;
  padding: 0.55rem 0.75rem;
  border: none;
  border-radius: 6px;
  background: none;
  color: var(--ink-3);
  font: inherit;
  font-size: 0.85rem;
  text-align: left;
  cursor: pointer;
}

.logout-button:hover {
  background: var(--raised);
  color: var(--ink-2);
}

/* --- Main column / topbar --- */

.main-column {
  display: flex;
  flex-direction: column;
  min-width: 0; /* allow child tables to shrink/scroll instead of blowing out the grid */
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.75rem;
  padding: 1rem 1.5rem;
  border-bottom: 1px solid var(--rule);
}

.topbar-left {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

/* Hidden by default - the sidebar (visible >900px) already carries the
   full brand lockup, so showing it again here would be redundant. Shown
   only under the 900px breakpoint below, where the sidebar becomes an
   off-canvas drawer and there's otherwise no branding on screen at all
   until it's opened. Height-constrained (not width) since the lockup's
   ~3.24:1 aspect ratio wants horizontal room a narrow topbar doesn't
   have; 24px fits comfortably inside the topbar's measured 32.8px
   content-row height at the narrowest (<640px) tier. */
.topbar-brand {
  display: none;
  height: 24px;
  width: auto;
}

.topbar-selectors {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.selector {
  background: var(--raised);
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: 0.4rem 0.75rem;
  font-size: 0.85rem;
  color: var(--ink-2);
}

.selector .label {
  color: var(--ink-3);
  margin-right: 0.35rem;
}

.deadline-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: rgba(250, 178, 25, 0.12);
  border: 1px solid rgba(250, 178, 25, 0.35);
  color: var(--status-warning);
  border-radius: 999px;
  padding: 0.4rem 0.9rem;
  font-size: 0.85rem;
  font-weight: 600;
  white-space: nowrap;
}

.role-badge {
  display: inline-flex;
  align-items: center;
  background: var(--raised);
  border: 1px solid var(--rule);
  color: var(--ink-2);
  border-radius: 999px;
  padding: 0.4rem 0.9rem;
  font-size: 0.85rem;
  font-weight: 600;
  white-space: nowrap;
}

.deadline-pill .dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  display: inline-block;
}

.tabs {
  display: flex;
  gap: 1.5rem;
  padding: 0 1.5rem;
  border-bottom: 1px solid var(--rule);
}

.tabs a {
  display: inline-block;
  padding: 0.9rem 0.1rem;
  color: var(--ink-3);
  text-decoration: none;
  font-size: 0.9rem;
  font-weight: 600;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}

.tabs a.active {
  color: var(--ink-1);
  border-bottom-color: var(--red);
}

.page-content {
  padding: 1.5rem;
}

.page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.page-header h1 {
  font-family: var(--font-heading);
  color: var(--ink-1);
  font-size: 1.4rem;
  margin: 0 0 0.25rem;
}

.page-header .subtitle {
  color: var(--ink-3);
  font-size: 0.9rem;
  margin: 0;
}

.page-header-actions {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.illustrative-tag {
  color: var(--ink-3);
  font-size: 0.75rem;
  white-space: nowrap;
}

.footnote {
  color: var(--ink-3);
  font-size: 0.8rem;
  margin-top: -0.5rem;
  margin-bottom: 1.5rem;
}

/* --- Stat tiles --- */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.stat-tile {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: 1rem 1.1rem;
}

.stat-tile .label {
  color: var(--ink-3);
  font-size: 0.8rem;
  margin-bottom: 0.5rem;
}

.stat-tile .value {
  color: var(--ink-1);
  font-size: 1.7rem;
  font-weight: 700;
  font-family: var(--font-heading);
}

.stat-tile .value .unit {
  font-size: 1rem;
  color: var(--ink-3);
  font-weight: 400;
}

.stat-tile .delta {
  margin-top: 0.5rem;
  font-size: 0.8rem;
  font-weight: 600;
}

/* --- Status badges (reserved status colours - never reused as chart series) --- */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0.2rem 0.55rem;
  border-radius: 999px;
  white-space: nowrap;
}

.badge::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}

.badge-good { color: var(--status-good); background: rgba(12, 163, 12, 0.12); }
.badge-warning { color: var(--status-warning); background: rgba(250, 178, 25, 0.12); }
.badge-serious { color: var(--status-serious); background: rgba(236, 131, 90, 0.12); }
.badge-critical { color: var(--status-critical); background: rgba(208, 59, 59, 0.12); }
.badge-muted { color: var(--ink-3); background: var(--raised); }

/* Forecasting Hub, Stage 2 of 6 - pipeline board stage selects. Same
   grey/amber/green colours as .badge-muted/.badge-warning/.badge-good
   above (identical color/background values), but shaped for a real
   <select> form control rather than a <span> - .badge's display:
   inline-flex + ::before dot don't render correctly on a replaced
   element like <select> in most browsers, so this is a deliberately
   separate, minimal rule set rather than reusing .badge directly. */
.pipeline-stage-select {
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0.25rem 0.5rem;
  border-radius: 6px;
  border: 1px solid var(--rule);
  cursor: pointer;
}
.pipeline-stage-select.status-pending { color: var(--ink-3); background: var(--raised); }
.pipeline-stage-select.status-in_progress { color: var(--status-warning); background: rgba(250, 178, 25, 0.12); }
.pipeline-stage-select.status-completed { color: var(--status-good); background: rgba(12, 163, 12, 0.12); }

.delta.status-good::before,
.delta.status-warning::before,
.delta.status-serious::before,
.delta.status-critical::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  margin-right: 0.35rem;
}

/* --- SmartHub demo-data banner (compliance-dashboards-v1-build-brief.md
   SS4 step 11) - deployed to production with the mock data still in
   place, so this makes that unmistakable on every page it appears on.
   Reuses the exact warning-tint recipe deadline-pill/badge-warning
   already use (rgba(250,178,25,0.12) fill, 0.35-alpha border,
   --status-warning text) rather than inventing a new color - matches
   how this app already renders a warning-level state. In-flow content
   banner, not fixed/chrome: CLAUDE.md's own rule reserves a fixed,
   brand-red banner for chrome/identity state (the impersonation banner
   below) - this is a warning about the DATA on the page, not the app's
   chrome, so it sits in the normal page flow the same way
   broadcast-banner already does, not pinned to the viewport. */
.demo-data-banner {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  background: rgba(250, 178, 25, 0.12);
  border: 1px solid rgba(250, 178, 25, 0.35);
  border-radius: 6px;
  padding: 0.75rem 1rem;
  margin-bottom: 1.5rem;
  color: var(--status-warning);
  font-weight: 600;
  font-size: 0.9rem;
}

/* --- Broadcast message banners (admin-authored, dismissible per session) --- */

.broadcast-banners {
  margin-bottom: 1.5rem;
}

.broadcast-banner {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  background: var(--raised);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--series-1);
  border-radius: 6px;
  padding: 0.75rem 1rem;
  margin-bottom: 0.75rem;
  color: var(--ink-2);
}

.broadcast-banner-body {
  white-space: pre-wrap;
  flex: 1 1 auto;
}

.broadcast-banner-dismiss {
  flex: 0 0 auto;
  background: none;
  border: none;
  color: var(--ink-3);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  padding: 0.15rem 0.4rem;
}

.broadcast-banner-dismiss:hover {
  color: var(--ink-1);
}

/* --- Panels (field completeness, needs attention, etc) --- */

.panel {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: 1.25rem;
  margin-bottom: 1.5rem;
}

.panel h2 {
  font-family: var(--font-heading);
  color: var(--ink-1);
  font-size: 1rem;
  margin: 0 0 0.25rem;
}

.panel .panel-subtitle {
  color: var(--ink-3);
  font-size: 0.8rem;
  margin: 0 0 1rem;
}

/* --- Progress / field completeness bars --- */

.bar-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0;
}

.bar-row .bar-label {
  width: 140px;
  flex-shrink: 0;
  color: var(--ink-2);
  font-size: 0.85rem;
}

.bar-track {
  flex: 1;
  background: var(--raised);
  border-radius: 4px;
  height: 10px;
  overflow: hidden;
}

.bar-fill {
  display: block;
  height: 100%;
  background: var(--series-1);
  border-radius: 4px;
}

.bar-row .bar-value {
  width: 48px;
  text-align: right;
  flex-shrink: 0;
  color: var(--ink-1);
  font-size: 0.85rem;
  font-weight: 600;
}

/* SmartHub breakdown/top-venues rows (src/views/smarthub/components.js)
   reuse .bar-row/.bar-track/.bar-fill above as-is, but show longer text
   ("131 (71.2%)" / a bare venue count) than readiness.js's own plain "X%" -
   an additive modifier, not a change to .bar-value itself, so
   readiness.js's existing page is untouched. */
.bar-row .bar-value.bar-value-wide {
  width: auto;
  min-width: 84px;
}

/* --- SmartHub shared components (src/views/smarthub/components.js) --- */

/* Platform dashboard period selector v1 - replaces the old quarter-only
   .quarter-selector/.quarter-nav-btn/.quarter-label-group/.quarter-label/
   .quarter-range/.quarter-reset-tag rules (a centered Prev/Next arrow
   pair) with a real <select>-based control (renderPeriodSelector). Reuses
   .bc-select directly for the select itself (same look as the scope-
   toggle's own selects, applied here via a plain class on the <select>,
   not a copy of its rules) rather than inventing new select styling. */
.period-selector {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.6rem;
}

/* No separate .period-range label - the select's own closed-state text
   already shows "<label> — <range>" per option (renderPeriodSelector),
   so a standalone range span would just repeat it a second time; caught
   visually before shipping, not left in as dead CSS. */

/* Custom range's own date inputs + Apply button (only rendered once the
   resolved period is actually Custom - see renderPeriodSelector's own
   comment on why this is a real server-rendered state, not a client-side
   reveal). */
.custom-range-fields {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.custom-range-fields input[type="date"] {
  padding: 0 0.6rem;
  border: 1px solid var(--rule);
  border-radius: 6px;
  background: var(--surface);
  color: var(--ink-1);
  font-size: 0.85rem;
  font-family: inherit;
}

.custom-range-sep {
  color: var(--ink-3);
}

/* Layout-stability fix (same bug CLASS as the earlier scope-picker
   layout-shift fix, found via an explicit before/after
   getBoundingClientRect() check comparing "This Quarter" (shortest
   period-selector state) against an active Custom range (widest state -
   both date inputs + the Apply button visible), not assumed stable from
   a screenshot alone - see this engagement's own build report for the
   exact before/after numbers).
   First attempt matched vertical PADDING across .bc-select/the date
   input/.btn-primary instead of this - that didn't actually work: a
   <select>, a native <input type="date"> (which reserves extra width
   internally for its own calendar-icon affordance) and a <button> each
   have a different intrinsic content height from the browser's own UA
   styling even at identical padding, so matching padding alone left a
   different ~2px mismatch rather than removing it. An explicit, equal
   `height` on all three (border-box sizing already applies sitewide -
   see the `* { box-sizing: border-box; }` rule near the top of this
   file) is the only way to guarantee .period-selector's own row height
   - and so the position of the sibling .scope-toggle-row below it -
   never depends on which control happens to be present. Scoped to
   these two specific selectors, not a change to .bc-select or
   .btn-primary themselves - both are shared by controls elsewhere in
   the app (the scope-toggle's own selects, auditLogList.js's "Filter",
   various "Save" actions) that must keep their own sizing untouched. */
.period-select,
.custom-range-fields input[type="date"],
.custom-range-fields .btn-primary {
  height: 34px;
}

/* Breadcrumb drill-down scope toggle (step 19), replacing the old pills
   row + conditionally-shown-selects layout (src/views/smarthub/
   components.js's renderScopeToggle). flex-wrap: nowrap + overflow-x:
   auto is the hard "all on a line" constraint from design review -
   horizontal scroll on this one control if space is genuinely too tight
   at some viewport, wrapping onto a second line is not acceptable even
   for super_admin's full 4-segment case. */
.scope-toggle-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: nowrap;
  overflow-x: auto;
}

/* Platform dashboard rebuild follow-up - a real layout bug found by
   Paul's own live check: the row's OWN contents never wrap (flex-wrap:
   nowrap above already guarantees that), but .page-header (this row's
   parent on all 4 pages that render it - dashboard.js/selfExclusion.js/
   ageChallenges.js/interactions.js) is `display: flex; flex-wrap: wrap`,
   with this row as a sibling flex item next to the quarter selector.
   None of .bc-select's rules give it a fixed width, so each select
   sizes to its own currently-selected option's text - confirmed
   directly (rendered HTML at Platform/Group/Company/Venue level always
   has the identical 3 selects/1 label/3 chevrons; only the selected
   text length changes, e.g. "Jump to a company…" (18 chars) vs
   "Boomerang MSA" (13 chars)). That varying total width crosses
   .page-header's own wrap threshold differently on each navigation,
   so the whole row visibly jumps between sitting beside the quarter
   selector and wrapping onto its own line below it, depending on
   nothing more meaningful than how long the currently-selected
   company/venue name happens to be.
   flex-basis: 100% forces this row to always claim a full line on its
   own within .page-header, regardless of its own rendered width - a
   deterministic position (always its own line, below the quarter
   selector) instead of one that depends on content-length arithmetic.
   Scoped to `.page-header .scope-toggle-row` deliberately, not a change
   to .page-header's own base rule - that class is shared by several
   other, unrelated page headers (src/views/layout.js) that have no
   scope-toggle-row and should keep their own existing wrap behaviour
   untouched. */
.page-header .scope-toggle-row {
  flex-basis: 100%;
}

.scope-crumb-label {
  padding: 0.4rem 0.75rem;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--ink-2);
  background: var(--plane);
  border: 1px solid var(--rule);
  border-radius: 6px;
  white-space: nowrap;
  flex-shrink: 0;
}

.scope-chevron {
  color: var(--ink-3);
  flex-shrink: 0;
}

.bc-select {
  appearance: none;
  -webkit-appearance: none;
  padding: 0.4rem 1.6rem 0.4rem 0.6rem;
  border: 1px solid var(--rule);
  border-radius: 6px;
  background: var(--surface);
  color: var(--ink-1);
  font-size: 0.85rem;
  font-family: inherit;
  white-space: nowrap;
  flex-shrink: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23C9C2C5' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
}

.bc-select:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

/* SmartHub sub-nav's own active item (src/views/smarthub/components.js) -
   non-clickable, so it's a <span>, not an <a> - the existing
   .sidebar-nav a.active/.nav-lvl-2 rules (above) are scoped to <a> only,
   so this mirrors their exact declarations for the one non-interactive
   case, rather than widening those selectors and risking real nav links. */
.sidebar-nav span.active {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.55rem 0.75rem;
  border-radius: 6px;
  color: var(--ink-1);
  font-size: 0.9rem;
  font-weight: 600;
  border-left: 3px solid var(--red);
  background: var(--raised);
}

.sidebar-nav span.nav-lvl-2 {
  padding-left: 2.25rem;
}

/* --- Needs attention list --- */

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

.attention-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.75rem 0;
  border-bottom: 1px solid var(--rule);
  flex-wrap: wrap;
}

.attention-list li:last-child {
  border-bottom: none;
}

.attention-list .item-title {
  color: var(--ink-1);
  font-size: 0.9rem;
}

.attention-list .item-tag {
  color: var(--ink-3);
  font-size: 0.8rem;
  margin-right: auto;
  padding-left: 1rem;
}

/* --- Data tables --- */

.table-scroll {
  overflow-x: auto;
  border: 1px solid var(--rule);
  border-radius: 8px;
}

table.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
  min-width: 640px; /* forces horizontal scroll instead of crushing columns on mobile */
}

table.data-table th {
  text-align: left;
  color: var(--ink-3);
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  font-weight: 600;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--rule);
  background: var(--surface);
}

table.data-table td {
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--rule);
  color: var(--ink-2);
}

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

table.data-table a {
  color: var(--ink-2);
  text-decoration: underline;
}

table.data-table a:hover {
  color: var(--red);
}

.sitemap-detail-row td {
  background: var(--plane);
  padding: 0.75rem 1rem 1rem;
}

.sitemap-detail p {
  color: var(--ink-2);
  font-size: 0.82rem;
  line-height: 1.5;
  margin: 0 0 0.5rem;
}

.sitemap-detail .sitemap-not-built {
  color: var(--ink-3);
  font-style: italic;
}

.sitemap-features {
  margin: 0;
  padding-left: 1.1rem;
  color: var(--ink-3);
  font-size: 0.8rem;
  line-height: 1.6;
}

.sitemap-features li {
  margin-bottom: 0.2rem;
}

/* --- Mobile nav toggle --- */

.nav-toggle {
  display: none;
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: 6px;
  color: var(--ink-1);
  padding: 0.4rem 0.6rem;
  cursor: pointer;
}

.sidebar-backdrop {
  display: none;
}

/* =========================================================================
   Ticket toast notifications (public/app.js) - first component of this
   kind in the app, so a few real decisions worth recording:

   - Fixed top-right, above everything (z-index above the mobile sidebar
     drawer's own 40/30) - internal/staff roles only, real SLA stakes on
     response time, so this has to actually be noticed, not sit quietly
     in a corner.
   - No auto-dismiss timer, on purpose - an SLA-relevant event that
     silently expired off-screen unread would defeat the entire point of
     building this. Manual dismiss (the x) or clicking through to the
     ticket are the only ways a toast goes away.
   - --red for the accent bar and hover border, same "chrome, not a data
     view" reasoning as .nav-badge-count above - this is a system
     notification, not a status indicator inside a data view.
   - The stack container itself is pointer-events: none so the empty
     space around toasts never blocks clicks on the real page underneath
     it; each toast re-enables pointer-events on itself only.
   - New toasts are prepended (public/app.js), so this is visually
     newest-on-top - a rapid burst of events stacks downward rather than
     any one silently replacing another.
   ========================================================================= */

.ticket-toast-stack {
  position: fixed;
  top: 1rem;
  right: 1rem;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  width: min(340px, calc(100vw - 2rem));
  pointer-events: none;
}

.ticket-toast {
  pointer-events: auto;
  position: relative;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-left: 4px solid var(--red);
  border-radius: 8px;
  padding: 0.85rem 2rem 0.85rem 1rem;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
  cursor: pointer;
  animation: ticket-toast-in 0.2s ease-out;
}

.ticket-toast:hover {
  border-color: var(--red);
}

/* Customer Issue Flagging, Stage 1 of 2 - a visually distinct toast
   variant for "a customer needs help", not the default --red every
   other toast (tickets, ready-for-review) uses. --status-critical is
   the real, actionable-status color (CLAUDE.md's brand rule: red in a
   data view means critical status, never chrome) - a blocked case
   waiting on a reviewer is exactly that. */
.ticket-toast-critical {
  border-left-color: var(--status-critical);
}

.ticket-toast-critical:hover {
  border-color: var(--status-critical);
}

.ticket-toast-label {
  font-family: var(--font-heading);
  color: var(--ink-1);
  font-size: 0.85rem;
  font-weight: 600;
  margin-bottom: 0.2rem;
}

.ticket-toast-detail {
  color: var(--ink-2);
  font-size: 0.8rem;
}

.ticket-toast-dismiss {
  position: absolute;
  top: 0.5rem;
  right: 0.6rem;
  background: none;
  border: none;
  color: var(--ink-3);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  padding: 0.15rem;
}

.ticket-toast-dismiss:hover {
  color: var(--ink-1);
}

@keyframes ticket-toast-in {
  from { opacity: 0; transform: translateX(12px); }
  to { opacity: 1; transform: translateX(0); }
}

/* =========================================================================
   Super Admin impersonation banner (docs/super-admin-impersonation-v1.md
   section 6) - fixed, not sticky: genuinely visible regardless of which
   element ends up scrolling, not tied to any one container. z-index sits
   ABOVE the modal backdrop below (1100) - deliberately: this must stay
   visible and legible even while a confirm modal is open, not be dimmed
   underneath its overlay. Brand-red background is the one place outside
   nav/primary-actions this app uses --red for chrome (CLAUDE.md's own
   rule: brand-red is chrome-only, never a data-view status color) - a
   persistent, hard-to-miss identity banner is chrome, not a data view.
   body.impersonating's own margin-top below reserves exactly this bar's
   height so it never overlaps .app-shell's own content underneath -
   only applied when the banner itself is actually rendered.
   ========================================================================= */

.impersonation-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1150;
  background: var(--red);
  color: var(--ink-1);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 0.6rem 1.5rem;
  font-size: 0.9rem;
  font-weight: 600;
  text-align: center;
}

.impersonation-banner-return {
  background: var(--ink-1);
  color: var(--red);
  border: none;
  border-radius: 6px;
  padding: 0.3rem 0.9rem;
  font-weight: 700;
  font-size: 0.85rem;
  cursor: pointer;
}

.impersonation-banner-return:hover,
.impersonation-banner-return:focus-visible {
  opacity: 0.85;
}

body.impersonating .app-shell {
  margin-top: 2.75rem;
}

/* =========================================================================
   Accessible modal (public/app.js) - Terms/Privacy/Cookie Policy links on
   the login page, first modal in this app. One shared backdrop/dialog
   pair, content swapped in per open - the real focus trap/inert-
   background/focus-return logic all live in app.js, this is just the
   visuals. z-index sits above the ticket toast stack (1000) - a modal is
   a blocking foreground interaction, a toast is a passive notification.
   ========================================================================= */

.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 1100;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
}

.modal-backdrop[hidden] {
  display: none;
}

.modal {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 8px;
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.55);
  max-width: 560px;
  width: 100%;
  max-height: min(80vh, 640px);
  overflow-y: auto;
  padding: 2rem 1.75rem 1.75rem;
}

.modal-close {
  position: absolute;
  top: 0.75rem;
  right: 0.85rem;
  background: none;
  border: none;
  color: var(--ink-3);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  padding: 0.25rem;
}

.modal-close:hover,
.modal-close:focus-visible {
  color: var(--ink-1);
}

.modal-heading {
  font-family: var(--font-heading);
  font-size: 1.2rem;
  color: var(--ink-1);
  margin: 0 0 0.25rem;
  padding-right: 1.5rem;
}

.modal-meta {
  margin: 0 0 1.25rem;
  font-size: 0.85rem;
}

.modal-body {
  white-space: pre-wrap;
  font-size: 0.9rem;
  color: var(--ink-2);
}

.modal-confirm-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.75rem;
  margin-top: 1.5rem;
}

/* =========================================================================
   Responsive breakpoints
   ========================================================================= */

@media (max-width: 1080px) {
  .stat-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

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

  .nav-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  .topbar-brand {
    display: block;
  }

  .sidebar {
    position: fixed;
    z-index: 40;
    left: 0;
    top: 0;
    width: var(--sidebar-width);
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    box-shadow: 2px 0 12px rgba(0, 0, 0, 0.4);
  }

  .app-shell.nav-open .sidebar {
    transform: translateX(0);
  }

  .app-shell.nav-open .sidebar-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 30;
  }

  .topbar-selectors {
    width: 100%;
  }

  .selector {
    flex: 1;
    min-width: 0;
  }
}

@media (max-width: 640px) {
  .stat-grid {
    grid-template-columns: 1fr;
  }

  .page-content {
    padding: 1rem;
  }

  .topbar {
    padding: 0.85rem 1rem;
  }

  .tabs {
    padding: 0 1rem;
    gap: 1rem;
    overflow-x: auto;
  }

  .bar-row .bar-label {
    width: 96px;
    font-size: 0.78rem;
  }
}

/* Show/hide password toggle - injected around every input[type="password"]
   by public/app.js, not hand-added per form (see that file). The wrapper
   is a plain block div so an input's own inline width:100% still fills
   it exactly as before; the button is absolutely positioned over the
   input's own right-hand padding (app.js pads the input to make room),
   never a sibling that could shift layout. */
.password-field-wrap {
  position: relative;
  display: block;
}

.password-toggle {
  position: absolute;
  top: 50%;
  right: 0.4rem;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: 0.25rem;
  margin: 0;
  cursor: pointer;
  color: var(--ink-3);
  line-height: 0;
}

.password-toggle:hover,
.password-toggle:focus-visible {
  color: var(--ink-1);
}

/* Permissions Matrix (Stage 3) - the admin page at /config/permissions.
   Blue (--series-1) marks an explicit rule, never red: red is chrome-only
   in this brand (see the palette comment at the top of this file). */
.pm-table { min-width: 900px; }
.pm-table th, .pm-table td { text-align: center; }
.pm-table tbody th {
  text-align: left;
  text-transform: none;
  letter-spacing: normal;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--ink-2);
  background: transparent;
}
.pm-block-row th, .pm-block-row td { background: var(--raised); }
.pm-cell { white-space: nowrap; }
.pm-cell.pm-explicit { box-shadow: inset 0 0 0 2px var(--series-1); border-radius: 4px; }
.pm-cell.pm-shadowed { outline: 2px dashed var(--ink-3); outline-offset: -4px; }
.pm-cell.pm-saving, .pm-block-cell.pm-saving { opacity: 0.5; }
.pm-reset {
  background: none;
  border: 0;
  color: var(--ink-3);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  padding: 0 0.25rem;
}
.pm-reset:hover { color: var(--ink-1); }
.pm-reset[hidden] { display: none; }
.pm-legend { padding: 0 0.35rem; border-radius: 3px; }
.pm-legend-explicit { box-shadow: inset 0 0 0 2px var(--series-1); }
.pm-status { min-height: 1.2rem; margin: 0; font-size: 0.8rem; color: var(--ink-3); }
.pm-status.pm-error { color: var(--status-serious); }
