/* ==========================================================================
   TrackFlo components: the single component vocabulary.
   Reads tokens from tf-tokens.css only — no hex literals.

   Three surfaces, one status system, one of everything else. If a page needs a
   surface that isn't here, extend one of these rather than adding a tenth card.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Surfaces
   -------------------------------------------------------------------------- */
.tf-card {
  background: var(--bs-secondary-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--tf-radius);
  box-shadow: var(--tf-shadow);
  transition: box-shadow .2s ease, transform .2s ease;
}
.tf-card:hover { box-shadow: var(--tf-shadow-hover); }
/* Generic selected state, so no page needs its own selected-card variant. */
.tf-card.is-active {
  border-color: var(--tf-blue);
  box-shadow: var(--tf-shadow-hover);
}

/* Flag card: a clickable card whose thick left border carries a status color
   (set inline as border-left-color: var(--tf-…) by the caller). Triage uses it;
   generic so the next queue-style page doesn't invent its own. */
.tf-card--flag {
  cursor: pointer;
  border-left-width: 6px;
  transition: background-color .12s ease, box-shadow .12s ease;
}
.tf-card--flag:hover { background-color: var(--bs-tertiary-bg); }
.tf-card--flag.is-active {
  background-color: var(--bs-tertiary-bg);
  box-shadow: 0 0 0 2px var(--tf-blue) inset;
}

/* Section container with a titled, ruled header. */
.tf-panel {
  background: var(--bs-secondary-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--tf-radius);
  box-shadow: var(--tf-shadow);
  padding: 1.5rem;
  margin-bottom: 1.5rem;
}
.tf-panel__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1.5rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--bs-border-color);
}
.tf-panel__title { font-size: 1.5rem; font-weight: 700; margin: 0; }

/* Fixed-height metric tile. --lg is the taller load-page variant. */
.tf-tile {
  height: var(--tf-tile-h);
  background: var(--bs-secondary-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--tf-radius-sm);
  position: relative;
  overflow: visible;      /* hover tips escape the tile */
  cursor: pointer;
  transition: transform .1s ease-in-out, box-shadow .1s ease-in-out;
}
.tf-tile:hover { transform: translateY(-2px); box-shadow: var(--tf-shadow-hover); z-index: 10; }
.tf-tile:active { box-shadow: var(--tf-shadow-hover); }
.tf-tile--lg { height: var(--tf-tile-h-lg); text-align: start; padding: 1.5rem; }

.tf-tile__number { font-size: 3rem; font-weight: 500; line-height: 1; margin-bottom: 4px; }
.tf-tile__number--xl { font-size: 4rem; }   /* two-tiles-on-screen boards */
.tf-tile__title  { font-size: 1rem; font-weight: 600; line-height: 1.1; }

.tf-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: .75rem;
}

@media (max-width: 768px) {
  .tf-grid { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
  .tf-tile { height: 90px; }
  .tf-tile__number { font-size: 1.5rem; }
}

/* --------------------------------------------------------------------------
   Status — ONE vocabulary. Combine a modifier with .tf-badge, .tf-dot, .tf-tile,
   or any text element. Replaces six previous vocabularies.
   -------------------------------------------------------------------------- */
.tf-status--ok       { color: var(--tf-ok); }
.tf-status--warn     { color: var(--tf-warn); }
.tf-status--danger   { color: var(--tf-danger); }
.tf-status--neutral  { color: var(--tf-neutral); }
.tf-brand-whatsapp   { color: var(--tf-whatsapp); }  /* channel icon, not a status */

.tf-badge {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding: .5rem 1rem;
  border-radius: var(--tf-radius-sm);
  font-weight: 600;
  font-size: .875rem;
}
/* Default tint only when nothing else colors the badge: Bootstrap's text-bg-*
   utilities set background-color, and our shorthand (loaded later) was wiping it —
   which left white text-bg-danger text on the neutral tint (load-page issue badge). */
.tf-badge:not([class*="text-bg-"]) { background-color: var(--tf-neutral-bg); }
.tf-badge--sm { padding: .25rem .6rem; border-radius: 4px; font-size: .7rem; }

/* A badge tints its background to match its status. */
.tf-badge.tf-status--ok      { background: var(--tf-ok-bg); }
.tf-badge.tf-status--warn    { background: var(--tf-warn-bg); }
.tf-badge.tf-status--danger  { background: var(--tf-danger-bg); }
.tf-badge.tf-status--neutral { background: var(--tf-neutral-bg); }

.tf-dot {
  width: 8px; height: 8px; border-radius: 50%;
  display: inline-block; margin-right: 6px; flex-shrink: 0;
  background: currentColor;   /* takes its color from the .tf-status--* modifier */
}

/* --------------------------------------------------------------------------
   Load search suggestions (9.17.26). The dropdown under the nav-bar search box.
   Composed from what is here -- .tf-dot + .tf-status--* for status, --tf-warn-bg for
   the <mark> (has a dark value, so it survives all three themes), Bootstrap surface
   tokens for the panel. The reference LABEL is muted inline text, not a badge: labels
   run 2-27 characters by tenant (McLeod `PO`, Tai `Linehaul Carrier Pro Number`) and a
   badge cannot hold both. tabular-nums on the digits people compare by eye.
   Design: claude/plans/9.17.26_load_search_autocomplete.md §3.5
   -------------------------------------------------------------------------- */
.tf-suggest {
  position: absolute; top: 100%; left: 0; right: 0; z-index: 1050;
  margin: .25rem 0 0; padding: .25rem; list-style: none; text-align: left;
  background: var(--bs-body-bg); color: var(--bs-body-color);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--tf-radius-sm); box-shadow: var(--tf-shadow-hover);
  max-height: 60vh; overflow-y: auto;
}
.tf-suggest__row {
  display: block; padding: .4rem .6rem; border-radius: 4px; cursor: pointer;
  line-height: 1.3;
}
.tf-suggest__row[aria-selected="true"] { background: var(--bs-tertiary-bg); }
.tf-suggest__line { display: flex; align-items: baseline; gap: .6rem; min-width: 0; }
.tf-suggest__number { font-weight: 600; font-variant-numeric: tabular-nums; flex: 0 0 auto; }
.tf-suggest__customer {
  flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--bs-secondary-color);
}
.tf-suggest__status { flex: 0 0 auto; font-size: .8rem; white-space: nowrap; }
/* Always rendered, empty when unused, so one- and two-line rows are the same height. */
.tf-suggest__match {
  display: block; min-height: 1.3em; font-size: .85rem; font-variant-numeric: tabular-nums;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tf-suggest__label { color: var(--tf-neutral); margin-right: .4rem; }
.tf-suggest mark { background: var(--tf-warn-bg); color: inherit; padding: 0; }
.tf-suggest__more { text-align: center; color: var(--tf-blue-strong); font-size: .85rem; }

/* Brand-colored link, replacing the raw blue hex used inline (~111 times) and in the
   DataTables renderers. Uses the -strong token: #57B1FF is ~2.2:1 on white, well
   under the 4.5:1 text threshold; the dark theme gets the true brand blue back. */
.tf-link { color: var(--tf-blue-strong); }

/* --------------------------------------------------------------------------
   Page header type (8.10.26). Pages used display-5/display-4 (3rem+) titles —
   the strongest remaining old-admin-template signal. Titles are compact and
   consistent; HEROES are data (load numbers, entity names), capped at 2.5rem.
   -------------------------------------------------------------------------- */
.tf-page-title {
  font-size: var(--tf-text-page-title);
  font-weight: 700;
  letter-spacing: -.01em;
  margin-bottom: .25rem;
}
.tf-page-desc {
  color: var(--bs-secondary-color);
  margin-bottom: 1rem;
}
.tf-hero-title {
  font-size: var(--tf-text-hero);
  font-weight: 700;
  letter-spacing: -.015em;
}
/* Sign-in title. The compact scale is for pages with content under the header; the
   auth screen has only a logo and two controls, so 1.5rem read as a caption there.
   Scales down on narrow screens the way display-4 did. */
.tf-auth-title {
  font-size: clamp(2.5rem, 1.475rem + 2.7vw, var(--tf-text-auth-title));
  font-weight: 700;
  letter-spacing: -.015em;
}

/* Small-caps section label. ONE definition — customer_milestones.html and the
   (since-removed) video tutorials page each defined their own; both agreed on
   this style, so the shared rule adopts it. */
.tf-eyebrow {
  font-size: .7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .14em;
  color: var(--bs-secondary-color);
}

/* --------------------------------------------------------------------------
   Buttons. Names kept: btn-trackflo-fancy is used 427 times and -square 147.
   -------------------------------------------------------------------------- */
.btn-trackflo-fancy,
.btn-trackflo-fancy-square {
  color: var(--tf-on-ink) !important;
  background: linear-gradient(var(--tf-ink), var(--tf-ink)) padding-box,
              var(--tf-brand-gradient) border-box;
  border: 4px solid transparent;
}
.btn-trackflo-fancy { border-radius: 50px; }

.btn-trackflo-fancy:hover, .btn-trackflo-fancy:focus, .btn-trackflo-fancy:active,
.btn-trackflo-fancy.active,
.btn-trackflo-fancy-square:hover, .btn-trackflo-fancy-square:focus,
.btn-trackflo-fancy-square:active, .btn-trackflo-fancy-square.active {
  color: var(--bs-body-color) !important;
  background: linear-gradient(var(--bs-secondary-bg), var(--bs-secondary-bg)) padding-box,
              var(--tf-brand-gradient) border-box;
  border: 4px solid transparent;
}

/* Bootstrap overrides, retokenized.
   --tf-btn-face, not --tf-ink: ink is theme-invariant, so a transparent button wearing
   it vanished against the dark page (8.13.26). The face token follows the surface. */
.btn-primary {
  color: var(--tf-btn-face) !important;
  border-color: var(--tf-btn-face) !important;
  background-color: transparent !important;
}
.btn-primary:hover, .btn-primary:focus, .btn-primary:active, .btn-primary.active {
  color: var(--tf-btn-face-contrast) !important;
  border-color: var(--tf-btn-face) !important;
  background-color: var(--tf-btn-face) !important;
}
.btn-sm { font-size: 14px; }

/* Big-number filter button (was custom_css.css, trapped inside the bundle;
   used in 10 templates). */
.big_number_button {
  border-radius: var(--tf-radius-sm);
  padding: 10px;
  margin: 0 5px 2px 0;
}
.big_number_button:hover {
  box-shadow: var(--tf-shadow-hover);
  border: 1px solid var(--bs-border-color);
  margin-bottom: 0;
}
.big_number_button:active {
  border-radius: var(--tf-radius-sm);
  border: 1px solid var(--bs-border-color);
  box-shadow: var(--tf-shadow-hover);
  margin-bottom: 0;
}

/* --------------------------------------------------------------------------
   Form controls (8.12.26). Bootstrap's default fills an input with --bs-body-bg
   and outlines it with --bs-border-color — on a page whose body IS --bs-body-bg
   (sign-in, signup, password reset, anything not inside a card) that made the
   field the same color as the page behind a near-invisible hairline. These rules
   give every text input, textarea and select its own surface and a border that
   clears 3:1 on both the body and a card, in every theme.
   -------------------------------------------------------------------------- */
.form-control,
.form-select {
  background-color: var(--tf-input-bg);
  border-color: var(--tf-input-border);
  color: var(--bs-body-color);
}

/* Bootstrap's disabled fill is --bs-secondary-bg, which is the CARD surface — in light
   mode that is #ffffff, i.e. identical to the new enabled fill, so a disabled field
   would look editable. Step it to the well color instead. */
.form-control:disabled,
.form-control[readonly],
.form-select:disabled {
  background-color: var(--bs-tertiary-bg);
  color: var(--bs-secondary-color);
}

/* Focus in the brand blue rather than Bootstrap's default #86b7fe, which is tuned for
   a white page and washes out on the dark body. */
.form-control:focus,
.form-select:focus {
  background-color: var(--tf-input-bg);
  border-color: var(--tf-blue-strong);
  color: var(--bs-body-color);
  box-shadow: 0 0 0 .2rem rgba(var(--tf-blue-strong-rgb), .25);
}

/* Prefixes/suffixes ride the same surface so an input group reads as one control. */
.input-group-text {
  background-color: var(--bs-tertiary-bg);
  border-color: var(--tf-input-border);
  color: var(--bs-body-color);
}

/* Checkboxes, radios and switches (8.22.26). The same defect the text inputs had, one
   step worse: Bootstrap fills .form-check-input with --bs-body-bg and hairlines it in
   --bs-border-color, which is ~1.1:1 on the light body. On the light and classic themes
   an unchecked box was therefore a near-white square on a near-white page — the call-action
   list in the Driver AI Voice Calls off-canvas read as bare labels with nothing to click.
   Same two tokens as .form-control, so a box and a field share one boundary color. */
.form-check-input {
  background-color: var(--tf-input-bg);
  border-color: var(--tf-input-border);
}
.form-check-input:not(:checked):not(:disabled):hover {
  border-color: var(--tf-blue-strong);
}
/* Focus in the brand blue, matching .form-control:focus, not Bootstrap's #86b7fe. */
.form-check-input:focus {
  border-color: var(--tf-blue-strong);
  box-shadow: 0 0 0 .2rem rgba(var(--tf-blue-strong-rgb), .25);
}
/* Checked fill moves to the themed blue: #57B1FF is a fill tuned for DARK surfaces, and
   Bootstrap's white check glyph on it clears only ~2:1 — the checked state was as hard to
   read as the empty one. -strong resolves back to the true brand blue in dark mode, so
   only light and classic deepen. */
.form-check-input:checked {
  background-color: var(--tf-blue-strong);
  border-color: var(--tf-blue-strong);
}
/* A disabled EMPTY box drops to the well color, the way .form-control:disabled does, so
   it doesn't advertise itself as clickable. Scoped to :not(:checked) on purpose — a
   checked disabled box must keep the blue fill, or its white check glyph disappears
   into the grey and the box reads as empty. */
.form-check-input:disabled:not(:checked) {
  background-color: var(--bs-tertiary-bg);
  border-color: var(--bs-border-color);
}

/* --------------------------------------------------------------------------
   Tabs. Built on Bootstrap .nav-tabs. Category modifiers carry meaning and stay.
   -------------------------------------------------------------------------- */
/* Container: kills .nav-tabs' default bottom border — these are pill-style tabs. */
.tf-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: .25rem;
  border-bottom: 0;
}
.tf-tabs__link {
  background: none;
  padding: .5rem 1rem;
  color: var(--bs-secondary-color);
  font-weight: 500;
  border: 4px solid transparent;
  border-radius: var(--tf-radius-sm);
  transition: all .2s ease;
}
.tf-tabs__link:hover:not(.active) {
  background: var(--bs-tertiary-bg);
  color: var(--bs-body-color);
}
.tf-tabs__link.active {
  color: var(--tf-on-ink) !important;
  background: linear-gradient(var(--tf-ink), var(--tf-ink)) padding-box,
              var(--tf-brand-gradient) border-box;
  border: 4px solid transparent;
}
/* Special Loads (hot, high value, exception) — amber/orange */
.tf-tabs__link.tab-cat-special.active {
  background: linear-gradient(var(--tf-ink), var(--tf-ink)) padding-box,
              linear-gradient(to left, var(--tf-warn), var(--tf-ok)) border-box;
}
/* Deep Workflows (detention, POD) — danger to blue */
.tf-tabs__link.tab-cat-workflow.active {
  background: linear-gradient(var(--tf-ink), var(--tf-ink)) padding-box,
              linear-gradient(to left, var(--tf-danger), var(--tf-blue)) border-box;
}
/* Intelligence (conversations, risk signals, data approval) — blue to green */
.tf-tabs__link.tab-cat-intel.active {
  background: linear-gradient(var(--tf-ink), var(--tf-ink)) padding-box,
              linear-gradient(to left, var(--tf-blue), var(--tf-green)) border-box;
}

/* --------------------------------------------------------------------------
   Nav: hover submenus (was inline in top_nav_secondary.html).
   Flyout submenu inside a dropdown (Bootstrap 5 dropped native nested menus).
   Hovering the parent reveals a child menu that fans out to the LEFT.
   -------------------------------------------------------------------------- */
.dropdown-submenu { position: relative; }
.dropdown-submenu > .dropdown-submenu-menu {
  display: none;
  position: absolute;
  top: 50%;                      /* vertically center the flyout on the parent item */
  bottom: auto;
  transform: translateY(-50%);
  right: 100%;   /* sit immediately to the left of the parent item (no hover gap) */
  left: auto;
  min-width: 18rem;
}
.dropdown-submenu:hover > .dropdown-submenu-menu { display: block; }
.dropdown-submenu > .dropdown-submenu-toggle { cursor: pointer; }

/* Bootstrap's own underline tabs (nav-tabs), used by the service-metrics pages:
   default link color is the LINK blue; these are tabs, not links, so they read
   as body text with a bolder active state (was inline in 3 service templates). */
.nav-tabs .nav-link { color: var(--bs-body-color); }
.nav-tabs .nav-link.active { font-weight: 600; }

/* --------------------------------------------------------------------------
   Superset embed (was inline in superset_test.html / superset_dashboard.html).
   -------------------------------------------------------------------------- */
#superset-iframe {
  width: 100%;
  height: 800px;
  border: 1px solid var(--bs-border-color);
  border-radius: 4px;
}
#superset-iframe iframe { width: 100% !important; height: 100% !important; border: none; }

/* --------------------------------------------------------------------------
   Hover tip. One rule; --right only changes the anchor.
   -------------------------------------------------------------------------- */
.tf-hover-tip {
  position: absolute;
  top: 100%; left: 50%;
  transform: translateX(-50%);
  margin-top: 8px;
  width: 280px;
  padding: 12px 16px;
  background: var(--tf-ink);
  color: var(--tf-on-ink);
  border-radius: var(--tf-radius-sm);
  font-size: .8rem;
  line-height: 1.4;
  text-align: left;
  box-shadow: var(--tf-shadow-hover);
  opacity: 0;
  visibility: hidden;
  transition: all .1s ease-in-out;
  z-index: 1000;
}
.tf-hover-tip::before {
  content: '';
  position: absolute;
  bottom: 100%; left: 50%;
  transform: translateX(-50%);
  border: 6px solid transparent;
  border-bottom-color: var(--tf-ink);
}
.tf-hover-tip--right { top: 50%; left: 100%; }

/* Card-style hover tip: several templates layer bg-body-secondary over the tip for a
   card look. That background is light in light mode, where the default on-ink text
   vanished (gate feedback 8.10.26) — themed body text on themed card backgrounds. */
.tf-hover-tip.bg-body-secondary { color: var(--bs-body-color); }

.tf-tile:hover .tf-hover-tip { opacity: 1; visibility: visible; }

/* --------------------------------------------------------------------------
   Stepper. Replaces the milestone roadmap, the onboarding rail, and the
   outreach lifecycle stage strip's cousins.
   -------------------------------------------------------------------------- */
.tf-stepper { display: flex; align-items: center; gap: 0; list-style: none; padding: 0; margin: 0; }
.tf-stepper--vertical { flex-direction: column; align-items: flex-start; }
.tf-stepper__step { display: flex; flex-direction: column; align-items: center; flex: 0 0 auto; }
.tf-stepper__marker {
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--bs-tertiary-bg);
  border: 2px solid var(--bs-border-color);
  transition: all .2s ease;
}
.tf-stepper__step.is-done .tf-stepper__marker {
  background: var(--tf-green);
  border-color: var(--tf-green);
}
.tf-stepper__step.is-active .tf-stepper__marker {
  background: var(--tf-blue);
  border-color: var(--tf-blue);
  transform: scale(1.25);
}
.tf-stepper__leg { flex: 1 1 auto; height: 2px; background: var(--bs-border-color); }
.tf-stepper__leg.is-done { background: var(--tf-green); }
.tf-stepper__label { font-size: .8rem; margin-top: .5rem; color: var(--bs-secondary-color); }

/* --------------------------------------------------------------------------
   Skeleton placeholder
   -------------------------------------------------------------------------- */
.tf-skeleton {
  background: linear-gradient(90deg,
      var(--bs-tertiary-bg) 25%, var(--bs-border-color) 50%, var(--bs-tertiary-bg) 75%);
  background-size: 200% 100%;
  animation: tf-skeleton-sweep 1.5s infinite;
  border-radius: 4px;
  height: 1em;
  width: 80px;
}
.tf-skeleton--sm { display: inline-block; width: 15px; vertical-align: middle; }

@keyframes tf-skeleton-sweep {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* --------------------------------------------------------------------------
   Task toasts (was task_toasts.css). Pinned above every overlay in tf-base.css's
   ladder because a toast must be readable while a modal is open.
   -------------------------------------------------------------------------- */
#task-toast-container {
  position: fixed; bottom: 20px; left: 20px;
  z-index: 9999;
  display: flex; flex-direction: column; gap: 10px;
  max-width: 350px;
}
.task-toast { opacity: 1; transform: translateY(0); transition: opacity .3s ease-out, transform .3s ease-out; }
.task-toast.hiding { opacity: 0; transform: translateY(20px); }
.spinner { animation: tf-spin 1s linear infinite; }
@keyframes tf-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
.toast-body { font-size: .9rem; }
.toast-body div { margin-bottom: 4px; }
.toast-body div:last-child { margin-bottom: 0; }

/* Toast surfaces (8.13.26). Bootstrap fills a toast from rgba(--bs-body-bg-rgb, .85) —
   the *-rgb pair, which tf-tokens.css does not redefine (it only sets the hex), so the
   toast surface tracked Bootstrap's stock theme instead of ours and a toast rendered as
   a translucent smear of the page. Pinning the toast tokens makes it read as a raised
   card in every theme, and gives the header + body a color that follows the surface —
   the flash toasts and ToastManager both used to hardcode near-black text, which was
   invisible on the dark surface (gate feedback: "can't see the text in the toasts and
   the word Success in the top right"). */
.toast {
  --bs-toast-bg:           var(--bs-secondary-bg);
  --bs-toast-color:        var(--bs-body-color);
  --bs-toast-border-color: var(--bs-border-color);
  --bs-toast-header-bg:    var(--bs-tertiary-bg);
  --bs-toast-header-color: var(--bs-body-color);
}
/* The header title is the strong; the "Just Now" timestamp stays secondary so the
   header keeps its hierarchy now that the header color is full-strength body text. */
.toast-header small { color: var(--bs-secondary-color); }

/* --------------------------------------------------------------------------
   Shift widget (was shift_widget.css)

   In normal flow as the right-hand item of the workflow page header. It used to
   be position:absolute at a hardcoded top:140px — an offset calibrated against
   the old 3rem display-5 titles — so the compact title scale (8.10.26) slid the
   tab row up underneath it and the widget covered the last tab. Flowing it means
   there is no magic offset to re-tune the next time the type scale moves.

   position:relative is load-bearing: the "Last Updated" line inside is a
   .stretched-link, which fills its nearest POSITIONED ancestor. Without it the
   link stretches across the whole page.
   -------------------------------------------------------------------------- */
.shift-widget {
  position: relative;
  background: var(--bs-secondary-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--tf-radius-sm);
  box-shadow: var(--tf-shadow);
  padding: 12px 16px;
  min-width: 160px;
  font-size: 14px;
}
.shift-widget:hover { box-shadow: var(--tf-shadow-hover); }
/* The widget's status dot is .tf-dot + .tf-status--*; its old .status-dot/.status-pass
   rules were retired in the 8.10.26 consolidation (test_component_vocabulary.py). */
.shift-widget__line { margin: 2px 0; color: var(--bs-secondary-color); }
.shift-outcome-cell { font-size: .85rem; font-weight: 500; text-align: center; }
.shift-outcome-link {
  color: inherit; text-decoration: none;
  display: block; width: 100%; height: 100%; padding: 4px;
}
.shift-outcome-link:hover { text-decoration: underline; color: inherit; }

/* --------------------------------------------------------------------------
   Conversations (was conversations.css; used on 4 templates and trapped inside
   the bundle). Scroll styling comes from .tf-thin-scroll in tf-base.css.
   -------------------------------------------------------------------------- */
#conversation-container {
  background-color: var(--bs-tertiary-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--tf-radius-sm);
  padding: 1rem;
}
#conversation-container .card { border: none; word-wrap: break-word; }
#conversation-container .message-text { white-space: pre-wrap; word-break: break-word; }

/* --------------------------------------------------------------------------
   Triage board (was inline in triage_workflow.html). Layout only — the cards
   themselves are .tf-card.tf-card--flag.
   The sticky nav (primary + secondary = ~120px) sits in normal flow above the
   wrapper, so sizing to the remaining viewport keeps the page exactly one screen
   tall (footer overridden empty) and offcanvas footers stay reachable. Triage is
   authenticated-only, so the nav is always the full ~120px.
   -------------------------------------------------------------------------- */
.triage-wrap { height: calc(100vh - 120px); background: var(--bs-secondary-bg); }
.triage-list-col {
  overflow-y: auto;
  border-right: 1px solid var(--bs-border-color);
  background: var(--bs-secondary-bg);
}
.triage-frame-col { position: relative; }
.triage-frame { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.triage-frame-placeholder { position: absolute; inset: 0; }

/* --------------------------------------------------------------------------
   Onboarding (was a 123-line block in onboarding.html). A deliberately branded,
   theme-independent flow: the green rail gradient reads its own tokens and the
   rail text stays white in every theme. Page surfaces and text DO theme.
   -------------------------------------------------------------------------- */
.onboarding-wrap {
  display: flex; flex-direction: column;
  justify-content: center; align-items: center;
  gap: 18px; min-height: 92vh; padding: 32px 16px;
}
/* Extends .tf-card: bigger radius + deeper shadow than the standard card. */
.onboarding-shell {
  display: flex; width: 100%; max-width: 1180px; min-height: 680px;
  border-radius: 20px; overflow: hidden;
  box-shadow: 0 24px 60px rgb(15 23 42 / .18);
}
.onboarding-support { color: var(--bs-secondary-color); font-size: 13px; text-align: center; }
.onboarding-support a { color: var(--bs-secondary-color); text-decoration: underline; }

.onboarding-rail {
  width: 38%;
  background: linear-gradient(160deg, var(--tf-onboard-rail-a) 0%, var(--tf-onboard-rail-b) 100%);
  color: white;
  padding: 34px 28px;
  display: flex; flex-direction: column;
}
.onboarding-rail .brand { font-size: 22px; font-weight: 800; letter-spacing: .5px; margin-bottom: 38px; }
.ob-rail-step {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 0; opacity: .65; transition: opacity .2s ease;
}
.ob-rail-step.active { opacity: 1; font-weight: 700; }
.ob-rail-step.complete { opacity: .9; }
.ob-dot {
  flex: 0 0 26px; height: 26px; border-radius: 50%;
  background: rgb(255 255 255 / .22);
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700;
}
.ob-rail-step.active .ob-dot { background: white; color: var(--tf-onboard-rail-b); }
.ob-rail-step.complete .ob-dot { background: var(--tf-green); color: var(--tf-ink); }
.rail-foot { margin-top: auto; font-size: 12px; opacity: .8; }

.onboarding-pane { flex: 1; padding: 56px 64px; display: flex; flex-direction: column; position: relative; }
.step-counter {
  position: absolute; top: 22px; right: 28px;
  font-size: 12px; font-weight: 700; letter-spacing: .4px; text-transform: uppercase;
  color: var(--bs-secondary-color);
}
/* Form wraps the terms + profile steps but must not affect layout —
   display:contents lets its inner steps flex as direct children of the pane. */
.onboarding-form { display: contents; }
.onboarding-step { display: none; flex-direction: column; height: 100%; }
.onboarding-step.active { display: flex; }
.terms-body {
  max-height: 320px; overflow-y: auto;
  margin-bottom: 18px; padding-right: 8px;
  color: var(--bs-secondary-color); font-size: 14px;
}
.terms-body ul { padding-left: 18px; }
.terms-body li { margin-bottom: 10px; }
.step-emoji { font-size: 46px; margin-bottom: 8px; }
.step-title { font-size: 26px; font-weight: 800; color: var(--bs-body-color); margin-bottom: 8px; }
.step-sub { color: var(--bs-secondary-color); margin-bottom: 24px; }
.step-actions { margin-top: auto; padding-top: 24px; display: flex; gap: 12px; align-items: center; }
.video-frame {
  position: relative; width: 100%; padding-top: 56.25%;
  border-radius: var(--tf-radius); overflow: hidden;
  background: var(--tf-ink);   /* deliberate dark letterbox in every theme */
}
.video-frame video {
  position: absolute; top: 0; left: 0; width: 100%; height: 100%;
  border: 0; object-fit: contain; background: var(--tf-ink);
}
@media (max-width: 640px) {
  .onboarding-rail { display: none; }
  .onboarding-pane { padding: 30px 22px; }
}

/* Map container (was maps.css; depended on --card-shadow defined in new_load_page.css,
   which is exactly the cross-file trap the tokens file removes). */
.map-container {
  border-radius: var(--tf-radius);
  overflow: hidden;
  box-shadow: var(--tf-shadow);
  background: var(--bs-tertiary-bg);
  display: flex; align-items: center; justify-content: center;
}
#map_loading_animation {
  display: flex; justify-content: center; align-items: center;
  height: 550px;  /* match the map container */
  width: 100%;
}

/* Dev-ticket description well (was inline in customer_hub.html). */
.dev-ticket-description {
  padding: 1rem;
  background: var(--bs-tertiary-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: .375rem;
}
.dev-ticket-description p:last-child { margin-bottom: 0; }
.dev-ticket-description pre { background: var(--bs-secondary-bg); padding: .5rem; border-radius: .25rem; overflow-x: auto; }
.dev-ticket-description code { background: var(--bs-secondary-bg); padding: .1rem .25rem; border-radius: .2rem; }
.dev-ticket-description pre code { padding: 0; background: transparent; }

/* --------------------------------------------------------------------------
   Big-numbers layout editor (was a 105-line block in big_numbers_layout_editor.html).
   The drag-and-drop JS GENERATES these classes (bn-cell, bn-row, bn-cat-*) —
   renames here must be mirrored in static/js/big_numbers_layout_editor.js.
   -------------------------------------------------------------------------- */
.bn-palette-tile {
    padding: 0.5rem 0.75rem;
    margin-bottom: 0.35rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 0.375rem;
    background: var(--bs-secondary-bg);
    cursor: grab;
    font-size: 0.875rem;
    user-select: none;
}
.bn-palette-tile:hover { background: var(--bs-tertiary-bg); }
.bn-palette-tile.bn-cat-critical { border-left: 4px solid var(--tf-danger); }
.bn-palette-tile.bn-cat-warning  { border-left: 4px solid var(--tf-warn); }
.bn-palette-tile.bn-cat-info     { border-left: 4px solid var(--tf-blue-strong); }

.bn-row {
    border: 1px solid var(--bs-border-color);
    border-radius: 0.5rem;
    padding: 0.75rem;
    margin-bottom: 0.75rem;
    background: var(--bs-tertiary-bg);
}
.bn-row-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 0.5rem;
    font-size: 0.8rem;
    color: var(--bs-secondary-color);
}
.bn-row-cells {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    min-height: 48px;
    padding: 0.25rem;
    background: var(--bs-secondary-bg);
    border-radius: 0.375rem;
}
.bn-row-cells.bn-row-empty:before {
    content: "Drop tiles here";
    color: var(--bs-secondary-color);
    font-style: italic;
    align-self: center;
    padding-left: 0.5rem;
}
.bn-cell {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.35rem 0.6rem;
    background: var(--bs-secondary-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: 0.375rem;
    font-size: 0.85rem;
    cursor: grab;
}
.bn-cell.bn-cat-critical { border-left: 4px solid var(--tf-danger); }
.bn-cell.bn-cat-warning  { border-left: 4px solid var(--tf-warn); }
.bn-cell.bn-cat-info     { border-left: 4px solid var(--tf-blue-strong); }
.bn-cell select {
    font-size: 0.75rem;
    padding: 0.1rem 0.3rem;
    border-radius: 0.25rem;
    border: 1px solid var(--bs-border-color);
    cursor: pointer;
}
.bn-cell .bn-remove {
    background: transparent;
    border: 0;
    color: var(--tf-danger);
    padding: 0 0.15rem;
    cursor: pointer;
}

/* sum pill is tf-badge tf-badge--sm; only the ok/warn tints live here */
.bn-sum-ok    { background: var(--tf-ok-bg); color: var(--tf-ok); }
.bn-sum-warn  { background: var(--tf-warn-bg); color: var(--tf-warn); }

.bn-preview-row {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    gap: 0.4rem;
    margin-bottom: 0.4rem;
}
.bn-preview-cell {
    background: var(--bs-secondary-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: 0.375rem;
    padding: 0.4rem 0.5rem;
    font-size: 0.75rem;
    text-align: center;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.bn-preview-cell.bn-cat-critical { border-top: 3px solid var(--tf-danger); }
.bn-preview-cell.bn-cat-warning  { border-top: 3px solid var(--tf-warn); }
.bn-preview-cell.bn-cat-info     { border-top: 3px solid var(--tf-blue-strong); }

/* Exception coverage panel (8.17.26) — also JS-generated; see refreshCoverage(). */
.bn-coverage-group { margin-bottom: 0.85rem; }
.bn-coverage-group:last-child { margin-bottom: 0; }
.bn-coverage-heading {
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-bottom: 0.4rem;
}
.bn-coverage-heading--missing { color: var(--tf-warn); }
.bn-coverage-heading--shown   { color: var(--tf-ok); }
.bn-coverage-heading--zero    { color: var(--bs-secondary-color); }
.bn-coverage-list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem 0.75rem;
    align-items: center;
}
.bn-coverage-missing-item {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}
.bn-coverage-add { font-size: 0.8rem; padding: 0.15rem 0.5rem; }
.bn-coverage-levels {
    font-size: 0.75rem;
    color: var(--bs-secondary-color);
}
.bn-coverage-chip {
    font-size: 0.8rem;
    padding: 0.2rem 0.5rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 0.375rem;
    background: var(--bs-secondary-bg);
    color: var(--bs-secondary-color);
}

/* --------------------------------------------------------------------------
   Customer milestones roadmap (was a 182-line block in customer_milestones.html).
   A designed brand component — gradient markers on a horizontal route — kept
   under tf-route__*/tf-roadmap-* names. Server-rendered; no JS hooks.
   -------------------------------------------------------------------------- */
.tf-roadmap {
        --tf-spine: var(--bs-border-color);
        max-width: 62rem;
    }

    /* The route, laid out left to right. Connectors flex so stops space evenly and the
       travelled portion carries the brand gradient. */
    .tf-route {
        display: flex;
        align-items: flex-start;
        padding: .25rem 0 0;
        overflow-x: auto;
        scrollbar-width: none;
    }

    .tf-route::-webkit-scrollbar {
        display: none;
    }

    .tf-route__leg {
        flex: 1 1 0;
        height: 2px;
        min-width: .5rem;
        margin-top: 1.24rem;
        background: var(--tf-spine);
    }

    .tf-route__leg--done {
        background: linear-gradient(to right, var(--tf-green), var(--tf-blue));
    }

    /* One stop: the marker plus its short label. */
    .tf-route__stop {
        flex: 0 0 auto;
        background: none;
        border: 0;
        padding: 0 .35rem;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: .4rem;
        color: var(--bs-secondary-color);
        max-width: 6.5rem;
    }

    .tf-route__marker {
        width: 2.5rem;
        height: 2.5rem;
        border-radius: 50%;
        display: grid;
        place-items: center;
        font-weight: 700;
        font-size: .95rem;
        font-variant-numeric: tabular-nums;
        background: var(--bs-body-bg);
        border: 2px solid var(--tf-spine);
        color: var(--bs-secondary-color);
        transition: transform .15s ease;
    }

    .tf-route__marker--done {
        border-color: transparent;
        background: linear-gradient(135deg, var(--tf-green), var(--tf-blue));
        color: var(--tf-ink);
    }

    .tf-route__label {
        font-size: .72rem;
        font-weight: 600;
        line-height: 1.15;
        text-align: center;
    }

    /* The selected stop is the one loud thing in the route. */
    .tf-route__stop.active {
        color: var(--bs-body-color);
    }

    .tf-route__stop.active .tf-route__marker {
        border: 2px solid transparent;
        background: linear-gradient(var(--bs-body-bg), var(--bs-body-bg)) padding-box,
                    linear-gradient(135deg, var(--tf-green), var(--tf-blue)) border-box;
        color: var(--bs-body-color);
        transform: scale(1.14);
        box-shadow: 0 .25rem .75rem rgba(87, 177, 255, .22);
    }

    .tf-route__stop.active .tf-route__marker--done {
        background: linear-gradient(135deg, var(--tf-green), var(--tf-blue));
        color: var(--tf-ink);
    }

    .tf-route__stop:hover .tf-route__marker {
        transform: scale(1.07);
    }

    .tf-route__stop:focus-visible {
        outline: 2px solid var(--tf-blue);
        outline-offset: 3px;
        border-radius: .5rem;
    }

    .tf-roadmap .tf-card {
        border: 2px solid transparent;
        border-radius: .85rem;
        background: linear-gradient(var(--bs-body-bg), var(--bs-body-bg)) padding-box,
                    linear-gradient(120deg, var(--tf-green), var(--tf-blue)) border-box;
        box-shadow: 0 .5rem 1.75rem rgba(87, 177, 255, .13);
    }

    .tf-roadmap-meter {
        height: 6px;
        border-radius: 99px;
        background: var(--bs-secondary-bg);
        overflow: hidden;
    }

    .tf-roadmap-meter__fill {
        height: 100%;
        border-radius: 99px;
        background: linear-gradient(to right, var(--tf-green), var(--tf-blue));
    }

    /* One orchestrated moment: the headline meter fills on load. */
    .tf-roadmap-meter__fill--animate {
        animation: tf-fill .9s cubic-bezier(.22, .61, .36, 1) both;
    }

    @keyframes tf-fill {
        from { width: 0; }
    }

    .tf-roadmap-count {
        font-size: 2rem;
        line-height: 1;
        font-weight: 700;
        font-variant-numeric: tabular-nums;
    }

    .tf-roadmap-metric {
        font-size: 1.35rem;
        font-weight: 700;
        line-height: 1;
        font-variant-numeric: tabular-nums;
    }

    .tf-roadmap-steps {
        margin: 0;
        padding-left: 1.15rem;
        font-size: .925rem;
    }

    .tf-roadmap-steps > li {
        margin-bottom: .3rem;
    }

    @media (prefers-reduced-motion: reduce) {
        .tf-roadmap-meter__fill--animate { animation: none; }
        .tf-route__marker { transition: none; }
        .tf-route__stop.active .tf-route__marker { transform: none; }
        .tf-route__stop:hover .tf-route__marker { transform: none; }
    }

    /* Below ~sm the labels crowd, so the route becomes markers only and scrolls. */
    @media (max-width: 575.98px) {
        .tf-route__label { display: none; }
        .tf-route__marker { width: 2.1rem; height: 2.1rem; font-size: .82rem; }
        .tf-route__leg { margin-top: 1.05rem; }
        .tf-roadmap-count { font-size: 1.6rem; }
    }

/* ==========================================================================
   Outreach lifecycle (was outreach_lifecycle.css). The vocabulary is kept — it
   is already generically named and internally consistent — but its --ol-*
   palette now aliases the shared tokens, so it themes with everything else.
   rgba()/color-mix() are legal here; only hex literals are banned.
   ========================================================================== */
:root {
  --ol-ink: var(--tf-ink);
  --ol-brand: var(--tf-green);
  --ol-blue: var(--tf-blue);
  --ol-grey: var(--bs-secondary-color);
  /* Text on an ink-filled cell: translucent white reads on ink in BOTH themes,
     where a theme-swapped grey would vanish in dark mode. */
  --ol-grey-light: rgba(255, 255, 255, .78);
  --ol-green-text: var(--tf-ok);
  --ol-line: var(--bs-border-color);
  --ol-well: var(--bs-tertiary-bg);
  --ol-surface: var(--bs-secondary-bg);
}

/* ── Stage strip: segmented picker; selection carried by aria-pressed ── */
.stage-strip {
  display: flex;
  border: 1px solid var(--ol-line);
  border-radius: 4px;
  background: var(--ol-surface);
  overflow: hidden;
}
.stage-cell {
  flex: 1 1 0;
  position: relative;
  border: 0;
  border-right: 1px solid var(--ol-line);
  background: var(--ol-surface);
  color: inherit;
  font: inherit;
  text-align: left;
  padding: 11px 13px;
  cursor: pointer;
  transition: background-color .1s ease;
}
.stage-cell:last-child { border-right: 0; }
.stage-cell:hover { background: var(--ol-well); }
.stage-cell[aria-pressed="true"] {
  background: var(--ol-ink);
  color: var(--tf-on-ink);
}
/* The TrackFlo gradient marks the selected stage — the one flourish on the page. */
.stage-cell[aria-pressed="true"]::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: var(--tf-brand-gradient);
}
.stage-cell:focus-visible { outline: 2px solid var(--ol-blue); outline-offset: -2px; }
.stage-cell__name { display: block; font-size: 15.5px; font-weight: 700; letter-spacing: -.012em; }
.stage-cell__meta {
  display: block; margin-top: 3px; font-size: 12.5px;
  color: var(--ol-grey); font-variant-numeric: tabular-nums;
}
.stage-cell[aria-pressed="true"] .stage-cell__meta { color: var(--ol-grey-light); }

/* ── Stage caption ── */
.stage-caption {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  margin: 20px 0 12px; padding-bottom: 10px;
  border-bottom: 1px solid var(--ol-line);
}
.stage-caption h2 { margin: 0; font-size: 19px; font-weight: 700; letter-spacing: -.015em; }
.stage-caption p { margin: 0; font-size: 15px; color: var(--ol-grey); }

/* ── Rule row + time gutter ── */
.rule-row {
  display: grid;
  grid-template-columns: 124px 1fr;
  align-items: stretch;
  background: var(--ol-surface);
  border: 1px solid var(--ol-line);
  border-radius: 4px;
}
.when-gutter {
  display: flex; flex-direction: column; justify-content: center; gap: 3px;
  padding: 14px 12px;
  background: var(--ol-well);
  border-right: 1px solid var(--ol-line);
}
.when-gutter__value {
  font-size: 21px; font-weight: 700; line-height: 1; letter-spacing: -.02em;
  color: var(--bs-body-color); font-variant-numeric: tabular-nums;
}
.when-gutter__value--range { font-size: 17px; letter-spacing: -.015em; }
.when-gutter__value--none { font-size: 14.5px; font-weight: 600; letter-spacing: 0; color: var(--ol-grey); }
.when-gutter__label {
  font-size: 11px; line-height: 1.35; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ol-grey);
}
.rule-row__body { padding: 14px 16px; }
.rule-row__head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.rule-row__name { font-size: 18px; font-weight: 700; letter-spacing: -.014em; }
.rule-row__desc { margin: 5px 0 0; max-width: 64ch; font-size: 15px; line-height: 1.55; color: var(--ol-grey); }
.rule-row__foot {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin-top: 12px; padding-top: 11px;
  border-top: 1px solid var(--ol-line);
}
.rule-row__stat { font-size: 13.5px; color: var(--ol-grey); font-variant-numeric: tabular-nums; }
.rule-row__stat b { font-weight: 700; color: var(--bs-body-color); }

/* ── Channel chain ── */
.channel-chain { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 11px; }
.channel-step {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 8px; font-size: 13px; font-weight: 600;
  background: var(--ol-well);
  border: 1px solid var(--ol-line);
  border-radius: 3px;
}
.channel-step__time { font-size: 12px; color: var(--ol-grey); font-variant-numeric: tabular-nums; }
.channel-chain__tick { color: var(--ol-grey); opacity: .5; font-size: 13px; }

/* ── Status LED ── */
.status-led {
  display: inline-block;
  width: 10px; height: 10px; border-radius: 50%;
  flex: none;
  background: var(--tf-neutral);
}
.status-led--on {
  background: var(--tf-ok);
  box-shadow: 0 0 0 3.5px color-mix(in srgb, var(--tf-ok) 18%, transparent);
}
.status-state {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12.5px; font-weight: 700; color: var(--ol-grey);
}

/* ── Segment strip ── */
.segment-strip {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  margin-top: 11px; padding: 9px 12px;
  background: var(--ol-well);
  border: 1px solid var(--ol-line);
  border-left: 3px solid var(--ol-brand);
  border-radius: 3px;
}
.segment-strip__name { font-size: 15px; font-weight: 700; }
.segment-strip__filter {
  font-family: var(--bs-font-monospace);
  font-size: 12.5px;
  color: var(--ol-grey);
  overflow-wrap: anywhere;
}
.segment-strip__actions { display: flex; gap: 6px; margin-left: auto; }

/* ── Merge tags ──────────────────────────────────────────────────────────────
   Click-to-insert chips above the SMS template boxes in the segment edit modal.
   Monospace and --ol-well to match .segment-strip__filter: both are literal code
   the user types, so they should not read as prose. */
.merge-tag-bar {
  display: flex; align-items: center; gap: 5px; flex-wrap: wrap;
  margin-bottom: 12px;
}
.merge-tag-bar__label {
  font-size: 12px; font-weight: 600; color: var(--ol-grey);
  margin-right: 2px;
}
.merge-tag {
  font-family: var(--bs-font-monospace);
  font-size: 11.5px;
  line-height: 1.4;
  padding: 2px 7px;
  color: var(--ol-grey);
  background: var(--ol-well);
  border: 1px solid var(--ol-line);
  border-radius: 3px;
  cursor: pointer;
}
.merge-tag:hover,
.merge-tag:focus-visible {
  color: var(--ol-ink);
  border-color: var(--ol-brand);
}

/* ── Outreach responsive ── */
@media (max-width: 768px) {
  .stage-strip { flex-wrap: wrap; }
  .stage-cell { flex: 1 1 40%; }
  .rule-row { grid-template-columns: 1fr; }
  .when-gutter {
    flex-direction: row; align-items: baseline; gap: 8px;
    border-right: 0;
    border-bottom: 1px solid var(--ol-line);
  }
}
@media (prefers-reduced-motion: reduce) {
  .stage-cell { transition: none; }
}

/* --------------------------------------------------------------------------
   Service levels: lifecycle stages and setting rows (9.7.26).

   Replaces a single-column form that stacked ten threshold inputs and then
   twenty-three switches, so the number a switch depended on sat twenty fields
   away from it. The page is now six .tf-panel stages in lifecycle order, each
   holding .tf-setting rows that carry the switch, the plain-language
   definition, and that exception's own thresholds together.

   Shared by the admin form and the read-only view, which is why the row is
   generic about its right-hand cluster: an editable .tf-num on the form, a
   read-only .tf-ref chip everywhere else.
   -------------------------------------------------------------------------- */
/* Six stages of settings rows is a dense reference table, not six feature cards, so
   the panels tighten: .tf-panel's 1.5rem padding and 1.5rem title are sized for a
   page carrying two or three of them. Written as a modifier rather than editing
   .tf-panel, which the load pages and focused views share. */
.tf-panel--dense { padding: 1rem 1.25rem; margin-bottom: 1rem; }
.tf-panel--dense .tf-panel__head { margin-bottom: .75rem; padding-bottom: .5rem; }
.tf-panel--dense .tf-panel__title { font-size: 1.125rem; }

.tf-stage__blurb {
  margin: .125rem 0 0;
  color: var(--bs-secondary-color);
  font-size: .8125rem;
  line-height: 1.4;
  max-width: 76ch;   /* the blurbs are prose; a full-width panel over-runs a comfortable measure */
}

/* A collapsed panel would otherwise keep the head's rule and bottom padding over an empty
   body, leaving a boxed strip of nothing. Only Setup collapses, but the rule is written
   against the structure rather than that one id. */
.tf-panel:has(> .collapse:not(.show)) .tf-panel__head {
  margin-bottom: 0;
  padding-bottom: 0;
  border-bottom: 0;
}

/* One exception. Grid rather than flex so the switch column stays aligned down
   the whole stage regardless of how long a description wraps — running your eye
   down that column is how you read what a tier watches for. */
.tf-setting {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 0 .875rem;
  align-items: start;
  padding: .5rem 0;
  border-bottom: 1px solid var(--bs-border-color);
}
.tf-setting:last-child { border-bottom: 0; padding-bottom: 0; }
.tf-setting:first-child { padding-top: 0; }

.tf-setting__switch { padding-top: .125rem; }
/* The switch is the control; the label text belongs to the body column, so the
   Bootstrap wrapper must not reserve its usual label indent. */
.tf-setting__switch .form-check { min-height: 0; margin: 0; padding-left: 0; }
.tf-setting__switch .form-check-input { margin: 0; float: none; }

.tf-setting__label { font-weight: 600; margin: 0; line-height: 1.3; }
.tf-setting__desc {
  margin: .0625rem 0 0;
  color: var(--bs-secondary-color);
  font-size: .8125rem;
  line-height: 1.35;
  max-width: 78ch;
}

/* An exception that is off is inert for this tier, and most tiers run a minority
   of the twenty-three. Muting the label (rather than lowering opacity, which
   would take the description under AA) says so while keeping every row readable.
   :has() means the state follows the checkbox with no JS — the count badges in
   the panel heads are scripted, but the row state is not.

   The :has(.form-check-input) qualifier is load-bearing: the read-only view reuses
   .tf-setting with a status icon and no checkbox, so an unqualified
   :not(:has(:checked)) matched every row there and muted every ACTIVE exception. */
.tf-setting:has(.form-check-input):not(:has(.form-check-input:checked)) .tf-setting__label {
  font-weight: 400;
  color: var(--bs-secondary-color);
}

/* Right-hand cluster: the thresholds this exception reads. Wraps under the body
   on narrow screens via the stacking rule below. */
.tf-setting__thresholds {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  justify-content: flex-end;
}

/* An editable threshold: caption over a narrow number field with its unit. */
.tf-num { display: flex; flex-direction: column; gap: .125rem; }
.tf-num__label {
  font-size: .6875rem;
  font-weight: 600;
  color: var(--bs-secondary-color);
  white-space: nowrap;
}
.tf-num__field { display: flex; align-items: center; gap: .375rem; }
.tf-num__field .form-control { width: 4.5rem; text-align: right; }
.tf-num__unit { font-size: .75rem; color: var(--bs-secondary-color); }

/* A threshold this exception reads but does not own. Four of the ten are shared
   across two lifecycle stages and the tier stores one value, so only the owning
   row gets the input; the rest state the live value and say where to change it. */
.tf-ref {
  display: flex;
  flex-direction: column;
  gap: .0625rem;
  padding: .25rem .5rem;
  border: 1px dashed var(--bs-border-color);
  border-radius: var(--tf-radius-sm);
  font-size: .75rem;
  white-space: nowrap;
}
.tf-ref__value { font-weight: 600; }
.tf-ref__owner { color: var(--bs-secondary-color); font-size: .6875rem; }

/* Sticky action bar. Sits directly under the ~120px sticky nav (see the triage
   block above) and holds the save control, which used to be reachable only after
   scrolling past every field on the page. */
.tf-form-bar {
  position: sticky;
  top: 120px;
  z-index: 3;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1rem;
  padding: .625rem 1.125rem;
  background: var(--bs-secondary-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--tf-radius);
  box-shadow: var(--tf-shadow);
}
.tf-form-bar__title { font-size: 1.125rem; font-weight: 700; margin: 0; }

/* Below md the three-column row cannot hold a description and a number cluster
   side by side, so the thresholds drop under the text and left-align. */
@media (max-width: 767.98px) {
  .tf-setting { grid-template-columns: auto minmax(0, 1fr); }
  .tf-setting__thresholds {
    grid-column: 2;
    justify-content: flex-start;
    margin-top: .5rem;
  }
  .tf-form-bar { position: static; }
}
