/* ==========================================================================
   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-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;
  background: currentColor;   /* takes its color from the .tf-status--* modifier */
}

/* 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 — video_tutorials.html and
   customer_milestones.html 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. */
.btn-primary {
  color: var(--tf-ink) !important;
  border-color: var(--tf-ink) !important;
  background-color: transparent !important;
}
.btn-primary:hover, .btn-primary:focus, .btn-primary:active, .btn-primary.active {
  color: var(--tf-on-ink) !important;
  border-color: var(--tf-on-ink) !important;
  background-color: var(--tf-ink) !important;
}
.btn-sm { font-size: 14px; }
.form-check-input:checked {
  background-color: var(--tf-blue);
  border-color: var(--tf-blue);
}

/* 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;
}

/* --------------------------------------------------------------------------
   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; }

/* --------------------------------------------------------------------------
   Shift widget (was shift_widget.css)
   -------------------------------------------------------------------------- */
.shift-widget {
  position: absolute; top: 140px; right: 20px;
  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;
  z-index: 10;
}
.shift-widget:hover { box-shadow: var(--tf-shadow-hover); }
.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%;
}

/* Demo-mode indicator pill (was inline in base.html). Deliberately translucent so it
   whispers on any theme; hover brings it to full opacity. */
.tf-demo-pill {
  position: fixed; bottom: 10px; left: 10px;
  background: rgb(0 0 0 / .15);
  color: rgb(0 0 0 / .3);
  padding: 3px 10px;
  border-radius: 12px;
  font-size: 11px;
  z-index: 9999;
  cursor: pointer;
  opacity: .4;
}
.tf-demo-pill:hover { opacity: 1; }
[data-bs-theme="dark"] .tf-demo-pill {
  background: rgb(255 255 255 / .12);
  color: rgb(255 255 255 / .35);
}

/* 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); }

/* --------------------------------------------------------------------------
   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; }
    }

/* --------------------------------------------------------------------------
   Video tutorials (was a 197-line block in video_tutorials.html). Cards open
   the player modal via data-bs attributes; no JS class hooks.
   -------------------------------------------------------------------------- */
.tf-video-grid { max-width: 78rem; }

    /* A tutorial card is a button: the whole thing opens the player. */
    .tf-tutorial-card {
        display: block;
        width: 100%;
        padding: 0;
        text-align: left;
        background: var(--bs-body-bg);
        border: 1px solid var(--bs-border-color);
        border-radius: .75rem;
        overflow: hidden;
        color: inherit;
        transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
    }

    /* Poster left, everything else right. Stacks below `sm`, where a side-by-side split
       would leave the poster too narrow to read and the copy column too cramped. */
    .tf-card-row {
        display: flex;
        flex-direction: column;
    }

    .tf-tutorial-body {
        flex: 1 1 auto;
        min-width: 0;
        display: flex;
        flex-direction: column;
        justify-content: center;
        gap: .35rem;
        padding: 1rem 1.25rem;
    }

    @media (min-width: 576px) {
        .tf-card-row {
            flex-direction: row;
            align-items: stretch;
        }

        /* clamp() keeps the poster readable on a laptop without letting it eat half the
           row on an ultrawide. align-self:stretch matches a tall copy column; the
           object-fit on the media inside keeps the framing right at any height. */
        .tf-thumb-wrap {
            flex: 0 0 clamp(15rem, 30%, 24rem);
            align-self: stretch;
        }
    }

    .tf-tutorial-card:hover,
    .tf-tutorial-card:focus-visible {
        transform: translateY(-3px);
        border-color: transparent;
        box-shadow: 0 .5rem 1.5rem rgba(87, 177, 255, .2);
        outline: none;
    }

    .tf-tutorial-card:focus-visible {
        box-shadow: 0 0 0 3px var(--tf-blue);
    }

    /* 16:9 poster frame. object-fit keeps odd thumbnail sizes from distorting. */
    .tf-thumb-wrap {
        position: relative;
        aspect-ratio: 16 / 9;
        background: linear-gradient(135deg, rgba(145, 225, 138, .18), rgba(87, 177, 255, .22));
        overflow: hidden;
    }

    .tf-thumb {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
    }

    /* Play badge sits over the poster and grows slightly on hover. */
    .tf-play {
        position: absolute;
        inset: 0;
        display: grid;
        place-items: center;
    }

    .tf-play-badge {
        width: 3.25rem;
        height: 3.25rem;
        border-radius: 50%;
        display: grid;
        place-items: center;
        background: linear-gradient(135deg, var(--tf-green), var(--tf-blue));
        color: var(--tf-ink);
        font-size: 1.1rem;
        box-shadow: 0 .25rem .75rem rgba(30, 32, 39, .35);
        transition: transform .15s ease;
    }

    .tf-tutorial-card:hover .tf-play-badge {
        transform: scale(1.12);
    }

    .tf-duration {
        position: absolute;
        right: .5rem;
        bottom: .5rem;
        background: rgba(30, 32, 39, .85);
        color: white;
        font-size: .72rem;
        font-weight: 600;
        padding: .1rem .4rem;
        border-radius: .25rem;
        font-variant-numeric: tabular-nums;
    }

    /* Who the video is for. A quiet pill rather than a coloured badge — it is a
       qualifier, not a status, and shouldn't compete with the title. */
    .tf-audience {
        font-size: .72rem;
        font-weight: 600;
        padding: .1rem .45rem;
        border-radius: 99px;
        border: 1px solid var(--bs-border-color);
        color: var(--bs-secondary-color);
        white-space: nowrap;
    }

    .tf-updated {
        font-size: .72rem;
        color: var(--bs-secondary-color);
        font-variant-numeric: tabular-nums;
        white-space: nowrap;
    }

    .tf-tutorial-title {
        font-weight: 600;
        font-size: 1.05rem;
        line-height: 1.3;
    }

    /* Runtime / audience / last-updated, on one line under the description. */
    .tf-meta {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: .5rem .9rem;
        margin-top: .35rem;
    }

    .tf-meta-item {
        font-size: .75rem;
        color: var(--bs-secondary-color);
        font-variant-numeric: tabular-nums;
        white-space: nowrap;
    }

    /* Responsive player wrapper for the modal. Holds both player types — only one is
       ever un-hidden, and both fill the frame. */
    .tf-player {
        position: relative;
        padding-top: 56.25%;
        width: 100%;
    }

    .tf-player > iframe,
    .tf-player > video {
        border: none;
        position: absolute;
        top: 0;
        left: 0;
        height: 100%;
        width: 100%;
        background: var(--tf-ink);   /* deliberate dark letterbox in every theme */
    }

    .tf-player > video {
        object-fit: contain;
    }

    @media (prefers-reduced-motion: reduce) {
        .tf-tutorial-card,
        .tf-play-badge { transition: none; }
        .tf-tutorial-card:hover,
        .tf-tutorial-card:focus-visible { transform: none; }
        .tf-tutorial-card:hover .tf-play-badge { transform: none; }
    }

/* ==========================================================================
   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; }

/* ── 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; }
}
