/* B-side shared component library — copied identically into bside-universe, bside-hunter,
   bside-market-research, bside-settings, bside-client-onboarding, bside-communication-hub,
   and every future B-side application. Edit in one place, then re-sync the others.
   Requires tokens.css to be loaded first. Every component below pulls from tokens only —
   no hardcoded hex/px in here beyond 0/1px/2px structural values. */

*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }
/* Content stored in both languages (e.g. Radar items): show only the viewer's - i18n.js sets
   <html lang="fr"> when French is the preferred language (profile menu). */
html[lang="fr"] .i18n-only-en, html:not([lang="fr"]) .i18n-only-fr { display: none !important; }

html, body { margin: 0; }
body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.5;
}
h1, h2, h3, h4 { font-family: var(--font-heading); font-weight: 700; margin: 0; }
p { margin: 0; }
a { color: var(--accent-700); }

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

/* ================= .btn ================= */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-2);
  font: inherit;
  font-weight: 600;
  font-size: 13.5px;
  text-align: left;
  border: var(--border-rule) solid transparent;
  border-radius: var(--radius);
  padding: 9px 18px;
  cursor: pointer;
  background: transparent;
  color: var(--ink);
  text-decoration: none;
  transition: background-color .1s, border-color .1s, color .1s;
}
.btn:disabled { opacity: .45; cursor: not-allowed; }

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.btn-primary:hover:not(:disabled) { background: var(--accent-600); border-color: var(--accent-600); }
.btn-primary:active:not(:disabled) { background: var(--accent-700); border-color: var(--accent-700); }

.btn-secondary {
  background: transparent;
  border-color: var(--accent-700);
  color: var(--accent-700);
}
.btn-secondary:hover:not(:disabled) { background: var(--accent-100); }
.btn-secondary:active:not(:disabled) { background: var(--accent-200); }

.btn-ghost {
  background: transparent;
  border-color: var(--ink-300);
  color: var(--ink);
}
.btn-ghost:hover:not(:disabled) { border-color: var(--accent-700); color: var(--accent-700); background: var(--accent-100); }
.btn-ghost:active:not(:disabled) { background: var(--accent-200); }

/* ghost variant for use on a dark surface (e.g. the mobile nav dropdown) */
.btn-ghost-inverse {
  border-color: rgba(255, 255, 255, .4);
  color: #ffffff;
}
.btn-ghost-inverse:hover:not(:disabled) {
  /* Literal, not var(--accent-300) - this is always on a dark surface by design (see above),
     so it must stay this exact light gold in both themes, not flip dark with the rest of the
     accent ramp the way accent-300's more common border-on-light-fill role does. */
  border-color: #ead9ae; color: #ead9ae; background: rgba(255, 255, 255, .08);
}

.btn-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font: inherit;
  padding: 0;
  width: 36px;
  height: 36px;
  border: var(--border-rule) solid var(--ink-300);
  border-radius: var(--radius);
  cursor: pointer;
  background: transparent;
  color: var(--ink);
  text-decoration: none;
  flex: none;
  transition: background-color .1s, border-color .1s, color .1s;
}
.btn-icon:disabled { opacity: .45; cursor: not-allowed; }
.btn-icon:hover:not(:disabled) { border-color: var(--accent-700); color: var(--accent-700); background: var(--accent-100); }
.btn-icon:active:not(:disabled) { background: var(--accent-200); }
.btn-icon svg { width: 18px; height: 18px; }
.btn-icon-inverse { border-color: rgba(255, 255, 255, .4); color: #ffffff; }
.btn-icon-inverse:hover:not(:disabled) {
  border-color: #ead9ae; color: #ead9ae; background: rgba(255, 255, 255, .08);
}

.btn-block { display: flex; width: 100%; justify-content: center; text-align: center; }

/* ================= .btn-split (primary action + its attached settings toggle) =================
   Two buttons rendered as one visually divided pill, so the settings gear can't be fat-fingered
   as the action button (and vice versa). The outer border lives on the wrapper (not the two
   children) so there's no doubled/misaligned edge where they meet; a solid black rule down the
   middle is the only divider. Only used with filled variants (btn-primary/btn-dark) today, so
   the wrapper border is pinned to --accent to match them. */
.btn-split {
  display: inline-flex;
  align-items: stretch;
  border: var(--border-rule) solid var(--accent);
  border-radius: var(--radius);
  overflow: hidden;
}
.btn-split > .btn { border: none; border-radius: 0; }
.btn-split > .btn-split-settings {
  width: 40px;
  padding: 0;
  justify-content: center;
  border-left: var(--border-rule) solid rgba(0, 0, 0, .6);
}
.btn-split-settings svg { width: 20px; height: 20px; }

.btn-sm { padding: 6px 12px; font-size: 12.5px; }

/* ================= .field / .input ================= */
.field { display: flex; flex-direction: column; gap: var(--space-1); }
.field-label {
  font-size: 12.5px; font-weight: 600; color: var(--ink-700);
  text-transform: uppercase; letter-spacing: .04em;
}
.input, select.input, textarea.input {
  font: inherit; font-size: 14.5px; color: var(--ink);
  background: var(--surface);
  border: var(--border-rule) solid var(--ink-300);
  border-radius: var(--radius);
  padding: 10px 12px;
  width: 100%;
}
.input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-color: var(--accent); }
/* Error, not "needs attention" - now that --danger exists (see tokens.css) this is red rather
   than the accent gold "done"/"published" already means everywhere else in the system; the two
   states read as visually opposite instead of looking like the same color. */
.field.is-invalid .input { border-color: var(--danger); }
.field.is-invalid .input:focus-visible { outline-color: var(--danger); }
.field-help { font-size: 12px; color: var(--ink-600); }
.field.is-invalid .field-help { color: var(--danger-700); font-weight: 600; }

.input-with-action { position: relative; display: flex; align-items: center; }
.input-with-action .input { padding-right: 44px; }
.input-with-action .btn-icon {
  position: absolute; right: 2px; width: 32px; height: 32px;
  border: none; background: transparent; color: var(--ink-600);
}
.input-with-action .btn-icon:hover { color: var(--accent-700); background: transparent; }

/* Hover/keyboard-focus label for icon-only buttons: data-tooltip="Edit user" (keep the
   aria-label for screen readers; i18n.js translates both). */
[data-tooltip] { position: relative; }
[data-tooltip]::after {
  content: attr(data-tooltip); position: absolute; bottom: calc(100% + 6px); left: 50%; z-index: 60;
  transform: translateX(-50%); padding: 4px 8px; border-radius: 6px; white-space: nowrap;
  background: var(--ink); color: var(--surface); font-size: 12px; font-weight: 500; line-height: 1.3;
  letter-spacing: normal; text-transform: none; opacity: 0; pointer-events: none; transition: opacity .12s;
}
[data-tooltip]:hover::after, [data-tooltip]:focus-visible::after { opacity: 1; }
/* Destructive icon button (delete / deactivate): red, not the neutral icon colour. */
.btn-icon.btn-icon-danger { color: var(--danger); }
.btn-icon.btn-icon-danger:hover { color: var(--danger-700); background: var(--danger-100); border-color: var(--danger); }

/* ================= .tag ================= */
.tag {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: 3px 9px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  white-space: nowrap;
}
.tag-accent { background: var(--accent-100); color: var(--accent-800); border-color: var(--accent-300); }
.tag-neutral { background: var(--ink-100); color: var(--ink-700); border-color: var(--ink-300); }

/* ================= .table ================= */
.table-wrap { width: 100%; overflow-x: auto; }
.table { width: 100%; border-collapse: collapse; background: var(--surface); }
.table th, .table td {
  text-align: left; padding: var(--space-3) var(--space-4);
  border-bottom: var(--border-hairline) solid var(--ink-200);
  font-size: 14px;
}
.table th {
  background: var(--ink-100);
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  color: var(--ink-700);
  border-bottom: var(--border-rule) solid var(--ink-300);
}
.table th.is-sortable { cursor: pointer; user-select: none; }
.table th.is-sortable .sort-icon { display: inline-flex; vertical-align: -3px; margin-left: 4px; opacity: .5; }
.table th.is-sortable.is-active .sort-icon { opacity: 1; color: var(--accent-700); }
.table tbody tr { min-height: 48px; }
.table tbody tr:hover { background: var(--accent-100); }
.table td.actions-cell { display: flex; gap: var(--space-2); justify-content: flex-end; }

/* ================= .nav (topbar) ================= */
.nav {
  min-height: var(--nav-height);
  background: var(--ink-100);
  color: var(--ink);
  border-bottom: var(--border-rule) solid var(--ink-300);
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
  padding: 10px var(--space-5);
  /* Sticky, not fixed - it participates in normal layout (no need for every app to pad its
     first section to compensate for it going out-of-flow) but stays put once scrolled to. */
  position: sticky; top: 0; z-index: 20;
}
/* See theme-init.js: hidden until the page's scripts have filled the header in. */
html.nav-pending .nav > * { visibility: hidden; }
.notif-popover-wrap { display: flex; }
.nav-brand { display: flex; align-items: center; gap: var(--space-3); flex: none; }
.nav-brand img { height: 32px; width: auto; display: block; }
.nav-brand-mark { cursor: pointer; }
.nav-brand-word { font-family: var(--font-heading); font-size: 20px; font-weight: 700; letter-spacing: .2px; color: var(--ink); }
.nav-links { display: flex; align-items: center; gap: var(--space-5); flex: 1; }
.nav-link {
  font-family: var(--font-heading); font-weight: 600; font-size: 14px;
  color: var(--ink-600); text-decoration: none;
  background: none; border: none; cursor: pointer;
  height: var(--nav-height); display: flex; align-items: center;
  border-bottom: var(--border-rule) solid transparent;
  padding: 0;
}
.nav-link:hover { color: var(--ink); }
.nav-link.is-active { color: var(--ink); border-bottom-color: var(--accent); }
.nav-right { display: flex; align-items: center; gap: var(--space-3); margin-left: auto; flex: none; flex-wrap: wrap; }
.nav-user { display: flex; align-items: center; gap: var(--space-2); font-size: 12.5px; color: var(--ink-600); }
.nav-user-email { color: var(--ink); font-weight: 600; }
.nav-menu-btn { display: none; }
/* Global search bar (widgets.js): only on wide screens, where it replaces the nav's search
   icon. Auto margins on both sides split the nav's free space evenly around it, so it sits
   centered between the brand/links and .nav-right without ever overlapping either. */
.nav-search-bar { display: none; }
@media (min-width: 1100px) {
  #bside-search-btn { display: none; }
  .nav:has(.nav-search-bar) .nav-links { flex: none; }
  .nav:has(.nav-search-bar) .nav-right { margin-left: 0; }
  .nav-search-bar {
    position: relative; display: flex; align-items: center; gap: var(--space-2);
    flex: 1 1 200px; max-width: 480px; min-width: 200px; margin: 0 auto; height: 36px; padding: 0 var(--space-3);
    font: inherit; font-size: 13.5px; color: var(--ink-500); text-align: left; cursor: text;
    background: var(--surface); border: var(--border-rule) solid var(--ink-300); border-radius: var(--radius);
  }
  .nav-search-bar:hover { border-color: var(--ink-500); }
  .nav-search-bar:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
  .nav-search-bar svg { flex: none; width: 16px; height: 16px; }
  .nav-search-bar-input {
    flex: 1; min-width: 0; height: 100%; padding: 0; border: 0; outline: 0; background: transparent;
    font: inherit; color: var(--ink);
  }
  .nav-search-bar-input::placeholder { color: var(--ink-500); }
  .nav-search-bar-input:focus-visible { outline: 0; }
  .nav-search-dropdown {
    position: absolute; top: calc(100% + 8px); left: 0; right: 0; z-index: 60;
    padding: var(--space-2); background: var(--surface); cursor: default;
    border: var(--border-rule) solid var(--ink-300); border-radius: var(--radius);
    box-shadow: 0 8px 24px rgba(0, 0, 0, .18);
  }
  .nav-search-bar kbd {
    flex: none; font: inherit; font-size: 11px; color: var(--ink-500);
    padding: 1px 6px; border: 1px solid var(--ink-300); border-radius: var(--radius);
  }
  /* Dark nav (ink-100) and --surface are near-identical, so lift the field a step to read as an input. */
  [data-theme="dark"] .nav-search-bar { background: var(--ink-200); }
}
.version-badge { font-size: 11.5px; font-weight: 600; color: var(--ink-500); white-space: nowrap; }

/* logged-in user badge — shared markup/classes used by hunter, market-research, settings app.js */
.user-badge { display: flex; align-items: center; gap: var(--space-2); font-size: 12.5px; color: var(--ink-600); }
.user-badge .user-email { font-weight: 600; color: var(--ink); }
.user-badge .user-role {
  padding: 2px 8px; border: 1px solid var(--ink-300);
  border-radius: var(--radius);
  text-transform: uppercase; letter-spacing: .04em; font-size: 10px;
}
.user-badge .user-logout {
  color: var(--accent-700); text-decoration: none;
  background: none; border: none; padding: 0; font: inherit; cursor: pointer;
}
.user-badge .user-logout:hover { text-decoration: underline; }

/* button + trailing meta line (e.g. "Sync criteria" + last-sync timestamp) */
.btn-with-meta { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
.btn-with-meta .meta-note { font-size: 11px; color: var(--ink-500); letter-spacing: .02em; padding-left: 2px; }
/* Reserve the caption's line while the app is still fetching it, so the header doesn't grow. */
.btn-with-meta .meta-note:empty::before { content: "\00a0"; }

/* content area always clears the fixed/persistent nav */
.app-content { padding-top: var(--space-6); }

/* ================= .dialog ================= */
.dialog-backdrop {
  position: fixed; inset: 0; background: rgba(32, 30, 29, .6);
  display: flex; align-items: flex-start; justify-content: center;
  z-index: 100; padding: var(--space-7) var(--space-4); overflow-y: auto;
}
.dialog {
  background: var(--surface);
  border: var(--border-rule) solid var(--ink);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  width: 100%; max-width: 560px;
  display: flex; flex-direction: column;
  overflow: hidden;
}
.dialog-sm { max-width: 420px; }
.dialog-md { max-width: 640px; }
.dialog-lg { max-width: 740px; }
.dialog-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-4) var(--space-5);
  border-bottom: var(--border-rule) solid var(--ink-200);
}
.dialog-head h2 { font-size: 19px; }
.dialog-body { padding: var(--space-5); display: flex; flex-direction: column; gap: var(--space-4); }
.dialog-actions {
  display: flex; justify-content: flex-end; gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-top: var(--border-rule) solid var(--ink-200);
}

/* ================= changelog entry (shared across all changelog dialogs) ================= */
.changelog-entry { padding: var(--space-4) 0; border-bottom: var(--border-hairline) solid var(--ink-200); }
.changelog-entry:last-child { border-bottom: none; }
.changelog-entry-head { display: flex; align-items: baseline; gap: var(--space-3); margin-bottom: var(--space-2); flex-wrap: wrap; }
.changelog-version { font-family: var(--font-heading); font-size: 15px; font-weight: 700; color: var(--accent-700); }
.changelog-date { font-size: 12.5px; color: var(--ink-600); }
.changelog-list { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 4px; }
.changelog-list li { font-size: 13.5px; line-height: 1.5; color: var(--ink); }

/* ================= .hr ================= */
.hr { border: none; border-top: var(--border-rule) solid var(--ink-200); margin: var(--space-5) 0; }

/* ================= .card ================= */
.card {
  background: var(--surface);
  border: var(--border-rule) solid var(--ink-200);
  border-radius: var(--radius);
  padding: var(--space-4);
  display: flex; flex-direction: column; gap: var(--space-2);
}
.card-grid { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }

/* ================= .stat-block ================= */
.stat-row { display: flex; flex-wrap: wrap; gap: 1px; background: var(--ink-200); border-bottom: var(--border-rule) solid var(--ink-200); }
.stat-block { background: var(--surface); padding: var(--space-4) var(--space-5); text-align: left; }
.stat-block b { display: block; font-family: var(--font-heading); font-size: 26px; font-weight: 700; }
.stat-block span { font-size: 11.5px; color: var(--ink-600); text-transform: uppercase; letter-spacing: .06em; }
.stat-block .field-help { display: block; text-transform: none; letter-spacing: 0; margin-top: 2px; }

/* ================= .cost-bar-list ================= */
.cost-breakdown-group { padding: var(--space-4) var(--space-5); border-bottom: var(--border-rule) solid var(--ink-200); }
.cost-breakdown-group h3 { margin: 0 0 var(--space-3); font-size: 13px; font-weight: 700; }
.cost-breakdown-group h3 .field-help { font-weight: 400; text-transform: none; letter-spacing: 0; }
.cost-bar-list { display: flex; flex-direction: column; gap: var(--space-2); }
.cost-bar-row { display: grid; grid-template-columns: minmax(120px, 200px) 1fr auto; align-items: center; gap: var(--space-3); }
.cost-bar-label { font-size: 13px; color: var(--ink-800); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cost-bar-track { height: 8px; border-radius: 999px; background: var(--ink-200); overflow: hidden; }
.cost-bar-fill { display: block; height: 100%; background: var(--accent-600); border-radius: 999px; }
.cost-bar-value { font-size: 13px; font-variant-numeric: tabular-nums; color: var(--ink-700); min-width: 70px; text-align: right; }

/* ================= .daily-cost-chart ================= */
.daily-cost-chart { display: flex; align-items: flex-end; gap: 2px; height: 90px; }
.daily-cost-bar-wrap { flex: 1 1 0; height: 100%; display: flex; align-items: flex-end; min-width: 3px; }
.daily-cost-bar { width: 100%; min-height: 2px; background: var(--accent-500); border-radius: 2px 2px 0 0; transition: opacity .1s; }
.daily-cost-bar.is-up { background: var(--danger); }
.daily-cost-bar.is-down { background: var(--ink-400); }
.daily-cost-bar-wrap:hover .daily-cost-bar { opacity: .75; }

/* ================= .task-usage-list ================= */
.task-usage-list { display: flex; flex-direction: column; gap: var(--space-2); }
.task-usage-row {
  display: grid; grid-template-columns: auto auto 1fr auto auto; align-items: center; gap: var(--space-3);
  padding: var(--space-2) 0; border-bottom: var(--border-rule) solid var(--ink-100);
}
.task-usage-row:last-child { border-bottom: none; }
.task-usage-label { font-size: 13px; color: var(--ink-800); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.task-usage-when { font-size: 12px; color: var(--ink-600); white-space: nowrap; }
.task-usage-amount { font-size: 13px; font-variant-numeric: tabular-nums; color: var(--ink-700); min-width: 60px; text-align: right; }

/* Security-relevant rejection (e.g. Advisory's out-of-scope/injection-style question gate) -
   red exclamation icon in the row, full "who asked what, why rejected" text on the line below. */
.task-usage-flag-icon { width: 16px; height: 16px; flex: none; color: var(--danger); }
.task-usage-flag-icon svg { width: 100%; height: 100%; }
.task-usage-row.is-flagged { background: var(--danger-100); border-radius: var(--radius-sm, 6px); padding-left: var(--space-2); padding-right: var(--space-2); }
.task-usage-row.is-flagged .task-usage-label { color: var(--danger-700); font-weight: 600; }
.task-usage-flag-detail { font-size: 12px; color: var(--danger-700); padding: 0 var(--space-2) var(--space-2); margin-top: calc(-1 * var(--space-2)); }

/* ================= .toolbar ================= */
.toolbar {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: var(--border-rule) solid var(--ink-200);
  background: var(--surface);
}
.toolbar-spacer { flex: 1; }

/* ================= .seg (segmented control) ================= */
.seg { display: flex; border: var(--border-rule) solid var(--ink); border-radius: var(--radius); width: fit-content; overflow: hidden; }
.seg-option {
  font: inherit; font-weight: 600; font-size: 13.5px;
  padding: 9px 18px; background: var(--surface); color: var(--ink);
  border: none; border-right: var(--border-rule) solid var(--ink);
  cursor: pointer;
}
.seg-option:last-child { border-right: none; }
.seg-option:hover { background: var(--accent-100); }
.seg-option.is-active { background: var(--accent); color: var(--on-accent); }
.seg-note { font-size: 12.5px; color: var(--ink-600); margin-top: var(--space-2); }

/* ================= .progress-inline ================= */
.progress-inline {
  display: flex; align-items: center; gap: var(--space-3);
  background: var(--accent-100); border-top: var(--border-hairline) solid var(--accent-300);
  border-bottom: var(--border-hairline) solid var(--accent-300);
  padding: var(--space-3) var(--space-5);
  font-size: 14px; font-weight: 600; color: var(--ink);
}
.progress-spinner { width: 18px; height: 18px; flex: none; animation: bside-spin 1s linear infinite; }
@keyframes bside-spin { to { transform: rotate(360deg); } }

/* ================= .zero-state ================= */
.zero-state {
  text-align: center; padding: var(--space-8) var(--space-4);
  display: flex; flex-direction: column; align-items: center; gap: var(--space-4);
  color: var(--ink-600);
}
.zero-state p { font-size: 14.5px; }

/* ================= banner (dismissible error) ================= */
.banner {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: var(--border-rule) solid var(--ink-200);
  border-radius: var(--radius);
  background: var(--ink-100);
  color: var(--ink);
  font-size: 13.5px; font-weight: 600;
}
.banner-message { flex: 1; }
.banner-close {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; flex: none; padding: 0;
  background: var(--surface); border: 1px solid var(--ink-300);
  border-radius: var(--radius);
  color: var(--ink); cursor: pointer;
  transition: background-color .1s, border-color .1s, color .1s;
}
.banner-close:hover { border-color: var(--accent-700); color: var(--accent-700); background: var(--accent-100); }
.banner-close svg { width: 14px; height: 14px; }

/* form-level success/error banners - same .banner shape, two color modifiers. Success reuses the
   system's one accent hue (already how "done"/"published" reads everywhere else); error is the
   one deliberate second hue (see tokens.css's --danger, and .field.is-invalid above for the
   matching per-field state). Use above a form after a submit succeeds/fails, not per-field. */
.banner-success { background: var(--accent-100); border-color: var(--accent-300); color: var(--accent-800); }
.banner-success svg { color: var(--accent-700); flex: none; }
.banner-error { background: var(--danger-100); border-color: var(--danger); color: var(--danger-700); }
.banner-error svg { color: var(--danger); flex: none; }

.client-fields { display: flex; flex-direction: column; gap: var(--space-4); }

/* ================= checkbox list (shared modal helper) ================= */
.checkbox-list { display: flex; flex-direction: column; gap: var(--space-2); max-height: 160px; overflow-y: auto; }
.checkbox-list label { display: flex; flex-direction: row; align-items: flex-start; gap: var(--space-2); font-weight: 400; font-size: 13.5px; color: var(--ink); }
.checkbox-list input { accent-color: var(--accent); width: 16px; height: 16px; margin-top: 2px; flex-shrink: 0; }
.checkbox-list .search-label { display: flex; flex-direction: column; gap: 1px; }
.checkbox-list .search-ref { font-weight: 600; color: var(--ink-600); }
.checkbox-list .search-meta { font-size: 11.5px; color: var(--ink-600); }

fieldset.bordered { border: var(--border-hairline) solid var(--ink-300); border-radius: var(--radius); padding: var(--space-3); }
fieldset.bordered legend { font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-600); padding: 0 6px; }

/* slide-down transition helper for conditionally-shown blocks */
.slide-down { display: grid; grid-template-rows: 1fr; transition: grid-template-rows .2s ease; }
.slide-down > * { overflow: hidden; }
.slide-down.is-collapsed { grid-template-rows: 0fr; }

/* ================= help (inline tooltips + "About this screen" dialog) =================
   Two triggers share the same small icon look (.help-icon-btn, ICONS.info):
   - [data-help="short text"]                 -> hover/focus/tap bubble (.help-tip), for a
     button, filter or data element that isn't self-explanatory.
   - [data-help-title][data-help-body]        -> click opens #modal-help, for a full
     screen-level "what is this screen / how do I use it" explanation, or any help text
     too long for a bubble. data-help-body paragraphs are separated by a blank line.
   Behaviour lives in help.js; requires a #modal-help dialog in the page markup. */
.help-icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; flex: none; padding: 0;
  border: none; background: none; color: var(--ink-500);
  cursor: pointer; line-height: 1;
  transition: color .1s;
}
.help-icon-btn:hover, .help-icon-btn:focus-visible {
  color: var(--accent-700);
}
.help-icon-btn svg { width: 20px; height: 20px; }

.help-label { display: inline-flex; align-items: center; gap: 5px; }

.help-tip {
  position: fixed; z-index: 300; max-width: 280px;
  background: var(--ink-solid); color: #ffffff;
  font-size: 12.5px; font-weight: 400; line-height: 1.45;
  padding: 8px 10px; border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
}

/* ================= cookie banner =================
   Behaviour in widgets.js: shown once per browser (localStorage flag) until dismissed. */
.cookie-banner {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center; gap: var(--space-4);
  flex-wrap: wrap;
  padding: var(--space-3) var(--space-5);
  background: var(--ink-solid); color: #ffffff;
  border-top: var(--border-rule) solid var(--accent);
  box-shadow: var(--shadow-lg);
}
.cookie-banner p { margin: 0; font-size: 13px; max-width: 640px; }
.cookie-banner .btn-primary { flex: none; }

/* ================= Ask Mireille chat widget =================
   Fixed bottom-right of every page in every app; markup + behaviour in mireille.js. */
.mireille-widget { position: fixed; right: var(--space-5); bottom: var(--space-5); z-index: 50; }
.mireille-fab {
  width: 56px; height: 56px; border-radius: 50%; flex: none;
  border: var(--border-rule) solid var(--accent-700); background: var(--accent);
  color: var(--on-accent); display: flex; align-items: center; justify-content: center;
  cursor: pointer; box-shadow: var(--shadow-md); margin-left: auto;
  transition: transform .12s, box-shadow .12s;
}
.mireille-fab:hover { transform: scale(1.06); box-shadow: var(--shadow-lg); }
.mireille-fab svg { width: 26px; height: 26px; }
.mireille-fab.is-open { background: var(--surface); color: var(--ink); }

.mireille-panel {
  width: min(360px, calc(100vw - 2 * var(--space-5)));
  height: min(480px, calc(100vh - 120px));
  display: flex; flex-direction: column;
  background: var(--surface); border: var(--border-rule) solid var(--ink-200); border-radius: var(--radius);
  box-shadow: var(--shadow-lg); margin-bottom: var(--space-3);
  position: relative;
}
/* drag-to-resize grip, top-left (mireille.js) - the widget is pinned bottom-right, so this is the
   corner that moves */
.mireille-resize {
  position: absolute; top: 0; left: 0; width: 16px; height: 16px; z-index: 1;
  cursor: nwse-resize; touch-action: none; border-top-left-radius: var(--radius);
  background: linear-gradient(135deg, var(--ink-300) 0 2px, transparent 2px 5px, var(--ink-300) 5px 6px, transparent 6px);
  opacity: .7;
}
.mireille-resize:hover { opacity: 1; }
/* larger-text mode: bigger default box and ~20% bigger type throughout */
.mireille-widget.is-large .mireille-panel {
  width: min(460px, calc(100vw - 2 * var(--space-5)));
  height: min(620px, calc(100vh - 120px));
}
.mireille-widget.is-large .mireille-msg { font-size: 16px; line-height: 1.5; }
.mireille-widget.is-large .mireille-input-row .input { font-size: 16px; }
.mireille-widget.is-large .mireille-contact { font-size: 14px; }
.mireille-widget.is-large .mireille-head-title strong { font-size: 16px; }
.mireille-widget.is-large .mireille-subtitle { font-size: 13px; }
#mireille-large[aria-pressed="true"] { color: var(--accent-700); }
.mireille-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  padding: var(--space-3) var(--space-4); border-bottom: var(--border-hairline) solid var(--ink-200); flex: none;
}
.mireille-head-title { display: flex; align-items: center; gap: var(--space-3); }
.mireille-avatar {
  width: 34px; height: 34px; border-radius: 50%; flex: none;
  background: var(--ink-solid); color: #fff;
  display: flex; align-items: center; justify-content: center; font-size: 16px;
}
.mireille-head-title strong { display: block; font-size: 13.5px; }
.mireille-subtitle { display: block; font-size: 11px; color: var(--ink-600); }
.mireille-head-actions { display: flex; align-items: center; gap: var(--space-2); }
.mireille-lang { display: inline-flex; border: var(--border-hairline) solid var(--ink-200); border-radius: 999px; padding: 2px; }
.mireille-lang button {
  border: 0; background: none; cursor: pointer; font: inherit; font-size: 11px; font-weight: 600;
  padding: 2px 8px; border-radius: 999px; color: var(--ink-600);
}
.mireille-lang button:hover { color: var(--ink); }
.mireille-lang button[aria-pressed="true"] { background: var(--ink-solid); color: #fff; }

.mireille-contact {
  display: flex; flex-wrap: wrap; align-items: center; gap: 2px var(--space-3);
  padding: var(--space-2) var(--space-4); flex: none;
  background: var(--accent-100); border-bottom: var(--border-hairline) solid var(--ink-200); font-size: 12px;
}
.mireille-contact-label { flex: 1 1 100%; color: var(--ink-600); }
.mireille-contact-label strong { color: var(--ink); }
.mireille-contact a { display: inline-flex; align-items: center; gap: 6px; color: var(--ink); text-decoration: none; font-weight: 600; }
.mireille-contact a:hover { color: var(--accent-800); text-decoration: underline; }
.mireille-contact svg { width: 14px; height: 14px; flex: none; color: var(--accent-700); }

.mireille-messages { flex: 1; overflow-y: auto; padding: var(--space-4); display: flex; flex-direction: column; gap: var(--space-3); }
/* house-themed scrollbar instead of the OS default (follows light/dark via the tokens) */
.mireille-messages { scrollbar-width: thin; scrollbar-color: var(--ink-300) transparent; }
.mireille-messages::-webkit-scrollbar { width: 8px; }
.mireille-messages::-webkit-scrollbar-track { background: transparent; }
.mireille-messages::-webkit-scrollbar-thumb { background: var(--ink-300); border-radius: 999px; border: 2px solid var(--surface); }
.mireille-messages::-webkit-scrollbar-thumb:hover { background: var(--accent-700); }
.mireille-msg { max-width: 85%; padding: 8px 12px; border-radius: var(--radius); font-size: 13px; line-height: 1.4; white-space: pre-line; }
.mireille-msg-bot { align-self: flex-start; background: var(--ink-100); color: var(--ink); }
.mireille-msg-user { align-self: flex-end; background: var(--accent); color: var(--on-accent); }
.mireille-msg-typing { opacity: 0.6; }
.mireille-msg-error { background: var(--danger-100); color: var(--danger-700); border: 1px solid var(--danger); }

.mireille-input-row { display: flex; gap: var(--space-2); padding: var(--space-3); border-top: var(--border-hairline) solid var(--ink-200); flex: none; }
.mireille-input-row .input { flex: 1; }
.mireille-input-row .btn-icon { flex: none; }

@media (max-width: 620px) {
  .mireille-widget { right: var(--space-3); bottom: var(--space-3); }
  /* phones: always full width, no dragging (!important beats a size saved on a desktop) */
  .mireille-panel, .mireille-widget.is-large .mireille-panel { width: calc(100vw - 2 * var(--space-3)) !important; max-height: calc(100vh - 100px); }
  .mireille-resize { display: none; }
}

/* ================= scroll-to-top button =================
   Behaviour in widgets.js: fades in past one viewport height of scroll. Bottom-left, not
   bottom-right, so it never fights the Ask Mireille fab (mireille.js) for the same corner. */
.scroll-top-btn {
  position: fixed; left: var(--space-5); bottom: var(--space-5); z-index: 40;
  width: 44px; height: 44px; border-radius: 50%; flex: none; padding: 0;
  border: 0.5px solid var(--ink-300); background: var(--surface); color: var(--ink);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  opacity: 0; visibility: hidden; transform: translateY(8px);
  transition: opacity .15s, transform .15s, visibility .15s, border-color .1s, color .1s;
}
.scroll-top-btn.is-visible { opacity: 1; visibility: visible; transform: none; }
.scroll-top-btn:hover { border-color: var(--accent-700); color: var(--accent-700); }
/* Explicit white (not an --ink token) - stays a crisp ring against the dark surface even though
   the app's ink ramp doesn't reach pure white; hover keeps the accent border above regardless. */
[data-theme="dark"] .scroll-top-btn:not(:hover) { border-color: #fff; }
.scroll-top-btn svg { width: 24px; height: 24px; }

/* ================= loading spinner / states ================= */
@keyframes bside-spin { to { transform: rotate(360deg); } }
.spinner {
  display: inline-block; width: 16px; height: 16px; flex: none;
  border: 2px solid var(--ink-300); border-top-color: var(--accent-700);
  border-radius: 50%;
  animation: bside-spin .7s linear infinite;
}
.spinner-lg { width: 28px; height: 28px; border-width: 3px; }
/* Full-panel "still loading" state - drop a single <div class="loading-block"><span
   class="spinner spinner-lg"></span></div> wherever a page/section is waiting on its first
   fetch, in place of blank space. */
.loading-block { display: flex; align-items: center; justify-content: center; padding: var(--space-8); color: var(--ink-500); }
/* Button mid-action: add is-loading and the button's own :disabled styling still applies; the
   spinner replaces its icon/leading content via JS (see widgets.js's setButtonLoading). */
.btn.is-loading, button.is-loading { pointer-events: none; }
.btn.is-loading .spinner, button.is-loading .spinner { border-color: currentColor; border-top-color: transparent; opacity: .8; }

/* ================= contact popover (nav "Contact" button) ================= */
.contact-popover-wrap { position: relative; }
.contact-popover {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 60;
  width: 240px;
  background: var(--surface); border: var(--border-rule) solid var(--ink-300); border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  padding: var(--space-2);
  display: flex; flex-direction: column; gap: 2px;
}
.contact-popover-name { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-500); padding: 6px 8px 4px; }
.contact-popover a {
  display: flex; align-items: center; gap: var(--space-2);
  padding: 8px; border-radius: var(--radius);
  color: var(--ink); text-decoration: none; font-size: 13.5px;
}
.contact-popover a:hover { background: var(--accent-100); color: var(--accent-800); }
.contact-popover svg { width: 16px; height: 16px; flex: none; color: var(--ink-500); }

/* ================= global search dialog =================
   Reuses .dialog-backdrop/.dialog (same modal shell as changelog/help) - just its own body
   layout. Behaviour in widgets.js: debounced fetch to this app's own /api/search?q= (every app
   exposes that path at the same relative URL - see each server's own search route). */
.search-dialog .dialog-body { padding: var(--space-3); gap: var(--space-3); }
.search-dialog .input { font-size: 16px; }
.search-results { display: flex; flex-direction: column; gap: 2px; max-height: 50vh; overflow-y: auto; }
.search-result {
  display: flex; flex-direction: column; gap: 2px;
  padding: var(--space-2) var(--space-3); border-radius: var(--radius);
  color: inherit; text-decoration: none;
}
.search-result:hover, .search-result:focus-visible { background: var(--accent-100); }
.search-result-title { font-size: 14px; font-weight: 600; color: var(--ink); }
.search-result-meta { font-size: 12px; color: var(--ink-600); display: flex; align-items: center; gap: 6px; }
/* pinned "Contact agent" card (widgets.js) - not a link itself, its email/phone are */
.search-contact:hover { background: none; }
.search-contact .search-result-meta { flex-wrap: wrap; gap: 4px var(--space-3); }
.search-contact a { display: inline-flex; align-items: center; gap: 6px; color: var(--ink); font-weight: 600; text-decoration: none; }
.search-contact a:hover { color: var(--accent-800); text-decoration: underline; }
.search-contact svg { width: 14px; height: 14px; flex: none; color: var(--accent-700); }
.search-result-app {
  font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--accent-700); background: var(--accent-100); border-radius: 999px; padding: 1px 8px;
}
.search-empty { font-size: 13px; color: var(--ink-600); padding: var(--space-4) var(--space-3); text-align: center; }

/* ================= profile menu (profile.js) =================
   One round account-icon button pinned to the far right of the header; the account details and
   actions (change password, admin settings, log out) live in its dropdown panel. */
.profile-menu { position: relative; order: 99; margin-left: auto; display: flex; align-items: center; flex: none; }
.profile-btn {
  width: 36px; height: 36px; border-radius: 50%; padding: 0; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent); color: var(--on-accent); border: var(--border-rule) solid var(--accent-700);
  font: 700 14px/1 var(--font-heading, inherit); transition: box-shadow .12s, transform .12s;
}
/* Placeholder until profile.js fills the badge: same spot and size as the button, so the header
   doesn't shift on load. The header theme button is hidden from the start for the same reason
   (profile.js moves it into the menu). */
.user-badge:empty { order: 99; margin-left: auto; flex: none; width: 36px; height: 36px; border-radius: 50%; background: var(--ink-200); }
body:has(.user-badge) #btn-theme-toggle { display: none; }
.profile-btn svg { width: 20px; height: 20px; color: #000; }
.profile-btn:hover { box-shadow: 0 0 0 3px var(--accent-100); }
.profile-btn:focus-visible { outline: 2px solid var(--accent-700); outline-offset: 2px; }
.profile-btn[aria-expanded="true"] { box-shadow: 0 0 0 3px var(--accent-100); }
.profile-panel {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 60; width: 260px;
  background: var(--surface); border: var(--border-rule) solid var(--ink-200); border-radius: var(--radius);
  box-shadow: var(--shadow-lg, var(--shadow-md)); overflow: hidden;
}
.profile-head { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-4); border-bottom: var(--border-hairline) solid var(--ink-200); }
.profile-avatar {
  width: 40px; height: 40px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent); color: var(--on-accent); font: 700 16px/1 var(--font-heading, inherit);
}
.profile-id { min-width: 0; }
.profile-email { font-size: 13.5px; font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
.profile-role {
  display: inline-block; margin-top: 4px; font-size: 10.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--accent-700); background: var(--accent-100); border-radius: 999px; padding: 1px 8px;
}
.profile-items { display: flex; flex-direction: column; padding: var(--space-2); }
.profile-item {
  display: flex; align-items: center; gap: var(--space-3); width: 100%; padding: 9px 10px;
  border: none; background: none; border-radius: var(--radius); cursor: pointer; text-align: left;
  font: inherit; font-size: 13.5px; color: var(--ink); text-decoration: none;
}
.profile-item:hover, .profile-item:focus-visible { background: var(--ink-100); outline: none; }
.profile-item svg { width: 16px; height: 16px; flex: none; color: var(--ink-500); }
.profile-item-logout { border-top: var(--border-hairline) solid var(--ink-200); border-radius: 0 0 var(--radius) var(--radius); margin-top: var(--space-1); }
/* "Contact agent" row expands in place to the staff contact's details */
.profile-contact { display: flex; flex-direction: column; padding: 0 0 var(--space-1) 36px; }
.profile-contact-name { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-500); padding: 2px 8px 4px; }
.profile-contact a {
  display: flex; align-items: center; gap: var(--space-2); padding: 6px 8px; border-radius: var(--radius);
  color: var(--ink); text-decoration: none; font-size: 13px;
}
.profile-contact a:hover { background: var(--accent-100); color: var(--accent-800); }
.profile-contact svg { width: 14px; height: 14px; flex: none; color: var(--ink-500); }
/* language row: label + EN/FR segmented choice on the right */
.profile-lang { cursor: default; }
.profile-lang:hover { background: none; }
.profile-seg { margin-left: auto; display: inline-flex; border: var(--border-hairline) solid var(--ink-300); border-radius: 999px; overflow: hidden; }
.profile-seg button {
  border: none; background: none; cursor: pointer; padding: 2px 9px;
  font: inherit; font-size: 11.5px; font-weight: 700; color: var(--ink-500);
}
.profile-seg button:hover { color: var(--ink); }
.profile-seg button:focus-visible { outline: 2px solid var(--accent-700); outline-offset: -2px; }
.profile-seg button[aria-pressed="true"] { background: var(--accent); color: var(--on-accent); }
/* dark-mode row: label + on/off switch pill on the right */
.profile-switch {
  margin-left: auto; position: relative; width: 30px; height: 17px; flex: none;
  border-radius: 999px; background: var(--ink-300); transition: background .15s;
}
.profile-switch::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 13px; height: 13px;
  border-radius: 50%; background: #fff; transition: transform .15s;
}
.profile-item[aria-checked="true"] .profile-switch { background: var(--accent); }
.profile-item[aria-checked="true"] .profile-switch::after { transform: translateX(13px); }

/* ================= responsive ================= */
/* "More" menu and text-action row of the phone header (widgets.js initPhoneNav) - both empty and
   hidden on wider screens, where their contents sit in the header as usual. */
.nav-more { position: relative; display: none; }
.nav-actions { display: none; }
.nav-more-panel {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 60; width: 250px;
  display: flex; flex-direction: column; gap: 2px; padding: var(--space-2);
  background: var(--surface); border: var(--border-rule) solid var(--ink-200); border-radius: var(--radius);
  box-shadow: var(--shadow-lg, var(--shadow-md));
}
/* Icon-only header buttons become labelled menu rows; the label is their own (translated)
   aria-label. The ::after overrides the [data-tooltip] bubble some of them carry. */
.nav-more-panel > :is(.btn-icon, .help-icon-btn) {
  width: 100%; height: auto; justify-content: flex-start; gap: var(--space-3); padding: 10px;
  border: none; border-radius: var(--radius); background: none; color: var(--ink); text-align: left;
}
.nav-more-panel > :is(.btn-icon, .help-icon-btn):hover { background: var(--ink-100); color: var(--ink); }
.nav-more-panel > :is(.btn-icon, .help-icon-btn) svg { width: 18px; height: 18px; flex: none; color: var(--ink-500); }
.nav-more-panel > :is(.btn-icon, .help-icon-btn)::after {
  content: attr(aria-label); position: static; transform: none; opacity: 1; pointer-events: none;
  padding: 0; background: none; color: inherit; white-space: normal;
  font-size: 14px; font-weight: 500; line-height: 1.3;
}

@media (max-width: 720px) {
  /* One header row: brand left; bell, More menu and profile button right. Text actions get
     their own row of equal columns below (.nav-actions), view tabs a swipeable strip under that.
     Not sticky here, so the header never covers the page while scrolling. */
  .nav { padding: var(--space-2) var(--space-4); position: relative; row-gap: 0; column-gap: var(--space-2); }
  .nav-brand { flex: 1 1 0; min-width: 0; min-height: 44px; flex-wrap: nowrap; gap: var(--space-2); }
  .nav-brand img { height: 28px; }
  .nav-brand-word { font-size: 17px; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .nav-brand .version-badge { display: none; }
  /* Pages without widgets.js (no More menu) keep the header buttons wrapping on a full-width
     row of their own under the brand. */
  .nav-right { flex: 1 1 100%; min-width: 0; margin-left: 0; gap: var(--space-2); flex-wrap: wrap; }
  .nav-right > * { min-width: 0; max-width: 100%; }
  .nav.has-phone-nav .nav-right { flex: none; flex-wrap: nowrap; }
  .nav-more { display: flex; }
  .nav-actions { order: 5; flex: 1 1 100%; display: flex; gap: var(--space-2); }
  /* margin, not the row gap, so a row whose actions are all hidden takes no space */
  .nav-actions > * { flex: 1 1 0; min-width: 0; margin: var(--space-2) 0 var(--space-1); }
  .nav-actions .btn-with-meta { align-items: stretch; }
  .nav-actions .help-label { display: flex; width: 100%; }
  .nav-actions .help-icon-btn { display: none; }
  .nav-actions .btn-with-meta .meta-note { text-align: center; padding: 0; }
  .nav-actions .btn-with-meta .meta-note:empty { display: none; }
  .nav-actions > .btn, .nav-actions .help-label > .btn, .nav-actions .btn-split { flex: 1; min-height: 36px; justify-content: center; }
  .nav-actions .btn-split > .btn:not(.btn-split-settings) { flex: 1; justify-content: center; }
  .nav-links {
    order: 10; flex: 1 1 100%; display: flex; gap: var(--space-5); overflow-x: auto; scrollbar-width: none;
    margin: var(--space-1) calc(-1 * var(--space-4)) calc(-1 * var(--space-2)); padding: 0 var(--space-4);
    border-top: var(--border-hairline) solid var(--ink-200);
  }
  .nav-links::-webkit-scrollbar { display: none; }
  .nav-link { height: 44px; flex: none; white-space: nowrap; }
  .nav-menu-btn { display: none; }
  /* The bell isn't the right-most button here, so its popover spans the screen instead of
     hanging off the left edge. */
  .nav .notif-popover { position: fixed; top: 60px; left: var(--space-3); right: var(--space-3); width: auto; max-height: 70vh; }
  .dialog-backdrop { padding: var(--space-4) var(--space-3); }
  .dialog-head, .dialog-body, .dialog-actions { padding-left: var(--space-4); padding-right: var(--space-4); }
  .dialog-actions { flex-wrap: wrap; }
  .dialog-actions > .btn { flex: 1 1 auto; justify-content: center; text-align: center; }
  .user-badge, .nav-user { flex-wrap: wrap; min-width: 0; }
  .user-badge .user-email, .nav-user-email { overflow-wrap: anywhere; }
  /* Page toolbars ("Radar (i) ...... Sources  Refresh"): the spacer that pushes the controls
     right on desktop becomes a line break, so the title keeps its own line and the controls
     start together on the next one instead of wrapping one at a time. */
  .toolbar { padding: var(--space-3) var(--space-4); gap: var(--space-2); }
  .toolbar > h2 { font-size: 22px; }
  .toolbar > :is(.spacer, .toolbar-spacer) { flex: 0 0 100%; height: 0; }
  /* Segmented filters stay one bordered row and swipe sideways, rather than wrapping into
     a grid whose borders no longer line up. */
  .seg { flex-wrap: nowrap; max-width: 100%; overflow-x: auto; scrollbar-width: none; }
  .seg::-webkit-scrollbar { display: none; }
  .seg-option { flex: none; white-space: nowrap; }
  /* Stat tiles in an even two-column grid (an odd last tile spans both) instead of a ragged
     wrap with grey filler boxes. */
  .stat-row { display: grid; grid-template-columns: 1fr 1fr; }
  .stat-row > .stat-fill { display: none; }
  .stat-row > .stat-block:nth-child(odd of .stat-block:not(.stat-fill)):nth-last-child(1 of .stat-block:not(.stat-fill)) { grid-column: 1 / -1; }
  .stat-block { padding: var(--space-3) var(--space-4); min-width: 0; }
  .stat-block b { font-size: 22px; }
  /* Any bare table (not already in a .table-wrap) scrolls inside itself, not the page. */
  main table:not(.table-wrap table) { display: block; max-width: 100%; overflow-x: auto; }
  input, select, textarea { max-width: 100%; }

  .table-wrap.is-stacked table, .table-wrap.is-stacked thead { display: block; }
  .table-wrap.is-stacked thead { position: absolute; left: -9999px; }
  .table-wrap.is-stacked tbody { display: block; }
  .table-wrap.is-stacked tr {
    display: block; border: var(--border-rule) solid var(--ink-200); margin-bottom: var(--space-3);
  }
  .table-wrap.is-stacked td {
    display: flex; justify-content: space-between; align-items: center; gap: var(--space-3);
    border-bottom: var(--border-hairline) solid var(--ink-200); text-align: right;
  }
  .table-wrap.is-stacked td::before {
    content: attr(data-label); font-size: 11px; font-weight: 700; text-transform: uppercase;
    letter-spacing: .05em; color: var(--ink-600); text-align: left;
  }
  .table-wrap.is-stacked td.actions-cell { justify-content: flex-end; }
  .table-wrap.is-stacked td.actions-cell::before { content: none; }

  body { padding-left: 0; padding-right: 0; }
  .full-width-mobile { padding-left: 16px; padding-right: 16px; }
}

/* ---------- Client View (impersonation) banner ----------
   Injected by widgets.js on every app when the bside_client_view flag cookie is present (see its
   own header comment). Sits above the sticky .nav in normal flow, so it scrolls the same way. */
.impersonation-banner {
  position: sticky; top: 0; z-index: 500;
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap;
  gap: var(--space-3); min-height: 40px; padding: 8px var(--space-5);
  background: #b45309; color: #fff; font-weight: 600; text-align: center;
}
.impersonation-banner .btn { flex: none; }
body.has-impersonation-banner .nav { top: 40px; }

/* Version history is staff-only: the header button and its dialog are hidden for client sessions
   (and until the role is known, so a client never sees it flash). The current version and date
   live in the dialog head, not the app header. Universe names these #version (header badge),
   #changelog (button) and #changelog-modal. */
html:not([data-role-hint]) body:not([data-role]) :is(#btn-changelog, #changelog, #version),
html[data-role-hint="client"] body:not([data-role]) :is(#btn-changelog, #changelog, #version),
body[data-role="client"] :is(#btn-changelog, #modal-changelog, #changelog, #version, #changelog-modal) { display: none !important; }
.dialog-head .version-badge { display: block; margin-top: 2px; }

/* ================= .bside-footer (widgets.js) ================= */
.bside-footer {
  margin-top: auto; border-top: var(--border-rule) solid var(--ink-200); color: var(--ink-600);
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-5);
  font-size: 12px; padding: var(--space-4) clamp(20px, 4vw, 52px);
  padding-right: max(clamp(20px, 4vw, 52px), 100px); /* clear the floating Mireille button */
}
.bside-footer-copy { font-weight: 600; color: var(--ink-700); }
.bside-footer-copy a { color: inherit; font-weight: 400; }
.bside-footer-copy a:hover, .bside-footer-copy a:focus-visible { color: var(--accent-700); }
.bside-footer-contact { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); }
.bside-footer-contact a { display: inline-flex; align-items: center; gap: 6px; color: inherit; text-decoration: none; }
.bside-footer-contact a:hover, .bside-footer-contact a:focus-visible { color: var(--accent-700); text-decoration: underline; }
.bside-footer-contact svg { width: 14px; height: 14px; flex: none; }
body:has(.editor-main:not([hidden])) .bside-footer, body[data-view="calendar"] .bside-footer { display: none; }
@media (max-width: 620px) { .bside-footer { flex-direction: column; align-items: flex-start; padding-right: 100px; } }
@media print { .bside-footer { display: none; } }
