/* ============================================================================
   ORPPIO back office (bo-*)
   Agency staff surface: dense, table-first, desktop-first. Scoped to
   body.bo-app (the agency layout) so the portal and public pages are untouched.

   This file is the COMPONENT layer. It holds no colour, no size and no radius
   of its own: every value comes from kernol.css, and the block below is the
   only place the two layers meet. The --bo-* names are the contract the
   templates and BackofficeHelper are written against — two hundred views spend
   them — so they stay, mapped onto the brand rather than restating it.

   Re-tinting the back office is an edit to kernol.css. Reshaping a component
   is an edit here. Nothing is an edit to a template.
   ============================================================================ */

:root {
  /* ═══ The bridge: --bo-* roles onto Kernol tokens ═════════════════════════
     Read this as the back office's own vocabulary. Left of the colon is what a
     component asks for; right of it is what the brand answers. */

  /* ─── Neutral ramp ─────────────────────────────────────────────────────
     Still called "navy" because that is what the templates and the sibling
     stylesheets ask for, and because it is true: the workspace greys are the
     brand navy's own hue, desaturated as they lighten. */
  --bo-navy-25:  var(--kernol-grey-25);
  --bo-navy-50:  var(--kernol-grey-50);
  --bo-navy-100: var(--kernol-grey-100);
  --bo-navy-200: var(--kernol-grey-300);
  --bo-navy-300: var(--kernol-grey-400);
  --bo-navy-400: var(--kernol-grey-600);
  --bo-navy-500: var(--kernol-grey-700);
  --bo-navy-600: var(--kernol-grey-800);
  --bo-navy-700: var(--kernol-navy-700);
  --bo-navy-850: var(--kernol-navy-900);
  --bo-navy-870: var(--kernol-chrome-850);
  --bo-navy-900: var(--kernol-chrome-900);
  --bo-navy-925: var(--kernol-chrome-950);
  --bo-navy-950: var(--kernol-navy-950);

  /* The public quotation page is written against a `slate` ramp that this file
     never declared, so every colour on it resolved to nothing. Aliased here so
     the one page a client reads before they have bought anything draws from the
     same palette as everything else. */
  --bo-slate-50:  var(--kernol-grey-50);
  --bo-slate-100: var(--kernol-grey-100);
  --bo-slate-200: var(--kernol-grey-300);
  --bo-slate-400: var(--kernol-grey-500);
  --bo-slate-500: var(--kernol-grey-600);
  --bo-slate-600: var(--kernol-grey-700);
  --bo-slate-700: var(--kernol-grey-800);
  --bo-slate-900: var(--kernol-navy-900);

  /* ─── Accents. One family per meaning, and only one. ───────────────────
     `green` is the brand pine green and `violet` is the muted plan indigo: the names are the
     templates', the colours are the brand's. Keeping the old names is what lets
     a hundred call sites of `bo_badge(:pos)` change colour without being
     touched — and there is no second green to disagree with the pine. */
  --bo-blue-50:  var(--kernol-blue-50);
  --bo-blue-100: var(--kernol-blue-100);
  --bo-blue-200: var(--kernol-blue-200);
  --bo-blue-300: var(--kernol-blue-400);
  --bo-blue-500: var(--kernol-blue-500);
  --bo-blue-600: var(--kernol-blue-600);
  --bo-blue-700: var(--kernol-blue-700);
  --bo-blue-800: var(--kernol-blue-800);

  --bo-green-50:  var(--kernol-teal-50);
  --bo-green-100: var(--kernol-teal-100);
  --bo-green-200: var(--kernol-teal-200);
  --bo-green-600: var(--kernol-teal-600);
  --bo-green-700: var(--kernol-teal-700);

  --bo-amber-50:  var(--kernol-tan-50);
  --bo-amber-100: var(--kernol-tan-100);
  --bo-amber-200: var(--kernol-tan-200);
  --bo-amber-500: var(--kernol-tan-500);
  --bo-amber-600: var(--kernol-tan-600);
  --bo-amber-700: var(--kernol-tan-700);

  --bo-violet-50:  var(--kernol-periwinkle-50);
  --bo-violet-100: var(--kernol-periwinkle-100);
  --bo-violet-200: var(--kernol-periwinkle-200);
  --bo-violet-600: var(--kernol-periwinkle-500);
  --bo-violet-700: var(--kernol-periwinkle-700);

  --bo-red-50:  var(--kernol-red-50);
  --bo-red-100: var(--kernol-red-100);
  --bo-red-200: var(--kernol-red-200);
  --bo-red-600: var(--kernol-red-600);
  --bo-red-700: var(--kernol-red-700);

  /* ─── The work canvas ──────────────────────────────────────────────────
     The page is no longer white. Orppio's workspace is a warm near-white ground with
     white cards floating on it — that separation is what lets a table, a queue
     and a metric strip sit next to each other and still read as three things.
     --bo-bg stays white because it is what a *component* is made of. */
  --bo-primary:      var(--kernol-primary);
  --bo-primary-700:  var(--kernol-primary-hover);
  --bo-bg:           var(--kernol-surface);
  --bo-bg-shell:     var(--kernol-background);
  --bo-bg-sunken:    var(--kernol-surface-secondary);
  --bo-bg-rail:      var(--kernol-surface-rail);
  --bo-card:         var(--kernol-surface);
  --bo-ink:          var(--kernol-text-primary);
  --bo-ink-soft:     var(--kernol-text-body);
  --bo-muted:        var(--kernol-text-secondary);
  --bo-faint:        var(--kernol-text-muted);
  --bo-border:       var(--kernol-border);
  --bo-border-soft:  var(--kernol-border-subtle);
  --bo-border-hover: var(--kernol-border-strong);

  /* ─── Dark chrome ──────────────────────────────────────────────────────
     The accent inverts: on the light canvas the interactive colour is blue, and
     in here it is the pine green. That is the point of it. Blue on the sidebar
     would be a second "you can act on this" competing with the buttons on the
     page, whereas the pine fill says only "you are here", which nothing else in the
     product says. */
  --bo-chrome:        var(--kernol-chrome);
  --bo-chrome-hover:  var(--kernol-chrome-hover);
  --bo-chrome-line:   var(--kernol-chrome-line);
  --bo-chrome-raised: var(--kernol-chrome-raised);
  --bo-chrome-quiet:  var(--kernol-chrome-850);
  --bo-chrome-well:   var(--kernol-chrome-950);
  --bo-chrome-floor:  var(--kernol-chrome-950);
  --bo-chrome-edge:   var(--kernol-chrome-edge);

  --bo-chrome-ink:    var(--kernol-chrome-ink);
  --bo-chrome-soft:   var(--kernol-chrome-soft);
  --bo-chrome-muted:  var(--kernol-chrome-muted);
  --bo-chrome-faint:  var(--kernol-chrome-faint);
  --bo-chrome-accent: var(--kernol-nav-active-icon);

  --bo-chrome-pill-bg:       var(--kernol-chrome-pill-bg);
  --bo-chrome-pill-ink:      var(--kernol-chrome-pill-ink);
  --bo-chrome-pill-alert-bg: var(--kernol-chrome-pill-alert-bg);
  --bo-chrome-pill-alert-ink:var(--kernol-chrome-pill-alert-ink);
  --bo-chrome-pill-info-bg:  var(--kernol-chrome-pill-info-bg);
  --bo-chrome-pill-info-ink: var(--kernol-chrome-pill-info-ink);

  /* ─── Shared component surfaces ────────────────────────────────────────
     Every table, queue, card and list draws its hover, its selection and its
     column-header band from these four and from nothing else. The header band
     is now barely a band: Orppio's tables carry their columns on weight and a
     hairline rather than on a filled strip. */
  --bo-hover:        var(--kernol-hover);
  --bo-hover-strong: var(--kernol-hover-strong);
  --bo-selected:     var(--kernol-selected);
  --bo-th-bg:        var(--kernol-grey-50);
  --bo-th-ink:       var(--kernol-text-secondary);
  --bo-th-line:      var(--kernol-border);

  --bo-cell-x:    14px;
  --bo-cell-edge: var(--bo-page-x);

  /* ─── Shape ────────────────────────────────────────────────────────────
     Controls at 8, panels at 12, the largest surfaces at 14, a badge at 6.
     Nothing in the product is rounder than 14 except an avatar. */
  --bo-r-xs:    var(--kernol-radius-xs);
  --bo-r-sm:    var(--kernol-radius);      /* buttons, inputs, chips, nav      */
  --bo-r:       var(--kernol-radius-md);   /* menus, callouts, empty states    */
  --bo-r-lg:    var(--kernol-radius-lg);   /* cards, the table card            */
  --bo-r-badge: var(--kernol-radius-sm);
  --bo-r-full:  var(--kernol-radius-full);

  --bo-shadow-card:  var(--kernol-shadow-xs);
  --bo-shadow-sm:    var(--kernol-shadow-sm);
  --bo-shadow-pop:   var(--kernol-shadow-pop);
  --bo-shadow-modal: var(--kernol-shadow-modal);

  /* ─── Metrics ──────────────────────────────────────────────────────────── */
  --bo-sidebar-w:   var(--kernol-sidebar-w);
  --bo-topbar-h:    var(--kernol-topbar-h);
  --bo-row-h:       var(--kernol-row-h);
  --bo-row-h-tight: 38px;
  --bo-page-x:      var(--kernol-page-x);
  --bo-gap:         var(--kernol-space-4);

  /* ─── Type ─────────────────────────────────────────────────────────────── */
  --bo-font:      var(--kernol-font);
  --bo-font-mono: var(--kernol-font-mono);
  --bo-t-10: var(--kernol-text-10);
  --bo-t-11: var(--kernol-text-11);
  --bo-t-12: var(--kernol-text-12);
  --bo-t-13: var(--kernol-text-13);
  --bo-t-14: var(--kernol-text-14);
  --bo-t-16: var(--kernol-text-16);
  --bo-t-18: var(--kernol-text-18);
  --bo-t-22: var(--kernol-text-22);
  --bo-t-24: var(--kernol-text-26);

  --bo-track-eyebrow: var(--kernol-tracking-eyebrow);
}

/* ─── Scoped element defaults ────────────────────────────────────────────── */
body.bo-app {
  font-family: var(--bo-font);
  color: var(--bo-ink);
  background: var(--bo-bg-shell);
  font-size: var(--bo-t-13);
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  margin: 0;
}
/* The link reset is wrapped in :where() so it carries no specificity at all. A
   backoffice link takes its colour from whatever it is part of — a table cell, a
   nav item, a breadcrumb — and a reset that outranks those component classes
   silently wins every one of those arguments. It did: with the sidebar light,
   `color: inherit` resolving to --bo-ink happened to look like the nav's own
   colour, so nothing showed. On the navy chrome the same rule painted every nav
   label navy-on-navy. Zero specificity is the fix: any class beats the reset. */
:where(.bo-app a) { color: inherit; text-decoration: none; }
.bo-app a.bo-link, .bo-app .bo-link-sm { color: var(--bo-primary); }
.bo-app a.bo-link:hover, .bo-app .bo-link-sm:hover { color: var(--bo-primary-700); }

/* ─── Type helpers ───────────────────────────────────────────────────────── */
/* The page title is the only type in the product above 18px. It is set at the
   size Kernol's own screens use — big enough to name the page from across a
   desk, and the reason the topbar underneath it carries no title of its own. */
.bo-h1      { font-size: var(--bo-t-24); font-weight: 680; letter-spacing: var(--kernol-tracking-tight); line-height: 1.2; color: var(--bo-ink); }
.bo-h2      { font-size: var(--bo-t-16); font-weight: 650; letter-spacing: -0.012em; color: var(--bo-ink); }
.bo-h3      { font-size: var(--bo-t-14); font-weight: 600; color: var(--bo-ink); }
/* A section title inside a page, one step under .bo-h1 — used where a page has
   several named regions rather than one subject. */
.bo-h1--sub { font-size: var(--bo-t-18); font-weight: 660; }
.bo-body    { font-size: var(--bo-t-13); color: var(--bo-ink-soft); }
.bo-sm      { font-size: var(--bo-t-12); color: var(--bo-ink-soft); }
.bo-xs      { font-size: var(--bo-t-12); color: var(--bo-muted); }
.bo-faint-t { color: var(--bo-faint); }
.bo-eyebrow { font-size: var(--bo-t-11); font-weight: 650; text-transform: uppercase; letter-spacing: var(--bo-track-eyebrow); color: var(--bo-muted); }
.bo-num     { font-variant-numeric: tabular-nums; }
.bo-strong  { font-weight: 600; color: var(--bo-ink); }

/* Icon sizing — bo_icon stamps .bo-ic on every svg */
.bo-ic      { width: 16px; height: 16px; flex: none; }
.bo-ic-sm   { width: 14px; height: 14px; }
.bo-ic-lg   { width: 18px; height: 18px; }
.bo-ic-xl   { width: 22px; height: 22px; }

/* ==========================================================================
   1. APP SHELL
   ========================================================================== */
.bo-shell { display: grid; grid-template-columns: var(--bo-sidebar-w) 1fr; min-height: 100vh; background: var(--bo-bg-shell); }
.bo-main  { min-width: 0; display: flex; flex-direction: column; }

/* Pages that scroll a region of themselves instead of the document — the Messagerie,
   where a scrolling page would carry the composer off screen. The page sets
   `content_for :body_class`, and every link from <body> down to the scroller is
   given a definite height here.
   The chain has to be unbroken: one link left at `auto` height, and its content
   pushes the document past the viewport again. That is what each declaration is
   for — `minmax(0, 1fr)` because an implicit grid row is `auto` and grows past a
   fixed-height grid, `min-height: 0` because a flex item refuses to shrink below
   its content without it. Nothing is clipped with `overflow: hidden`: when the
   chain holds, nothing overflows in the first place, and hiding it would only
   have made a real overflow invisible instead of absent. */
body.bo-app--fixed { height: 100dvh; }
.bo-app--fixed .bo-shell { height: 100%; min-height: 0; grid-template-rows: minmax(0, 1fr); }
/* The sidebar's own 100vh is taller than 100dvh where the two differ. */
.bo-app--fixed .bo-sidebar { height: 100%; }
.bo-app--fixed .bo-main { min-height: 0; }
.bo-app--fixed .bo-topbar { flex: none; }
.bo-app--fixed .bo-page { min-height: 0; }

/* ── The chrome: a navy sidebar, a light topbar ────────────────────────────
   The sidebar is where you go somewhere; the topbar tells you where you are and
   belongs to the page under it, so it stays on the canvas. Only the sidebar is
   navy, and everything inside it draws from --bo-chrome-*; no component in here
   reaches for a light-canvas token, because a token tuned for ink-on-white
   inverts badly. */
.bo-sidebar { background: var(--bo-chrome); border-right: 1px solid var(--bo-chrome-edge); display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh; overflow-y: auto; }
/* A long nav scrolls; its scrollbar should belong to the chrome rather than
   arrive as a pale strip out of the operating system's own palette. */
.bo-sidebar { scrollbar-width: thin; scrollbar-color: var(--bo-chrome-raised) transparent; }
.bo-sidebar::-webkit-scrollbar { width: 10px; }
.bo-sidebar::-webkit-scrollbar-track { background: transparent; }
.bo-sidebar::-webkit-scrollbar-thumb { background: var(--bo-chrome-line); border-radius: var(--bo-r-full); border: 3px solid transparent; background-clip: padding-box; }

/* ── Brand and workspace ───────────────────────────────────────────────────
   The product names itself once, at the top of the sidebar, with the light
   lockup — the wordmark is an image cut from the brand sheet rather than type,
   so it is never re-set and never stretched (height is fixed, width follows).
   Under it the *agency* names itself, in a control shaped like the switcher it
   will become when an account can hold more than one workspace. Which is the
   right hierarchy for a multi-tenant tool: whose software this is, then whose
   data you are looking at. */
/* The Orppio lockup: the extracted mark (public/branding/orppio-icon.png, a
   transparent PNG cut from the brand sheet) beside the name set in type. The
   mark carries a warm-grey counter, so it needs the dark rail under it — which
   is exactly where it lives. Type rather than a cut wordmark, because the name
   has to sit on one baseline with the rest of the rail's vocabulary and stay
   crisp at any zoom. */
.bo-brand { display: flex; align-items: center; padding: 18px var(--kernol-space-4) 10px; }
.bo-brand > a { display: inline-flex; align-items: center; gap: 10px; min-width: 0; }
.bo-brand-mark { height: 30px; width: auto; display: block; flex: none; }
.bo-brand-word { font-size: 19px; font-weight: 700; letter-spacing: -0.022em; color: var(--bo-chrome-ink); line-height: 1; }

.bo-workspace { margin: var(--kernol-space-3) var(--kernol-space-3) var(--kernol-space-2); display: flex; align-items: center; gap: var(--kernol-space-2); width: calc(100% - var(--kernol-space-6)); padding: 9px 11px; border: 1px solid var(--bo-chrome-line); border-radius: var(--bo-r-sm); background: transparent; color: var(--bo-chrome-ink); font-family: var(--bo-font); font-size: var(--bo-t-13); font-weight: 600; text-align: left; line-height: 1.25; }
.bo-workspace-name { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bo-workspace .bo-ic { color: var(--bo-chrome-muted); }
a.bo-workspace:hover, button.bo-workspace:hover { background: var(--bo-chrome-hover); border-color: var(--bo-chrome-raised); }

.bo-nav { flex: 1; padding: 4px 12px 16px; display: flex; flex-direction: column; gap: 1px; }
.bo-nav-group { margin-top: var(--kernol-space-5); }
.bo-nav-group:first-child { margin-top: var(--kernol-space-2); }
/* The group parked at the floor of the rail (Settings): pushed down by the free space
   above it and kept off the person's card by a hairline. */
.bo-nav-group--pinned { margin-top: auto; padding-top: var(--kernol-space-3); border-top: 1px solid var(--bo-chrome-line); }
.bo-nav-group-label { font-size: var(--bo-t-11); font-weight: 650; text-transform: uppercase; letter-spacing: 0.08em; color: var(--bo-chrome-faint); padding: 0 10px 6px; }
.bo-nav-item { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: var(--kernol-radius-md); font-size: var(--bo-t-13); font-weight: 500; color: var(--bo-chrome-soft); cursor: pointer; line-height: 1.3; border: 1px solid transparent; }
.bo-nav-item:hover { background: var(--bo-chrome-hover); color: var(--bo-chrome-ink); }
.bo-nav-item .bo-ic { color: var(--bo-chrome-muted); }
.bo-nav-item:hover .bo-ic { color: var(--bo-chrome-ink); }
/* The product's signature. Everywhere else in Kernol the warm accent is
   rationed; here it is spent in full, because "where am I" is the one question
   the sidebar exists to answer and nothing else on the navy competes for it.
   A peach fill, a rust label that clears 4.5:1 on that fill, a brighter tan
   icon beside it, and a ring — four signals, so the item is still obviously
   current in greyscale or through any colour vision. */
.bo-nav-item.is-active {
  background: var(--kernol-nav-active-bg);
  border-color: var(--kernol-nav-active-ring);
  color: var(--kernol-nav-active-ink);
  font-weight: 640;
  box-shadow: 0 0 0 2px rgb(46 125 90 / 0.22);
}
.bo-nav-item.is-active:hover { background: var(--kernol-teal-700); color: var(--kernol-nav-active-ink); }
.bo-nav-item.is-active .bo-ic,
.bo-nav-item.is-active:hover .bo-ic { color: var(--kernol-nav-active-icon); }
.bo-nav-item.is-active .bo-nav-count { background: var(--kernol-teal-800); color: var(--kernol-teal-100); }
.bo-nav-count { margin-left: auto; font-size: var(--bo-t-10); font-weight: 650; font-variant-numeric: tabular-nums; color: var(--bo-chrome-pill-ink); background: var(--bo-chrome-pill-bg); border-radius: var(--bo-r-full); padding: 1px 6px; line-height: 1.5; }
.bo-nav-count--alert { background: var(--bo-chrome-pill-alert-bg); color: var(--bo-chrome-pill-alert-ink); }
.bo-nav-count--info  { background: var(--bo-chrome-pill-info-bg);  color: var(--bo-chrome-pill-info-ink); }

.bo-sidebar-foot { background: var(--bo-chrome-floor); border-top: 1px solid var(--bo-chrome-line); padding: 12px; display: flex; align-items: center; gap: 10px; }
.bo-sidebar-foot .bo-name { font-size: var(--bo-t-12); font-weight: 600; color: var(--bo-chrome-ink); line-height: 1.25; }
.bo-sidebar-foot .bo-role { font-size: var(--bo-t-11); color: var(--bo-chrome-muted); }
.bo-sidebar-foot .bo-avatar { background: var(--bo-chrome-pill-info-bg); color: var(--bo-chrome-pill-info-ink); }
.bo-sidebar-foot .bo-iconbtn { color: var(--bo-chrome-muted); }
.bo-sidebar-foot .bo-iconbtn:hover { background: var(--bo-chrome-well); color: var(--bo-chrome-ink); }

/* The topbar is the page's own header: light, and separated from the canvas by
   the same hairline every other region uses. */
/* The topbar sits ON the canvas rather than on a white bar of its own: in
   Kernol the page title is the largest thing on the screen and owns the top of
   it, so a second horizontal rule above it would only cut the header in two.
   What is left up here is where-you-are on the left and the three things that
   belong to the account rather than to the page on the right. */
.bo-topbar { height: var(--bo-topbar-h); background: var(--bo-bg-shell); display: flex; align-items: center; gap: var(--kernol-space-3); padding: 0 var(--bo-page-x); position: sticky; top: 0; z-index: 40; }
.bo-crumbs { display: flex; align-items: center; gap: 6px; font-size: var(--bo-t-12); font-weight: 500; color: var(--bo-muted); min-width: 0; }
.bo-crumbs a:hover { color: var(--bo-ink); }
.bo-crumbs .bo-ic-sm { color: var(--bo-faint); }
.bo-crumb-here { color: var(--bo-ink); font-weight: 600; }
.bo-topbar-spacer { flex: 1; }

/* The palette trigger is dressed as the search field it stands in for — a real
   white control on the canvas, wide enough to read as the way you find things,
   with the shortcut parked at its right edge. It is still a button: what opens
   is the ⌘K palette, and there is no input here to type into by accident. */
.bo-searchbtn { display: flex; align-items: center; gap: var(--kernol-space-2); width: 300px; height: 38px; padding: 0 12px; border: 1px solid var(--bo-border); border-radius: var(--bo-r-sm); background: var(--kernol-surface); color: var(--bo-faint); font-size: var(--bo-t-13); cursor: pointer; font-family: var(--bo-font); box-shadow: var(--bo-shadow-card); }
.bo-searchbtn:hover { border-color: var(--bo-border-hover); color: var(--bo-muted); }
.bo-searchbtn:focus-visible { outline: none; border-color: var(--kernol-blue-400); box-shadow: var(--kernol-focus-ring); }
.bo-kbd { margin-left: auto; display: inline-flex; align-items: center; gap: 2px; font-size: var(--bo-t-10); font-weight: 600; color: var(--bo-muted); border: 1px solid var(--bo-border); border-radius: var(--bo-r-xs); background: var(--kernol-surface-secondary); padding: 2px 5px; font-family: var(--bo-font); }
/* The master-access pill: only a platform operator inside a tenant ever sees it. Warm
   rather than alarming — it is a reminder of where you are, not an error. */
.bo-masterpill { display: inline-flex; align-items: center; gap: var(--kernol-space-2); height: 30px; padding: 0 10px; border-radius: 999px; background: var(--kernol-warning-soft); color: var(--kernol-warning-ink); font-size: var(--bo-t-12); font-weight: 600; text-decoration: none; white-space: nowrap; }
.bo-masterpill:hover { text-decoration: underline; }

.bo-iconbtn { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: var(--bo-r-sm); border: none; background: transparent; color: var(--bo-muted); cursor: pointer; position: relative; }
.bo-iconbtn:hover { background: var(--bo-hover-strong); color: var(--bo-ink); }

/* The topbar's own controls: create, notifications, account. Circular, on the
   canvas, bordered rather than filled — they belong to the account and must not
   read as the page's primary action, which is the one navy button below them. */
.bo-topbar-btn { display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px; border-radius: var(--bo-r-full); border: 1px solid var(--bo-border); background: var(--kernol-surface); color: var(--bo-muted); cursor: pointer; position: relative; flex: none; box-shadow: var(--bo-shadow-card); font-family: var(--bo-font); }
.bo-topbar-btn:hover { border-color: var(--bo-border-hover); color: var(--bo-ink); }
.bo-topbar-btn--accent { color: var(--kernol-accent); }
.bo-topbar-btn:focus-visible { outline: none; border-color: var(--kernol-blue-400); box-shadow: var(--kernol-focus-ring); }
/* The create menu. <details> gives it open/close and the keyboard for free; the
   summary is dressed as the topbar's own round button and its marker removed. */
.bo-createmenu { position: relative; }
.bo-createmenu > summary { list-style: none; }
.bo-createmenu > summary::-webkit-details-marker { display: none; }
.bo-createmenu > summary::marker { content: ""; }
.bo-createmenu[open] > summary { border-color: var(--bo-border-hover); color: var(--bo-ink); }
.bo-createmenu > .bo-menu { position: absolute; right: 0; top: calc(100% + 6px); z-index: 60; min-width: 232px; }

.bo-topbar-avatar { flex: none; display: inline-flex; cursor: pointer; border-radius: var(--bo-r-full); }
.bo-topbar-avatar .bo-avatar { width: 38px; height: 38px; font-size: var(--bo-t-13); border: 1px solid var(--bo-border); }
.bo-topbar-avatar:hover .bo-avatar { border-color: var(--bo-border-hover); }
/* The account menu: the same <details> as the create menu, whose summary is the
   avatar. Its head names who is signed in; the two items are the profile and the
   way out. The sign-out is a button_to, so its form has to lay out like a link. */
.bo-accountmenu > summary { list-style: none; }
.bo-accountmenu > summary::-webkit-details-marker { display: none; }
.bo-accountmenu > summary::marker { content: ""; }
.bo-accountmenu[open] > .bo-topbar-avatar .bo-avatar { border-color: var(--bo-border-hover); }
.bo-accountmenu > .bo-menu { min-width: 240px; }
.bo-menu-head { padding: 8px 10px 6px; }
.bo-menu-head-name { font-size: var(--bo-t-13); font-weight: 620; color: var(--bo-ink); }
.bo-menu-head-meta { margin-top: 2px; font-size: var(--bo-t-12); color: var(--bo-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bo-menu-form { display: contents; }
.bo-menu-form .bo-menu-item { width: 100%; background: transparent; border: 0; font: inherit; text-align: left; cursor: pointer; }

/* ==========================================================================
   2. PAGE STRUCTURE
   ========================================================================== */
.bo-page { flex: 1; display: flex; flex-direction: column; min-width: 0; }

/* Every page opens the same way: what this page is, one line saying what it is
   for, and its actions on the right. The title carries the page, so the head
   needs no rule under it and no card around it — it sits directly on the
   canvas and the first real surface below is the tabs or the table. */
.bo-pagehead { display: flex; align-items: flex-start; gap: var(--kernol-space-4); padding: var(--kernol-space-1) var(--bo-page-x) 0; }
.bo-pagehead-titles { min-width: 0; padding-top: 1px; }
.bo-pagehead-sub { font-size: var(--bo-t-13); color: var(--bo-muted); margin-top: 3px; }
.bo-pagehead-actions { margin-left: auto; display: flex; align-items: center; gap: var(--kernol-space-2); flex: none; padding-top: 2px; }
.bo-content { padding: var(--kernol-space-5) var(--bo-page-x) var(--kernol-space-10); display: flex; flex-direction: column; gap: var(--kernol-space-4); }

/* A page's sub-navigation: the segmented control that sits between the page
   head and the page body. It spends the page gutter itself, so the tabs line up
   with the title above them and the card below them instead of hugging the
   sidebar. Whatever follows already brings its own top inset, so it sets none. */
.bo-subnav { padding: var(--kernol-space-4) var(--bo-page-x) 0; }

.bo-detail { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 0; flex: 1; border-top: 1px solid var(--bo-border); margin-top: 12px; }
.bo-detail-main { min-width: 0; padding: var(--kernol-space-5) var(--bo-page-x) var(--kernol-space-10); display: flex; flex-direction: column; gap: var(--kernol-space-4); }
/* The side rail scrolls with the page rather than in its own box: no sticky,
   no max-height, no inner scrollbar. Content taller than the viewport is
   reached by scrolling the page itself. */
.bo-detail-side { border-left: 1px solid var(--bo-border); background: var(--bo-bg-rail); padding: var(--kernol-space-5) var(--kernol-space-4); display: flex; flex-direction: column; gap: var(--kernol-space-4); }
@media (min-width: 1600px) { .bo-detail { grid-template-columns: minmax(0, 1fr) 360px; } }

/* The sticky action bar on a long form: the save is always reachable, and the
   shadow points up so the bar reads as sitting over the page it will submit. */
.bo-actionbar { position: sticky; bottom: 0; z-index: 30; background: var(--kernol-surface); border-top: 1px solid var(--bo-border); padding: var(--kernol-space-3) var(--bo-page-x); display: flex; align-items: center; gap: var(--kernol-space-2); box-shadow: 0 -6px 16px -8px rgb(12 31 23 / 0.14); }
.bo-actionbar-spacer { flex: 1; }

/* ==========================================================================
   3. BUTTONS
   ========================================================================== */
/* Four weights and a size. The rule that matters is one primary per screen:
   the navy fill is the thing this page is for, and everything else — export,
   filter, cancel, a row's own actions — is the white secondary or a ghost.
   A screen showing six navy buttons has told the reader nothing. */
.bo-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 34px; padding: 0 14px; border-radius: var(--bo-r-sm); font-family: var(--bo-font); font-size: var(--bo-t-13); font-weight: 570; line-height: 1; cursor: pointer; border: 1px solid transparent; white-space: nowrap; transition: background 0.12s, border-color 0.12s, color 0.12s, box-shadow 0.12s; }
.bo-btn:active { transform: translateY(1px); }
.bo-btn:focus-visible { outline: none; box-shadow: var(--kernol-focus-ring); }
.bo-btn--primary { background: var(--bo-primary); color: var(--kernol-text-on-dark); }
.bo-btn--primary:hover { background: var(--bo-primary-700); color: var(--kernol-text-on-dark); }
.bo-btn--secondary { background: var(--kernol-surface); color: var(--bo-ink-soft); border-color: var(--bo-border); box-shadow: var(--bo-shadow-card); }
.bo-btn--secondary:hover { border-color: var(--bo-border-hover); color: var(--bo-ink); }
.bo-btn--ghost { background: transparent; color: var(--bo-muted); }
.bo-btn--ghost:hover { background: var(--bo-hover-strong); color: var(--bo-ink); }
.bo-btn--danger { background: var(--bo-red-600); color: var(--kernol-text-on-dark); }
.bo-btn--danger:hover { background: var(--bo-red-700); color: var(--kernol-text-on-dark); }
.bo-btn--danger-soft { background: var(--kernol-surface); color: var(--bo-red-700); border-color: var(--bo-border); }
.bo-btn--danger-soft:hover { background: var(--bo-red-50); border-color: var(--bo-red-200); color: var(--bo-red-700); }
.bo-btn--sm { height: 28px; padding: 0 10px; font-size: var(--bo-t-12); }
.bo-btn--lg { height: 40px; padding: 0 18px; font-size: var(--bo-t-14); }
.bo-btn .bo-ic { width: 15px; height: 15px; }
.bo-btn[disabled], .bo-btn.is-disabled { opacity: 0.45; pointer-events: none; }

.bo-splitbtn { display: inline-flex; }
.bo-splitbtn .bo-btn:first-child { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.bo-splitbtn .bo-btn:last-child { border-top-left-radius: 0; border-bottom-left-radius: 0; margin-left: 1px; padding: 0 6px; }

/* ==========================================================================
   4. STATUS SYSTEM — dot+text default; tinted badge for lifecycle emphasis
   ========================================================================== */
.bo-st { display: inline-flex; align-items: center; gap: 6px; font-size: var(--bo-t-12); font-weight: 500; color: var(--bo-ink-soft); white-space: nowrap; }
.bo-st::before { content: ''; }
/* The status dot, drawn once. .bo-st puts it before a word; .bo-dot is the same
   dot with no word, for a legend or a feed row. Each tone is declared in exactly
   one place so the two can never disagree about what "warn" looks like. */
.bo-st::before,
.bo-dot { width: 7px; height: 7px; border-radius: var(--bo-r-full); background: var(--bo-navy-300); flex: none; display: inline-block; }
.bo-st--neutral::before, .bo-dot--neutral { background: var(--bo-navy-300); }
.bo-st--info::before,    .bo-dot--info    { background: var(--bo-blue-500); }
.bo-st--pos::before,     .bo-dot--pos     { background: var(--bo-green-600); }
.bo-st--warn::before,    .bo-dot--warn    { background: var(--bo-amber-500); }
.bo-st--neg::before,     .bo-dot--neg     { background: var(--bo-red-600); }
.bo-st--review::before,  .bo-dot--review  { background: var(--bo-violet-600); }
.bo-st--neg { color: var(--bo-red-700); font-weight: 600; }

/* A rounded rectangle, not a capsule. Kernol's statuses are read in columns of
   twenty and a screen of capsules reads as a bag of sweets; the softer corner
   keeps a status looking like a piece of data. Pastel ground, strong ink — and
   the word itself always present, so no status is carried by colour alone. */
.bo-badge { display: inline-flex; align-items: center; gap: 5px; height: 22px; padding: 0 8px; border-radius: var(--bo-r-badge); font-size: var(--bo-t-12); font-weight: 600; white-space: nowrap; letter-spacing: -0.005em; }
.bo-badge--neutral { background: var(--bo-navy-100); color: var(--bo-navy-600); }
.bo-badge--info    { background: var(--bo-blue-50); color: var(--bo-blue-700); }
.bo-badge--pos     { background: var(--bo-green-50); color: var(--bo-green-700); }
.bo-badge--warn    { background: var(--bo-amber-50); color: var(--bo-amber-700); }
.bo-badge--neg     { background: var(--bo-red-50); color: var(--bo-red-700); }
.bo-badge--review  { background: var(--bo-violet-50); color: var(--bo-violet-700); }
.bo-badge .bo-ic { width: 12px; height: 12px; }

.bo-row-flag { box-shadow: inset 3px 0 0 transparent; }
.bo-row-flag--neg  { box-shadow: inset 3px 0 0 var(--bo-red-600); }
.bo-row-flag--warn { box-shadow: inset 3px 0 0 var(--bo-amber-500); }
.bo-row-flag--review { box-shadow: inset 3px 0 0 var(--bo-violet-600); }

/* ==========================================================================
   5. VIEW TABS, TOOLBAR, FILTER CHIPS
   ========================================================================== */
/* The status rail above a table. The current view is a white tab lifted off the
   canvas onto the surface below it — the same move the table itself makes — and
   pinned with a navy underline; the rest stay flat on the canvas. Each carries
   its own count, so the shape of the work is legible before anything is
   clicked, and a zero is shown rather than hidden. */
.bo-viewtabs { display: flex; align-items: flex-end; gap: 2px; border-bottom: 1px solid var(--bo-border); padding: 0 var(--bo-page-x); overflow-x: auto; scrollbar-width: none; }
.bo-viewtabs::-webkit-scrollbar { display: none; }
.bo-viewtab { display: inline-flex; align-items: center; gap: 7px; padding: 9px 14px; font-size: var(--bo-t-13); font-weight: 520; color: var(--bo-muted); border: 1px solid transparent; border-bottom: 2px solid transparent; border-radius: var(--bo-r-sm) var(--bo-r-sm) 0 0; margin-bottom: -1px; cursor: pointer; white-space: nowrap; }
.bo-viewtab:hover { color: var(--bo-ink); background: var(--bo-hover); }
/* A tab that names a section rather than filtering a list carries its icon.
   The glyph follows the label's own weight — quiet until the tab is the page. */
.bo-viewtab .bo-ic { color: var(--bo-faint); }
.bo-viewtab:hover .bo-ic { color: var(--bo-muted); }
.bo-viewtab.is-active .bo-ic { color: var(--kernol-navy-900); }
.bo-viewtab.is-active { background: var(--kernol-surface); border-color: var(--bo-border-soft); border-bottom-color: var(--kernol-navy-900); color: var(--bo-ink); font-weight: 650; }
.bo-viewtab.is-active:hover { background: var(--kernol-surface); }
.bo-tabcount { font-size: var(--bo-t-11); font-weight: 650; font-variant-numeric: tabular-nums; color: var(--bo-muted); background: var(--kernol-surface-secondary); border-radius: var(--bo-r-badge); padding: 1px 6px; min-width: 20px; text-align: center; }
.bo-viewtab.is-active .bo-tabcount { background: var(--kernol-grey-100); color: var(--bo-ink); }
.bo-tabcount--alert { background: var(--bo-red-50); color: var(--bo-red-700); }
.bo-viewtab.is-active .bo-tabcount--alert { background: var(--bo-red-50); color: var(--bo-red-700); }

.bo-toolbar { display: flex; align-items: center; gap: var(--kernol-space-2); padding: var(--kernol-space-3) var(--bo-page-x); flex-wrap: wrap; }
/* A filter bar directly under the page head, with no view tabs to separate the
   two. Four index pages carried this as the same inline style. */
.bo-toolbar--ruled { border-top: 1px solid var(--bo-border); margin-top: var(--kernol-space-3); }
.bo-toolbar-spacer { flex: 1; }

/* The filter bar as its own white surface, sitting above the table card rather
   than floating on the canvas — Kernol treats "how this list is narrowed" as
   part of the list, not as page chrome. Inset to the page gutter, and it drops
   the gutter again inside a container that already spends it. */
.bo-toolbar--filters { margin: var(--kernol-space-4) var(--bo-page-x) 0; padding: var(--kernol-space-3) var(--kernol-space-3); background: var(--kernol-surface); border: 1px solid var(--bo-border); border-radius: var(--bo-r-lg); box-shadow: var(--bo-shadow-card); }
.bo-content .bo-toolbar--filters,
.bo-detail-main .bo-toolbar--filters,
.bo-card .bo-toolbar--filters { margin-inline: 0; }
.bo-content > .bo-toolbar--filters,
.bo-detail-main > .bo-toolbar--filters { margin-top: 0; }
/* A filter bar and the table it narrows are one object: no canvas between. */
.bo-toolbar--filters + .bo-tablewrap { margin-top: var(--kernol-space-2); }

.bo-search { display: flex; align-items: center; gap: var(--kernol-space-2); height: 38px; padding: 0 12px; border: 1px solid var(--bo-border); border-radius: var(--bo-r-sm); background: var(--kernol-surface); width: 260px; color: var(--bo-faint); }
.bo-search input { border: none; outline: none; background: transparent; font-family: var(--bo-font); font-size: var(--bo-t-13); color: var(--bo-ink); width: 100%; }
.bo-search input::placeholder { color: var(--bo-faint); }
.bo-search:focus-within { border-color: var(--kernol-blue-400); box-shadow: var(--kernol-focus-ring); }

/* A filter bar: one search that takes the slack, and selects sized to their own
   content, all on the same line.
   .bo-select is width:100% by design — it is a form-field control first — so inside
   the wrapping toolbar each select claims a whole row and the bar reads as four
   stacked inputs instead of one strip. The flex-basis on the search is what makes it
   the element that grows; the selects keep their natural width and only the search
   gives ground when the window narrows, down to min-width, after which the bar wraps
   as it did before. */
.bo-toolbar--filters .bo-search { flex: 1 1 300px; width: auto; min-width: 200px; }
/* A select in a filter bar is a filter *button*, not a form field: it keeps its
   own width, stands on the same white as the bar, and is bordered rather than
   filled so the bar reads as a row of equal choices. */
/* A <select> sizes itself to its widest option, and a country list is very wide
   indeed — capped, or four filters push the search off the row. */
.bo-toolbar--filters .bo-select { flex: 0 1 auto; width: auto; min-width: 136px; max-width: 190px; height: 38px; font-weight: 550; color: var(--bo-ink-soft); }
.bo-toolbar--filters .bo-btn { height: 38px; }

.bo-chip { display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 9px; border: 1px solid var(--bo-border); border-radius: var(--bo-r-sm); background: #FFFFFF; font-size: var(--bo-t-12); font-weight: 500; color: var(--bo-ink-soft); cursor: pointer; white-space: nowrap; font-family: var(--bo-font); }
.bo-chip:hover { border-color: var(--bo-border-hover); }
.bo-chip .bo-ic { width: 13px; height: 13px; color: var(--bo-muted); }
.bo-chip--active { background: var(--bo-blue-50); border-color: var(--bo-blue-200); color: var(--bo-blue-800); }
.bo-chip--active .bo-ic { color: var(--bo-blue-600); }
.bo-chip--add { border-style: dashed; color: var(--bo-muted); background: transparent; }
.bo-chip--add:hover { color: var(--bo-ink); background: #FFFFFF; }

/* A select shaped like a button, for a page head or an inline control where a
   full-width form field would stretch across the row. */
.bo-chipselect { height: 34px; border: 1px solid var(--bo-border); border-radius: var(--bo-r-sm); background: var(--kernol-surface); font-family: var(--bo-font); font-size: var(--bo-t-13); font-weight: 550; color: var(--bo-ink-soft); padding: 0 30px 0 12px; appearance: none; box-shadow: var(--bo-shadow-card); background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%237E899B' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 10px center; }
.bo-chipselect:hover { border-color: var(--bo-border-hover); }
.bo-chipselect:focus-visible { outline: none; border-color: var(--kernol-blue-400); box-shadow: var(--kernol-focus-ring); }

/* Two small helpers the index pages used to carry as inline `style` attributes. */
.bo-note { margin: 0 0 var(--kernol-space-3); }
.bo-more { margin-top: var(--kernol-space-4); display: flex; justify-content: center; }

/* ==========================================================================
   6. TABLES — one container, everywhere
   Every table in the backoffice is the same card: inset from the page edges,
   bordered, rounded, with its own cell gutter and its own caption under it.
   There is one class, and a view does not choose a look — the only thing left
   to say is whether the table *is* a card body already, which is --flush.

   Spacing is a margin on the wrapper rather than padding on whatever holds it,
   so a table dropped into a new page brings its own inset. The three rules
   below zero the horizontal half of it inside containers that already spend the
   page gutter; that is the whole of the placement logic, in one place, instead
   of an inline `style` per view.

   The gutter is the --bo-cell-edge custom property rather than a pair of
   :first-child/:last-child rules per variant.
   ========================================================================== */
.bo-tablewrap {
  --bo-cell-edge: var(--bo-cell-x);
  margin: var(--kernol-space-4) var(--bo-page-x) 0;
  border: 1px solid var(--bo-border);
  border-radius: var(--bo-r-lg);
  background: var(--bo-card);
  box-shadow: var(--bo-shadow-card);
  overflow-x: auto;
}
/* Anything that already spends the page gutter: the table drops the sides and
   keeps its top inset. */
.bo-content .bo-tablewrap,
.bo-detail-main .bo-tablewrap,
.bo-card .bo-tablewrap,
.bo-content .bo-tablefoot,
.bo-detail-main .bo-tablefoot,
.bo-card .bo-tablefoot { margin-inline: 0; }

/* .bo-content and .bo-detail-main also space their own children with `gap`, so a
   *direct* child gives up its top inset to them — and only a direct child, since
   a table nested a level deeper is not a flex item of theirs and still has to
   space itself off whatever it follows. */
.bo-content > .bo-tablewrap,
.bo-detail-main > .bo-tablewrap { margin-top: 0; }

/* The table *is* the card body: the card around it already draws the border and
   the corners, so the wrapper draws neither and takes the card's own gutter. */
.bo-tablewrap--flush { --bo-cell-edge: 14px; margin: 0; border: none; border-radius: 0; box-shadow: none; }

.bo-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--bo-t-13); }

/* ONE column-header band. Every list in the backoffice that names its columns
   wears it: this table, the quotation line items, the record shelf. They are
   grouped into a single rule rather than each restating the same six
   declarations, which is what let them drift apart in the first place.

   Kernol's band is barely a band. The old one was a filled strip in shouting
   caps, which on a page of five tables produced five grey stripes competing
   with the data. Here the header is near-white, sentence case at reading size,
   and separated from the rows by a single hairline: weight and position say
   "these are the columns", and the ink saved goes to the records. */
.bo-colhead,
.bo-table thead th,
.bo-li-head,
.bo-rec-group { background: var(--bo-th-bg); color: var(--bo-th-ink); border-bottom: 1px solid var(--bo-th-line); font-size: var(--bo-t-12); font-weight: 620; letter-spacing: 0; }

.bo-table thead th { position: sticky; top: 0; z-index: 10; text-align: left; padding: 11px var(--bo-cell-x); white-space: nowrap; }
.bo-table thead th:first-child { padding-left: var(--bo-cell-edge); }
.bo-table thead th:last-child  { padding-right: var(--bo-cell-edge); }
.bo-table tbody td { border-bottom: 1px solid var(--bo-border-soft); padding: 10px var(--bo-cell-x); height: var(--bo-row-h); vertical-align: middle; }
.bo-table tbody td:first-child { padding-left: var(--bo-cell-edge); }
.bo-table tbody td:last-child  { padding-right: var(--bo-cell-edge); }
.bo-table tbody tr:hover { background: var(--bo-hover); }
.bo-table tbody tr.is-selected { background: var(--bo-selected); }
.bo-table tbody tr.is-muted td { color: var(--bo-faint); }
.bo-table tbody tr.is-muted .bo-entity-name { color: var(--bo-muted); }
.bo-td-num { text-align: right; font-variant-numeric: tabular-nums; }
.bo-td-meta { color: var(--bo-muted); font-size: var(--bo-t-12); }
.bo-td-actions { text-align: right; white-space: nowrap; }

/* A column head sits over its own column. `thead th` sets text-align for every
   header cell, which outranks a bare class, so the right-aligned columns have to
   restate it here at the same weight — without this the head of a money column
   sits at the left of figures that end at the right. */
.bo-table thead th.bo-td-num,
.bo-table thead th.bo-td-actions { text-align: right; }

/* The wrapper clips to its own radius, so the header's corners follow it; the
   last row's rule would still draw a straight line across the rounded bottom. */
.bo-table tbody tr:last-child td { border-bottom: none; }
/* Except when the table is a card body and the card continues below it. */
.bo-tablewrap--flush .bo-table tbody tr:last-child td { border-bottom: 1px solid var(--bo-border-soft); }

/* The caption belongs to the table above it: same inset, no rule of its own. */
.bo-tablefoot { display: flex; align-items: center; gap: var(--kernol-space-3); margin: var(--kernol-space-2) var(--bo-page-x) 0; font-size: var(--bo-t-12); color: var(--bo-muted); }
/* Pulled back through the container's `gap`, or the rhythm meant for separate
   blocks parts the caption from the thing it is captioning. Direct children
   only: a caption further down is not a flex item and would just ride up over
   the row above it. */
.bo-content > .bo-tablefoot,
.bo-detail-main > .bo-tablefoot { margin-top: -8px; }
/* A caption directly inside a card is that card's footer strip, so it takes the
   card's own gutter and its own hairline rather than a margin. */
.bo-card > .bo-tablefoot { margin: 0; padding: 8px 14px 10px; border-top: 1px solid var(--bo-border-soft); }

/* Table cell atoms */
/* The identity cell: a face, the name, and the one line under it that says
   which one this is. Used for a client, a pet, a journey, a template — anything
   a person recognises rather than reads. */
.bo-entity { display: flex; align-items: center; gap: var(--kernol-space-3); min-width: 0; }
.bo-entity-txt { min-width: 0; line-height: 1.35; }
.bo-entity-name { font-weight: 620; color: var(--bo-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bo-entity-sub { font-size: var(--bo-t-12); color: var(--bo-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 1px; }

.bo-route { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; font-size: var(--bo-t-12); color: var(--bo-ink-soft); font-weight: 500; }
.bo-route .bo-ic { width: 12px; height: 12px; color: var(--bo-faint); }

.bo-avatar { width: 28px; height: 28px; border-radius: var(--bo-r-full); background: var(--bo-blue-50); color: var(--bo-blue-800); display: inline-flex; align-items: center; justify-content: center; font-size: var(--bo-t-11); font-weight: 650; flex: none; letter-spacing: 0.01em; overflow: hidden; }
.bo-avatar--sm { width: 24px; height: 24px; font-size: var(--bo-t-10); }
.bo-avatar--md { width: 32px; height: 32px; font-size: var(--bo-t-12); }
.bo-avatar--lg { width: 40px; height: 40px; font-size: var(--bo-t-13); }
.bo-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* An animal is warmth in an operations table, so it gets the warm ground — and
   a squircle rather than a circle, which is how a pet reads as a different kind
   of thing from a person at a glance down the column. */
.bo-avatar--pet { border-radius: var(--kernol-radius-md); background: var(--kernol-tan-soft); color: var(--kernol-tan-700); }
.bo-avatar--empty { background: transparent; border: 1.5px dashed var(--bo-navy-300); color: var(--bo-faint); }
.bo-avatar-stack { display: inline-flex; }
.bo-avatar-stack .bo-avatar { border: 1.5px solid #FFFFFF; margin-left: -6px; }
.bo-avatar-stack .bo-avatar:first-child { margin-left: 0; }

/* One pet on a lead: identity across the top, the record's facts in two key/value
   columns under it. The inner card sits on the pets card's body, so it keeps the
   sunk ground rather than a second shadow. */
.bo-petdetail { box-shadow: none; background: var(--kernol-surface-secondary, var(--kernol-surface)); }
.bo-petdetail-head { display: flex; align-items: flex-start; gap: 10px; }

.bo-assignee { display: inline-flex; align-items: center; gap: 7px; font-size: var(--bo-t-12); color: var(--bo-ink-soft); white-space: nowrap; }
.bo-assignee--none { color: var(--bo-amber-700); font-weight: 550; }

.bo-prog { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap; }
.bo-prog-track { display: inline-flex; gap: 2px; }
.bo-prog-seg { width: 9px; height: 5px; border-radius: 2px; background: var(--bo-navy-200); }
.bo-prog-seg.is-done { background: var(--bo-green-600); }
.bo-prog-seg.is-active { background: var(--bo-blue-500); }
.bo-prog-seg.is-blocked { background: var(--bo-red-600); }
.bo-prog-label { font-size: var(--bo-t-11); color: var(--bo-muted); font-variant-numeric: tabular-nums; }

.bo-bar { height: 5px; border-radius: var(--bo-r-full); background: var(--bo-navy-100); overflow: hidden; width: 100%; display: block; }
.bo-bar-fill { height: 100%; border-radius: var(--bo-r-full); background: var(--bo-blue-500); display: block; }
.bo-bar-fill--pos { background: var(--bo-green-600); }

.bo-date { white-space: nowrap; font-variant-numeric: tabular-nums; color: var(--bo-ink-soft); font-size: var(--bo-t-12); }
.bo-date-rel { color: var(--bo-muted); font-size: var(--bo-t-11); }
.bo-date--overdue, .bo-date--overdue .bo-date-rel { color: var(--bo-red-700); font-weight: 600; }
.bo-date--soon, .bo-date--soon .bo-date-rel { color: var(--bo-amber-700); font-weight: 550; }

/* ==========================================================================
   7. CARDS, PANELS, KEY-VALUES, CALLOUTS
   ========================================================================== */
/* ONE surface. Everything that is a raised box on the work canvas — a card, a
   metric, the quotation line items, a milestone, a template node, a composer,
   an automation card — is the same box (a table brings its own; see 6). Grouped
   for the same reason as the header band: five separate declarations of "white,
   1px border, 8px radius" are five chances to be almost the same. */
.bo-card,
.bo-metric,
.bo-lineitems,
.bo-mstone,
.bo-tnode,
.bo-composer,
.bo-auto-card { background: var(--bo-card); border: 1px solid var(--bo-border); border-radius: var(--bo-r-lg); box-shadow: var(--bo-shadow-card); }

/* ONE panel head: the named strip at the top of a surface. */
.bo-card-head,
.bo-tnode-head,
.bo-mstone-head { display: flex; align-items: center; gap: var(--kernol-space-2); padding: var(--kernol-space-3) var(--kernol-space-4); border-bottom: 1px solid var(--bo-border-soft); }
.bo-card-title { font-size: var(--bo-t-14); font-weight: 650; color: var(--bo-ink); display: flex; align-items: center; gap: var(--kernol-space-2); letter-spacing: -0.008em; }
.bo-card-title .bo-ic { color: var(--bo-muted); width: 15px; height: 15px; }
.bo-card-head .bo-link-sm { margin-left: auto; }
.bo-card-body { padding: var(--kernol-space-4); }

/* A card that names itself with a soft-tinted icon tile rather than a bare
   glyph — for the few panels on a dashboard that are landmarks rather than
   list containers. The tile takes the tone of what the card is about. */
.bo-card-icon { width: 30px; height: 30px; border-radius: var(--kernol-radius); display: inline-flex; align-items: center; justify-content: center; flex: none; background: var(--kernol-neutral-soft); color: var(--kernol-neutral-ink); }
.bo-card-icon .bo-ic { width: 16px; height: 16px; color: currentColor; }
.bo-card-icon--info { background: var(--kernol-info-soft); color: var(--kernol-info-ink); }
.bo-card-icon--pos  { background: var(--kernol-success-soft); color: var(--kernol-success-ink); }
.bo-card-icon--warn { background: var(--kernol-warning-soft); color: var(--kernol-warning-ink); }
.bo-card-icon--neg  { background: var(--kernol-danger-soft); color: var(--kernol-danger-ink); }
.bo-card-icon--review { background: var(--kernol-plan-soft); color: var(--kernol-plan-ink); }
.bo-card-head .bo-card-icon + .bo-card-title,
.bo-card-head .bo-card-icon + .bo-setting-txt .bo-card-title { font-size: var(--bo-t-16); }
.bo-card-body--flush { padding: 0; }

/* ── Settings blocks ───────────────────────────────────────────────────────
   The settings screen is a grid of areas that hold wildly different things: a
   form, a list of bank accounts, one sentence about a provider nobody has
   connected yet. Left to themselves they came out as cards of five different
   shapes and the screen read as a pile rather than as a set.

   So every area is the same card. A head that names it — tinted tile, title,
   one line of what it is for, and a badge saying how it stands. A body that
   stretches to whatever its neighbour's body needs. A foot that pins the way in
   to the bottom edge. Two cards side by side therefore line up at the top and
   at the bottom however different their middles are, which is the whole point:
   the eye reads down a column of states instead of down a ragged column of
   boxes.

   The tile's tone is the area's *state*, not its subject — green is set up and
   working, amber is waiting on somebody, grey is off or not started. Which area
   you are looking at is the icon's job, so colour keeps meaning one thing here
   as it does everywhere else in the backoffice. */
.bo-setting { display: flex; flex-direction: column; }
.bo-setting-txt { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.bo-setting-sub { font-size: var(--bo-t-12); color: var(--bo-muted); line-height: 1.35; }
.bo-setting-status { margin-left: auto; flex: none; display: flex; align-items: center; gap: 6px; }
/* Takes the slack so the feet of a row of cards sit on one line. */
.bo-setting-body { flex: 1 1 auto; }
/* An area nobody has set up yet has nothing to list, and next to a neighbour that
   does it was ending up as a sentence at the top of 200px of white. Centred, and
   with the area's own icon above it, that space becomes the empty state instead of
   a gap where content failed to appear. */
.bo-setting-body--empty { display: flex; flex-direction: column; justify-content: center; }
.bo-setting-body--empty > .bo-empty { padding: var(--kernol-space-5) var(--kernol-space-3); }
.bo-setting-foot { margin-top: auto; display: flex; align-items: center; flex-wrap: wrap; gap: var(--kernol-space-2) var(--kernol-space-3); padding: var(--kernol-space-3) var(--kernol-space-4); border-top: 1px solid var(--bo-border-soft); }
.bo-setting-foot .bo-ic { color: var(--bo-faint); flex: none; }
.bo-setting-foot .bo-xs { margin: 0; flex: 1; min-width: 0; }
.bo-link-sm { font-size: var(--bo-t-12); font-weight: 550; color: var(--bo-primary); white-space: nowrap; cursor: pointer; background: none; border: none; padding: 0; font-family: var(--bo-font); }
.bo-link-sm:hover { color: var(--bo-primary-700); }

.bo-kv { display: grid; grid-template-columns: 118px 1fr; row-gap: 7px; column-gap: 12px; font-size: var(--bo-t-12); align-items: baseline; margin: 0; }
.bo-kv dt { color: var(--bo-muted); font-weight: 500; }
.bo-kv dd { margin: 0; color: var(--bo-ink); font-weight: 500; min-width: 0; overflow-wrap: anywhere; }
.bo-kv dd.bo-quiet { color: var(--bo-faint); font-weight: 400; }

.bo-callout { display: flex; align-items: center; gap: 10px; border: 1px solid; border-radius: var(--bo-r); padding: 10px 14px; font-size: var(--bo-t-13); }
.bo-callout .bo-ic { flex: none; }
.bo-callout-txt { flex: 1; min-width: 0; }
.bo-callout-title { font-weight: 600; color: var(--bo-ink); }
.bo-callout-sub { font-size: var(--bo-t-12); color: var(--bo-muted); margin-top: 1px; }
.bo-callout--info { background: var(--bo-blue-50); border-color: var(--bo-blue-200); }
.bo-callout--info > .bo-ic { color: var(--bo-blue-600); }
.bo-callout--warn { background: var(--bo-amber-50); border-color: var(--bo-amber-200); }
.bo-callout--warn > .bo-ic { color: var(--bo-amber-600); }
.bo-callout--neg { background: var(--bo-red-50); border-color: var(--bo-red-200); }
.bo-callout--neg > .bo-ic { color: var(--bo-red-600); }
.bo-callout--pos { background: var(--bo-green-50); border-color: var(--bo-green-200); }
.bo-callout--pos > .bo-ic { color: var(--bo-green-600); }

/* ==========================================================================
   8. WORK QUEUES
   ========================================================================== */
.bo-queue { display: flex; flex-direction: column; }
.bo-qrow { display: flex; align-items: center; gap: 10px; padding: 8px 14px; border-bottom: 1px solid var(--bo-border-soft); min-height: var(--bo-row-h-tight); color: inherit; }
.bo-qrow:last-child { border-bottom: none; }
a.bo-qrow:hover, .bo-qrow--hover:hover { background: var(--bo-hover); }
.bo-qrow .bo-ic { color: var(--bo-muted); }
.bo-qrow-txt { flex: 1; min-width: 0; line-height: 1.35; }
.bo-qrow-title { font-size: var(--bo-t-13); font-weight: 550; color: var(--bo-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bo-qrow-sub { font-size: var(--bo-t-12); color: var(--bo-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bo-qrow-meta { flex: none; display: flex; align-items: center; gap: 10px; }
.bo-qrow .bo-go { color: var(--bo-faint); opacity: 0; }
.bo-qrow:hover .bo-go { opacity: 1; }
.bo-qdate { width: 46px; flex: none; font-weight: 600; color: var(--bo-ink); font-size: var(--bo-t-12); font-variant-numeric: tabular-nums; }

.bo-qhead { display: flex; align-items: center; gap: 7px; padding: 9px 14px 7px; font-size: var(--bo-t-11); font-weight: 650; text-transform: uppercase; letter-spacing: 0.06em; color: var(--bo-th-ink); }
.bo-qhead--neg { color: var(--bo-red-700); }
.bo-qhead--warn { color: var(--bo-amber-700); }
.bo-qhead-n { margin-left: auto; font-variant-numeric: tabular-nums; color: var(--bo-faint); text-transform: none; letter-spacing: 0; }

/* ==========================================================================
   9. METRICS
   ========================================================================== */
/* The headline counters. A tinted round tile carrying the resource's own icon,
   then the label, the number, and one line of context under it.

   The card itself stays white whatever the tone. An earlier version filled the
   whole card red when something was overdue, which meant a normal Monday
   produced a wall of red panels and the colour stopped meaning anything. Here
   the tone is spent on the tile and, when it is genuinely bad, on the one line
   of subtext that says why — so a single alarming tile in a row of five is
   visible from across the room. */
.bo-metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: var(--kernol-space-3); }
.bo-metric { padding: var(--kernol-space-4) var(--kernol-space-4); display: flex; align-items: flex-start; gap: var(--kernol-space-3); color: inherit; }
a.bo-metric:hover { border-color: var(--bo-border-hover); box-shadow: var(--bo-shadow-sm, var(--kernol-shadow-sm)); }
.bo-metric-tile { width: 42px; height: 42px; border-radius: var(--bo-r-full); display: inline-flex; align-items: center; justify-content: center; flex: none; background: var(--kernol-neutral-soft); color: var(--kernol-neutral-ink); }
.bo-metric-tile .bo-ic { width: 19px; height: 19px; }
.bo-metric-txt { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.bo-metric-label { font-size: var(--bo-t-13); font-weight: 600; color: var(--bo-ink-soft); }
.bo-metric-val { font-size: var(--bo-t-24); font-weight: 700; letter-spacing: var(--kernol-tracking-tight); color: var(--bo-ink); font-variant-numeric: tabular-nums; line-height: 1.15; }
.bo-metric-sub { font-size: var(--bo-t-12); color: var(--bo-muted); }

.bo-metric--info .bo-metric-tile { background: var(--kernol-info-soft); color: var(--kernol-info-ink); }
.bo-metric--pos .bo-metric-tile { background: var(--kernol-success-soft); color: var(--kernol-success-ink); }
.bo-metric--review .bo-metric-tile { background: var(--kernol-plan-soft); color: var(--kernol-plan-ink); }
.bo-metric--warn .bo-metric-tile { background: var(--kernol-warning-soft); color: var(--kernol-warning-ink); }
.bo-metric--alert .bo-metric-tile { background: var(--kernol-danger-soft); color: var(--kernol-danger-ink); }
.bo-metric--alert .bo-metric-sub { color: var(--bo-red-700); font-weight: 550; }

/* ==========================================================================
   10. MILESTONES & TASKS
   ========================================================================== */
.bo-mstone { overflow: hidden; }
.bo-mstone + .bo-mstone { margin-top: 10px; }
.bo-mstone--active .bo-mstone-head { background: var(--bo-selected); }
.bo-mstone-num { width: 22px; height: 22px; border-radius: var(--bo-r-full); display: inline-flex; align-items: center; justify-content: center; font-size: var(--bo-t-11); font-weight: 650; flex: none; background: var(--bo-navy-100); color: var(--bo-muted); border: 1.5px solid var(--bo-navy-200); }
.bo-mstone-num.is-done { background: var(--bo-green-600); border-color: var(--bo-green-600); color: #FFF; }
.bo-mstone-num.is-active { background: var(--bo-primary); border-color: var(--bo-primary); color: #FFF; }
.bo-mstone-title { font-size: var(--bo-t-13); font-weight: 600; color: var(--bo-ink); }
.bo-mstone-meta { font-size: var(--bo-t-11); color: var(--bo-muted); margin-top: 1px; }
.bo-mstone-right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.bo-mstone-tasks { border-top: 1px solid var(--bo-border-soft); }

.bo-task { display: flex; align-items: center; gap: 10px; padding: 7px 14px 7px 20px; border-bottom: 1px solid var(--bo-border-soft); min-height: 36px; }
.bo-task:last-child { border-bottom: none; }
.bo-task:hover { background: var(--bo-hover); }
.bo-task-state { flex: none; width: 16px; height: 16px; border-radius: var(--bo-r-full); border: 1.5px solid var(--bo-navy-300); display: inline-flex; align-items: center; justify-content: center; color: #FFF; }
.bo-task-state.is-done { background: var(--bo-green-600); border-color: var(--bo-green-600); }
.bo-task-state.is-review { background: var(--bo-violet-600); border-color: var(--bo-violet-600); }
.bo-task-state.is-blocked { background: #FFF; border-color: var(--bo-red-600); color: var(--bo-red-600); }
.bo-task-state.is-redo { background: #FFF; border-color: var(--bo-amber-500); color: var(--bo-amber-600); }
.bo-task-state .bo-ic { width: 10px; height: 10px; }
.bo-task-txt { flex: 1; min-width: 0; line-height: 1.3; }
.bo-task-title { font-size: var(--bo-t-13); font-weight: 500; color: var(--bo-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bo-task--done .bo-task-title { color: var(--bo-muted); text-decoration: line-through; text-decoration-color: var(--bo-navy-300); }
.bo-task-sub { font-size: var(--bo-t-11); color: var(--bo-muted); }
.bo-task-right { margin-left: auto; display: flex; align-items: center; gap: 10px; flex: none; }

.bo-owner { display: inline-flex; align-items: center; gap: 5px; font-size: var(--bo-t-11); font-weight: 600; color: var(--bo-muted); border: 1px solid var(--bo-border); background: var(--bo-navy-50); border-radius: var(--bo-r-xs); padding: 1px 6px; white-space: nowrap; }
.bo-owner .bo-ic { width: 11px; height: 11px; }
.bo-owner--client { color: var(--bo-amber-700); background: var(--bo-amber-50); border-color: var(--bo-amber-200); }
.bo-owner--agency { color: var(--bo-blue-700); background: var(--bo-blue-50); border-color: var(--bo-blue-200); }

/* The activity feed row. Both feeds — the agency-wide one and a record's own —
   are this row, so an entry reads the same whichever page it is on. It used to be
   Tailwind utilities and an inline border in two partials that had drifted apart. */
.bo-feed { margin: 0; padding: 0; list-style: none; }
.bo-feed-row { display: flex; align-items: flex-start; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--bo-border-soft); }
.bo-feed-row:last-child { border-bottom: none; }
.bo-feed--flush .bo-feed-row { padding: 8px 14px; }
.bo-feed-row .bo-dot { margin-top: 6px; }
.bo-feed-txt { min-width: 0; flex: 1; }
.bo-feed-summary { font-size: var(--bo-t-13); color: var(--bo-ink-soft); margin: 0; line-height: 1.4; }
.bo-feed-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; font-size: var(--bo-t-12); color: var(--bo-muted); margin: 2px 0 0; }

/* ==========================================================================
   11. ACTIVITY TIMELINE + COMPOSER
   ========================================================================== */
.bo-tl { display: flex; flex-direction: column; }
.bo-tl-item { display: flex; gap: 10px; position: relative; padding: 0 0 14px 0; }
.bo-tl-item::before { content: ''; position: absolute; left: 11px; top: 26px; bottom: 2px; width: 1.5px; background: var(--bo-navy-100); }
.bo-tl-item:last-child { padding-bottom: 0; }
.bo-tl-item:last-child::before { display: none; }
.bo-tl-dot { width: 23px; height: 23px; border-radius: var(--bo-r-full); background: var(--bo-navy-100); border: 1px solid var(--bo-border); display: inline-flex; align-items: center; justify-content: center; color: var(--bo-muted); flex: none; z-index: 1; }
.bo-tl-dot .bo-ic { width: 12px; height: 12px; }
.bo-tl-dot--pos { background: var(--bo-green-50); border-color: var(--bo-green-200); color: var(--bo-green-700); }
.bo-tl-dot--info { background: var(--bo-blue-50); border-color: var(--bo-blue-200); color: var(--bo-blue-700); }
.bo-tl-dot--warn { background: var(--bo-amber-50); border-color: var(--bo-amber-200); color: var(--bo-amber-700); }
.bo-tl-txt { min-width: 0; padding-top: 3px; font-size: var(--bo-t-12); color: var(--bo-ink-soft); line-height: 1.4; }
.bo-tl-when { color: var(--bo-faint); font-size: var(--bo-t-11); white-space: nowrap; }

.bo-composer { min-width: 0; }
.bo-composer textarea { width: 100%; border: none; outline: none; resize: none; font-family: var(--bo-font); font-size: var(--bo-t-13); color: var(--bo-ink); padding: 10px 12px 4px; background: transparent; min-height: 54px; }
.bo-composer textarea::placeholder { color: var(--bo-faint); }
.bo-composer-foot { display: flex; align-items: center; gap: 6px; padding: 6px 8px; }
.bo-composer:focus-within { border-color: var(--bo-blue-300); box-shadow: 0 0 0 3px var(--bo-blue-50); }

/* ==========================================================================
   12. FORMS
   ========================================================================== */
/* Controls are 38px so a form is comfortable to work down and every hit target
   clears the size a tired person can reliably click. Focus is a blue ring, on
   everything, always visible — the one place blue outranks the navy. */
.bo-field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.bo-label { font-size: var(--bo-t-12); font-weight: 620; color: var(--bo-ink-soft); }
.bo-label .bo-req { color: var(--bo-red-600); }
.bo-hint { font-size: var(--bo-t-12); color: var(--bo-faint); line-height: 1.4; }
.bo-input, .bo-select, .bo-textarea { font-family: var(--bo-font); font-size: var(--bo-t-13); color: var(--bo-ink); background: var(--kernol-surface); border: 1px solid var(--bo-border); border-radius: var(--bo-r-sm); height: 38px; padding: 0 12px; outline: none; width: 100%; transition: border-color 0.12s, box-shadow 0.12s; }
.bo-textarea { height: auto; min-height: 84px; padding: 10px 12px; resize: vertical; line-height: 1.5; }
.bo-input:hover, .bo-select:hover, .bo-textarea:hover { border-color: var(--bo-border-hover); }
.bo-input:focus, .bo-select:focus, .bo-textarea:focus { border-color: var(--kernol-blue-400); box-shadow: var(--kernol-focus-ring); }
.bo-input::placeholder, .bo-textarea::placeholder { color: var(--bo-faint); }
.bo-input[aria-invalid="true"], .bo-select[aria-invalid="true"], .bo-textarea[aria-invalid="true"],
.bo-input.is-invalid, .bo-select.is-invalid, .bo-textarea.is-invalid { border-color: var(--kernol-red-500); }
.bo-select { appearance: none; background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%237E899B' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 10px center; padding-right: 32px; }
.bo-input--sm, .bo-select--sm { height: 30px; font-size: var(--bo-t-12); padding-inline: 9px; }
.bo-formrow { display: grid; gap: var(--kernol-space-3); }
.bo-formrow--2 { grid-template-columns: 1fr 1fr; }
.bo-formrow--3 { grid-template-columns: 1fr 1fr 1fr; }
.bo-formrow--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 1180px) { .bo-formrow--4 { grid-template-columns: 1fr 1fr; } }

/* The staff "New lead" form: three cards down one reading column, the pet rows
   stacked inside the last. A row is a sunk panel so its own fields read as one
   animal, and the remove button lives in its head. */
.bo-newlead { display: flex; flex-direction: column; gap: var(--kernol-space-4); max-width: 880px; }
.bo-newlead-pets { display: flex; flex-direction: column; gap: var(--kernol-space-3); }
.bo-newlead-pet { padding: 12px 14px 14px; border: 1px solid var(--bo-border-soft); border-radius: var(--bo-r); background: var(--kernol-surface-secondary); }
.bo-newlead-pet__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
@media (max-width: 900px) { .bo-formrow--2, .bo-formrow--3 { grid-template-columns: 1fr; } }
.bo-checkbox { width: 16px; height: 16px; accent-color: var(--kernol-accent); border-radius: var(--bo-r-xs); }

/* A named group of fields inside a long form. Thirty inputs in one column is
   not a form, it is a wall; this is what breaks it into things a person can
   answer one at a time. */
/* A field that holds a number or a code rather than a sentence, and should not
   stretch to the width of the ones around it. */
.bo-field--narrow { max-width: 180px; }
/* A button_to form sitting in a row of links rather than on a line of its own. */
.bo-inline { display: inline-block; margin: 0; }

.bo-fieldset { display: flex; flex-direction: column; gap: var(--kernol-space-3); }
.bo-fieldset + .bo-fieldset { margin-top: var(--kernol-space-5); padding-top: var(--kernol-space-5); border-top: 1px solid var(--bo-border-soft); }
.bo-fieldset-head { display: flex; align-items: baseline; gap: var(--kernol-space-2); }
.bo-fieldset-title { font-size: var(--bo-t-14); font-weight: 650; color: var(--bo-ink); }
.bo-fieldset-sub { font-size: var(--bo-t-12); color: var(--bo-muted); }

.bo-errors { border: 1px solid var(--bo-red-200); background: var(--bo-red-50); color: var(--bo-red-700); border-radius: var(--bo-r-sm); padding: var(--kernol-space-3) var(--kernol-space-4); font-size: var(--bo-t-13); }
.bo-errors ul { margin: 5px 0 0; padding-left: 18px; }
/* The message under one field that failed, as opposed to the summary above the
   form. Carries an icon as well as the colour. */
.bo-field-error { display: flex; align-items: center; gap: 5px; font-size: var(--bo-t-12); font-weight: 550; color: var(--bo-red-700); }
.bo-field-error .bo-ic { width: 13px; height: 13px; flex: none; }

/* ONE segmented control, in two flavours: .bo-seg wraps links or buttons,
   .bo-segments wraps radios and keeps them in the accessibility tree. Same
   ground, same padding, same thumb — only the markup underneath differs.
   It keeps the width of its own buttons: inline-flex shrink-wraps in normal
   flow, but a grid or column-flex parent stretches it anyway — which is how the
   composer's two-word Portal/Email switch ended up spanning the thread pane.
   Both axes are pinned because the two parents disagree about which one is
   "across": align-self is the block axis in a grid and the cross axis in a
   column flex, justify-self the inline axis in a grid and ignored in flex. */
.bo-seg,
.bo-segments { display: inline-flex; align-self: flex-start; justify-self: start; gap: 2px; padding: 2px; border: 1px solid var(--bo-border); border-radius: var(--bo-r-sm); background: var(--bo-bg-sunken); }
.bo-seg-item,
.bo-segment span { display: inline-flex; align-items: center; padding: 4px 12px; border-radius: var(--bo-r-xs); font-size: var(--bo-t-12); font-weight: 550; color: var(--bo-muted); cursor: pointer; white-space: nowrap; }
.bo-seg-item:hover, .bo-segment span:hover { color: var(--bo-ink); }
.bo-seg-item.is-active,
.bo-segment input:checked + span { background: #FFFFFF; color: var(--bo-ink); box-shadow: var(--bo-shadow-card); }
/* The radio stays in the accessibility tree and is only hidden visually. */
.bo-segment { display: inline-flex; align-items: center; }
.bo-segment input { position: absolute; opacity: 0; width: 0; height: 0; }
.bo-segment input:focus-visible + span { outline: 2px solid var(--bo-blue-300); outline-offset: 1px; }

/* ==========================================================================
   13. MENUS, DRAWERS, MODALS, EMPTY STATES
   ========================================================================== */
.bo-menu { background: var(--kernol-surface); border: 1px solid var(--bo-border); border-radius: var(--bo-r); box-shadow: var(--bo-shadow-pop); padding: var(--kernol-space-1); min-width: 200px; }
.bo-menu-label { font-size: var(--bo-t-11); font-weight: 650; text-transform: uppercase; letter-spacing: var(--bo-track-eyebrow); color: var(--bo-faint); padding: 6px 10px 4px; }
.bo-menu-item { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: var(--bo-r-sm); font-size: var(--bo-t-13); color: var(--bo-ink-soft); cursor: pointer; white-space: nowrap; width: 100%; background: none; border: none; font-family: var(--bo-font); text-align: left; }
.bo-menu-item .bo-ic { color: var(--bo-faint); }
.bo-menu-item:hover { background: var(--bo-hover-strong); color: var(--bo-ink); }
.bo-menu-item:hover .bo-ic { color: var(--bo-muted); }
.bo-menu-item:focus-visible { outline: none; background: var(--bo-hover-strong); box-shadow: var(--kernol-focus-ring); }
/* Destructive items are separated as well as coloured: a red word next to five
   black ones is still one wrong click away from the wrong record. */
.bo-menu-item--danger { color: var(--bo-red-700); }
.bo-menu-item--danger .bo-ic, .bo-menu-item--danger:hover .bo-ic { color: var(--bo-red-600); }
.bo-menu-item--danger:hover { background: var(--bo-red-50); color: var(--bo-red-700); }
.bo-menu-sep { height: 1px; background: var(--bo-border-soft); margin: var(--kernol-space-1) 0; }

.bo-scrim { position: fixed; inset: 0; background: rgb(6 18 13 / 0.48); z-index: 70; }

/* An empty state says what would be here and how it gets here — never just
   "no results". The mascot is available but rationed: it belongs on the one or
   two screens a person meets on their first day, not on every filter that
   happens to match nothing. */
.bo-empty { border: 1.5px dashed var(--bo-border-strong); border-radius: var(--bo-r); padding: var(--kernol-space-8) var(--kernol-space-5); display: flex; flex-direction: column; align-items: center; gap: var(--kernol-space-2); text-align: center; background: var(--kernol-surface); }
.bo-empty .bo-ic-xl { color: var(--kernol-grey-500); }
.bo-empty-title { font-size: var(--bo-t-14); font-weight: 620; color: var(--bo-ink); }
.bo-empty-sub { font-size: var(--bo-t-13); color: var(--bo-muted); max-width: 380px; line-height: 1.5; }
.bo-empty-actions { display: flex; align-items: center; gap: var(--kernol-space-2); margin-top: var(--kernol-space-2); }
.bo-empty-mascot { width: 96px; height: auto; opacity: 0.95; margin-bottom: 2px; }
.bo-empty--plain { border: none; background: transparent; }

.bo-confirm-well { background: var(--bo-navy-50); border: 1px solid var(--bo-border); border-radius: var(--bo-r-sm); padding: 10px 12px; font-size: var(--bo-t-12); color: var(--bo-ink-soft); }

/* ==========================================================================
   14. COMMAND PALETTE (⌘K)
   ========================================================================== */
.bo-cmdk { position: fixed; top: 96px; left: 50%; transform: translateX(-50%); width: 620px; max-width: calc(100vw - 48px); background: #FFFFFF; border-radius: 10px; box-shadow: var(--bo-shadow-modal); z-index: 90; overflow: hidden; border: 1px solid var(--bo-border); }
.bo-cmdk-input { display: flex; align-items: center; gap: 10px; padding: 13px 16px; border-bottom: 1px solid var(--bo-border-soft); }
.bo-cmdk-input input { flex: 1; border: none; outline: none; font-family: var(--bo-font); font-size: var(--bo-t-14); color: var(--bo-ink); background: transparent; }
.bo-cmdk-input input::placeholder { color: var(--bo-faint); }
.bo-cmdk-list { max-height: 380px; overflow-y: auto; padding: 6px; }
.bo-cmdk-group { font-size: var(--bo-t-11); font-weight: 650; text-transform: uppercase; letter-spacing: 0.06em; color: var(--bo-faint); padding: 8px 10px 4px; }
.bo-cmdk-item { display: flex; align-items: center; gap: 10px; padding: 7px 10px; border-radius: var(--bo-r-sm); cursor: pointer; font-size: var(--bo-t-13); color: var(--bo-ink-soft); }
.bo-cmdk-item:hover, .bo-cmdk-item.is-active { background: var(--bo-hover-strong); color: var(--bo-ink); }
.bo-cmdk-item .bo-ic { color: var(--bo-muted); }
.bo-cmdk-meta { margin-left: auto; display: flex; align-items: center; gap: 8px; color: var(--bo-faint); font-size: var(--bo-t-11); }
.bo-cmdk-foot { border-top: 1px solid var(--bo-border-soft); padding: 7px 14px; display: flex; gap: 14px; font-size: var(--bo-t-11); color: var(--bo-faint); background: var(--bo-navy-25); }
.bo-cmdk-foot .bo-kbd { margin-left: 0; }

/* ==========================================================================
   15. QUOTATION BUILDER
   ========================================================================== */
.bo-lineitems { overflow: hidden; }
.bo-li-head { display: grid; grid-template-columns: 1fr 80px 105px 100px 130px; gap: 8px; align-items: center; padding: 7px 14px; }
.bo-li { display: grid; grid-template-columns: 1fr 80px 105px 100px 130px; gap: 8px; align-items: center; padding: 8px 14px; border-bottom: 1px solid var(--bo-border-soft); background: #FFF; }
.bo-li:hover { background: var(--bo-hover); }
.bo-li-name { font-weight: 550; color: var(--bo-ink); font-size: var(--bo-t-13); }
.bo-li-desc { font-size: var(--bo-t-12); color: var(--bo-muted); margin-top: 1px; }
.bo-li--optional { background: var(--bo-bg-sunken); }
.bo-li--optional .bo-li-name { color: var(--bo-ink-soft); }
.bo-li-add { display: flex; align-items: center; gap: 14px; padding: 9px 14px; flex-wrap: wrap; }
/* An editable line needs its own <form>; display:contents lets the form's
   fields sit directly in the .bo-li grid cells. */
.bo-li-form { display: contents; }
/* The remove button_to inside a line row is also a form. */
.bo-li > form.button_to { justify-self: end; }

/* The quotation template's line editor: the builder's row with one more column,
   because a template line has two prices — the catalog's, which every template
   follows by default, and the override this one charges instead. */
.bo-li-head--tpl,
.bo-li--tpl { grid-template-columns: minmax(0, 1fr) 52px 128px 128px 96px 112px; }

.bo-totals { display: flex; flex-direction: column; gap: 6px; font-size: var(--bo-t-13); }
.bo-totals-row { display: flex; justify-content: space-between; color: var(--bo-ink-soft); }
.bo-totals-row .bo-num { color: var(--bo-ink); }
.bo-totals-row--sub { color: var(--bo-muted); font-size: var(--bo-t-12); }
.bo-totals-row--sub .bo-num { color: var(--bo-muted); }
.bo-totals-row--grand { border-top: 1px solid var(--bo-border); padding-top: 8px; margin-top: 2px; font-size: var(--bo-t-14); font-weight: 650; color: var(--bo-ink); }
.bo-totals-row--grand .bo-num { font-size: var(--bo-t-16); }

/* ==========================================================================
   16. TEMPLATE TREE
   ========================================================================== */
.bo-tree { display: flex; flex-direction: column; gap: 10px; }
.bo-tnode { overflow: hidden; }
.bo-tnode-title { font-size: var(--bo-t-13); font-weight: 600; color: var(--bo-ink); }
.bo-tleaf { display: flex; align-items: center; gap: 9px; padding: 7px 12px 7px 34px; border-bottom: 1px solid var(--bo-border-soft); background: #FFF; }
.bo-tleaf:last-child { border-bottom: none; }
.bo-tleaf:hover { background: var(--bo-hover); }
.bo-tleaf-title { font-size: var(--bo-t-13); font-weight: 500; color: var(--bo-ink); flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bo-tleaf-add { padding: 7px 12px 7px 34px; }

.bo-typeicon { width: 24px; height: 24px; border-radius: var(--bo-r-sm); display: inline-flex; align-items: center; justify-content: center; flex: none; background: var(--bo-navy-100); color: var(--bo-navy-500); }
.bo-typeicon .bo-ic { width: 13px; height: 13px; }
.bo-typeicon--doc { background: var(--bo-blue-50); color: var(--bo-blue-700); }
.bo-typeicon--form { background: var(--bo-violet-50); color: var(--bo-violet-700); }
.bo-typeicon--action { background: var(--bo-amber-50); color: var(--bo-amber-700); }

.bo-chiptag { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 550; color: var(--bo-muted); background: var(--bo-navy-100); border-radius: 4px; padding: 1px 6px; white-space: nowrap; }
.bo-chiptag .bo-ic { width: 11px; height: 11px; }
.bo-code { font-family: ui-monospace, Menlo, monospace; font-size: 11px; color: var(--bo-muted); background: var(--bo-navy-100); border-radius: 4px; padding: 1px 5px; }
/* Monospace for a whole table cell. .bo-code is a chip with a ground of its own;
   put on a <td> it tints the entire cell, which is how the DNS records ended up
   with two columns washed in the header colour. */
.bo-td-code { font-family: ui-monospace, Menlo, monospace; font-size: 11px; color: var(--bo-ink-soft); overflow-wrap: anywhere; }

/* A public reference (JX3D5WQ) — see BackofficeHelper#bo_ref. Monospace and tabular
   so a column of them lines up; no ground of its own, so it sits in a cell or a page
   header without shouting. The copy control appears on hover and on keyboard focus. */
.bo-ref { display: inline-flex; align-items: center; gap: 3px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; font-weight: 600; letter-spacing: 0.04em; font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--bo-ink); }
.bo-ref .bo-ref-txt { color: inherit; text-decoration: none; }
.bo-ref a.bo-ref-txt:hover { text-decoration: underline; }
.bo-ref--sub { font-size: 11px; font-weight: 500; color: var(--bo-muted); }
.bo-ref--lg { font-size: 14px; }
.bo-ref--h1 { font-size: inherit; font-weight: inherit; letter-spacing: 0.02em; }
.bo-ref--h1 .bo-ref-copy .bo-ic { width: 15px; height: 15px; }
.bo-ref-copy { appearance: none; border: 0; background: transparent; padding: 2px; margin: -2px 0; border-radius: 4px; color: var(--bo-muted); cursor: pointer; line-height: 0; opacity: 0; transition: opacity 0.12s, color 0.12s; }
.bo-ref-copy .bo-ic { width: 12px; height: 12px; }
.bo-ref:hover .bo-ref-copy, .bo-ref-copy:focus-visible, .bo-ref-copy.is-copied { opacity: 1; }
.bo-ref-copy:hover { color: var(--bo-ink); background: var(--bo-navy-100); }
.bo-ref-copy.is-copied { color: var(--bo-green-700); }
.bo-table .bo-ref { font-size: 11.5px; }

/* ==========================================================================
   17. MISC LAYOUT HELPERS
   ========================================================================== */
.bo-divider { height: 1px; background: var(--bo-border-soft); border: none; margin: 0; }
.bo-grid { display: grid; gap: 12px; }
.bo-grid--2 { grid-template-columns: 1fr 1fr; }
.bo-grid--3 { grid-template-columns: 1fr 1fr 1fr; }
.bo-grid--main-side { grid-template-columns: minmax(0, 5fr) minmax(300px, 3fr); gap: 14px; align-items: start; }
@media (max-width: 1280px) { .bo-grid--3 { grid-template-columns: 1fr 1fr; } .bo-grid--main-side { grid-template-columns: 1fr; } }
/* Two-up cards hold real content — a table, a schedule — not just a sentence, so
   they give up the second column well before the viewport gets tight. */
@media (max-width: 1180px) { .bo-grid--2 { grid-template-columns: 1fr; } }
.bo-row { display: flex; align-items: center; gap: var(--kernol-space-2); min-width: 0; }
.bo-row--tight { gap: 6px; }
.bo-col { display: flex; flex-direction: column; gap: var(--kernol-space-2); min-width: 0; }
.bo-col-inline { display: inline-flex; flex-direction: column; min-width: 0; }
.bo-stack { display: flex; flex-direction: column; gap: var(--kernol-space-3); min-width: 0; }
.bo-grow { flex: 1; min-width: 0; }
.bo-wrap { flex-wrap: wrap; }
.bo-between { justify-content: space-between; }
.bo-end { justify-content: flex-end; }

/* ── Spacing utilities ─────────────────────────────────────────────────────
   The templates carried 460 inline `style` attributes, most of them a margin —
   and between them seven different values of margin-top where the system
   defines four steps. These are those, snapped to the 4px scale. They are
   deliberately few: a utility for every property is a second design system
   competing with the first.

   `!important` on .bo-m0 alone, because it is used to cancel a margin a
   component rule has already set (a <p> inside a card, a <dl> inside a rail),
   which is the one case a utility has to outrank a component. */
.bo-m0    { margin: 0 !important; }
.bo-mt-1  { margin-top: var(--kernol-space-1); }
.bo-mt-2  { margin-top: var(--kernol-space-2); }
.bo-mt-3  { margin-top: var(--kernol-space-3); }
.bo-mt-4  { margin-top: var(--kernol-space-4); }
.bo-mt-5  { margin-top: var(--kernol-space-5); }
.bo-mb-2  { margin-bottom: var(--kernol-space-2); }
.bo-mb-3  { margin-bottom: var(--kernol-space-3); }
.bo-mb-4  { margin-bottom: var(--kernol-space-4); }
.bo-my-3  { margin-block: var(--kernol-space-3); }
.bo-push  { margin-left: auto; }
.bo-right { text-align: right; }
.bo-center{ text-align: center; }
.bo-top   { vertical-align: top; }
.bo-full  { width: 100%; }
.bo-min0  { min-width: 0; }
.bo-span  { grid-column: 1 / -1; }
.bo-span-2{ grid-column: 2 / -1; }
.bo-clickable { cursor: pointer; }
.bo-prewrap { white-space: pre-wrap; }
.bo-plainlist { margin: 0; padding: 0; list-style: none; }
.bo-indent { padding-left: 18px; }
.bo-muted-t { color: var(--bo-muted); }
.bo-error-t { color: var(--bo-red-700); }

/* ── Reading measures ──────────────────────────────────────────────────────
   A form is read down a column, not across a 1920px screen. Three widths,
   because the templates used six. */
.bo-measure         { max-width: 660px; }
.bo-measure--wide   { max-width: 760px; }
.bo-measure--xwide  { max-width: 920px; }

/* Flash messages inside the agency shell */
.bo-flash { margin: 12px var(--bo-page-x) 0; }
.bo-flash p { border-radius: var(--bo-r-sm); border: 1px solid; padding: 8px 12px; font-size: var(--bo-t-12); font-weight: 550; margin: 0 0 8px; }
.bo-flash .bo-flash--notice { background: var(--bo-green-50); border-color: var(--bo-green-200); color: var(--bo-green-700); }
.bo-flash .bo-flash--alert { background: var(--bo-red-50); border-color: var(--bo-red-200); color: var(--bo-red-700); }

/* ==========================================================================
   18. MESSAGERIE
   The threads on the left, the open one on the right. Two panes that scroll
   independently, because reading a long thread must not lose the list — and
   because a chat that scrolls the page ends up with its composer offscreen.
   ========================================================================== */
/* The last link of the .bo-app--fixed chain: `flex: 1` takes what the tabs above
   leave, `minmax(0, 1fr)` hands exactly that on to the two panes. */
.bo-messagerie { display: grid; grid-template-columns: 340px minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); flex: 1; min-height: 0; border-top: 1px solid var(--bo-border); margin-top: 10px; }
@media (min-width: 1600px) { .bo-messagerie { grid-template-columns: 380px minmax(0, 1fr); } }

/* No page header above the Messagerie, so the filters carry the top spacing. They
   keep their own height rather than being squeezed to give the panes room. */
.bo-viewtabs--top { overflow-y: hidden; flex: none; }

.bo-msg-list { display: flex; flex-direction: column; min-width: 0; min-height: 0; border-right: 1px solid var(--bo-border); background: var(--bo-bg-rail); }
.bo-msg-listhead { flex: none; display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-bottom: 1px solid var(--bo-border); }
.bo-msg-search { flex: 1; min-width: 0; display: flex; align-items: center; gap: 7px; color: var(--bo-muted); }
.bo-msg-new { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: var(--bo-r-sm); background: var(--bo-primary); color: #FFFFFF; }
.bo-msg-new:hover { background: var(--bo-primary-700); color: #FFFFFF; }
.bo-msg-new .bo-ic { width: 15px; height: 15px; }
.bo-msg-search input { flex: 1; min-width: 0; height: 30px; padding: 0 10px; border: 1px solid var(--bo-border); border-radius: var(--bo-r-sm); background: #FFFFFF; font-family: var(--bo-font); font-size: var(--bo-t-13); color: var(--bo-ink); }
.bo-msg-search input:focus { outline: none; border-color: var(--bo-blue-300); box-shadow: 0 0 0 3px var(--bo-blue-50); }
/* The filters sit inside the pane rather than across the page, so they read as
   the list's own controls. */
.bo-viewtabs--stacked { margin-top: 0; padding: 0 6px; border-bottom: none; }
.bo-viewtabs--stacked .bo-viewtab { padding: 6px 8px; font-size: var(--bo-t-12); }

.bo-msg-rows { scrollbar-width: thin; flex: 1; min-height: 0; overflow-y: auto; }
.bo-msg-row { align-items: flex-start; gap: 10px; padding: 10px 12px; background: #FFFFFF; }
.bo-msg-row.is-active { background: var(--bo-selected); }
.bo-msg-row .bo-qrow-title { font-weight: 550; }
.bo-msg-row.is-unread .bo-qrow-title, .bo-msg-row.is-unread .bo-msg-row-subject { font-weight: 700; color: var(--bo-ink); }
.bo-msg-row-subject { font-size: var(--bo-t-12); color: var(--bo-ink-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bo-msg-row-meta { flex-direction: column; align-items: flex-end; gap: 6px; }
.bo-msg-dot { width: 8px; height: 8px; border-radius: var(--bo-r-full); background: var(--bo-primary); display: block; }
.bo-msg-dot--muted { background: var(--bo-amber-500); }

.bo-msg-thread { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.bo-msg-threadhead { flex: none; display: flex; align-items: flex-start; gap: 12px; padding: 12px var(--bo-page-x); border-bottom: 1px solid var(--bo-border); }
.bo-msg-threadhead-txt { min-width: 0; flex: 1; }
.bo-msg-threadhead-actions { flex: none; display: flex; gap: 8px; }

/* Reversed so the scroll origin is the bottom edge: a thread opens at its newest
   message because that is where the box starts, not because something scrolled it
   there afterwards. Which matters most while the page is still arriving — Trix
   growing the composer below, Inter reflowing every bubble — since each of those
   used to leave a scrolled thread short of the end, and a single measurement taken
   on the first frame could not know it. The messages keep their ordinary order
   inside .bo-msg-stream-inner: that wrapper is the only flex item here, so it is
   the only thing the reversal touches, and reading order is untouched.

   A reversed column packs toward the bottom, which would leave a two-message thread
   floating above the composer under a screen of nothing. The wrapper is given the
   full height instead, so a short conversation still begins at the top where it
   always did. Not `justify-content: flex-end`, which reads as the same thing and is
   not: once the conversation is taller than the box, justify-content decides which
   edge the overflow spills past, and the newest message ends up on the unreachable
   side of it. */
.bo-msg-stream { flex: 1; min-height: 0; overflow-y: auto; padding: 16px var(--bo-page-x); display: flex; flex-direction: column-reverse; }
/* `flex-shrink: 0` is not decoration. A flex item's automatic minimum size is what
   normally stops it being squashed below its content, and `min-height` replaces that
   — so on its own the 100% becomes a ceiling as well as a floor, and a long thread is
   compressed into one screen with the rest of it clipped. */
.bo-msg-stream-inner { min-height: 100%; flex-shrink: 0; display: flex; flex-direction: column; gap: 10px; }

/* The thread is drawn with its scrollbar showing rather than with an overlay one that
   fades out when it stops moving. In a two-pane Messagerie the bar is the only thing
   saying how much history sits above the message you are reading, and where you are
   in it — and with the pane opening at the newest message, the thumb parked at the
   bottom is what tells you there is anything above at all. Quiet enough to sit next
   to the conversation: a thin thumb on no track. Chrome and Firefox take the standard
   properties; the -webkit- rules are Safari's, which has nothing else. */
.bo-msg-stream { scrollbar-width: thin; scrollbar-color: var(--bo-navy-300) transparent; }
.bo-msg-stream::-webkit-scrollbar { width: 10px; }
.bo-msg-stream::-webkit-scrollbar-track { background: transparent; }
/* The transparent border with `background-clip: padding-box` is what makes the thumb
   narrower than the track it must still be easy to grab. */
.bo-msg-stream::-webkit-scrollbar-thumb { background: var(--bo-navy-300); border-radius: var(--bo-r-full); border: 3px solid transparent; background-clip: padding-box; }
.bo-msg-stream:hover { scrollbar-color: var(--bo-muted) transparent; }
.bo-msg-stream::-webkit-scrollbar-thumb:hover { background: var(--bo-muted); background-clip: padding-box; }
.bo-msg-day { display: flex; align-items: center; justify-content: center; margin: 6px 0; }
.bo-msg-day span { font-size: var(--bo-t-10); font-weight: 650; text-transform: uppercase; letter-spacing: 0.06em; color: var(--bo-muted); background: var(--bo-navy-100); border-radius: var(--bo-r-full); padding: 2px 10px; }

/* Theirs left, ours right: the shape of the conversation is readable before a
   word of it is. */
.bo-msg-bubblerow { display: flex; flex-direction: column; max-width: 74%; }
.bo-msg-bubblerow.is-them { align-self: flex-start; align-items: flex-start; }
.bo-msg-bubblerow.is-us { align-self: flex-end; align-items: flex-end; }
.bo-msg-bubble { border: 1px solid var(--bo-border); border-radius: var(--bo-r); padding: 8px 12px 10px; background: #FFFFFF; min-width: 0; }
.bo-msg-bubblerow.is-them .bo-msg-bubble { background: var(--bo-navy-50); border-bottom-left-radius: var(--bo-r-xs); }
.bo-msg-bubblerow.is-us .bo-msg-bubble { border-bottom-right-radius: var(--bo-r-xs); }
.bo-msg-bubble-head { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
.bo-msg-files { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 0; padding: 0; list-style: none; }
.bo-msg-bubble-meta { display: flex; align-items: center; gap: 8px; margin-top: 3px; font-size: var(--bo-t-10); color: var(--bo-muted); }
.bo-msg-failed { display: flex; align-items: center; gap: 8px; margin-top: 4px; font-size: var(--bo-t-12); color: var(--bo-red-700); }

.bo-msg-composer { flex: none; border-top: 1px solid var(--bo-border); padding: 8px var(--bo-page-x); background: #FFFFFF; }
.bo-msg-composer form { padding: 0 !important; }
.bo-msg-composer .bo-field { gap: 4px; }

/* The rich composer. Trix ships its own toolbar and editor styling; this only makes
   it sit inside the bordered box the plain composer already used, and keeps it short
   — a chat composer that opens four lines tall pushes the conversation off screen. */
.bo-composer--rich trix-toolbar { padding: 4px 6px 0; }
.bo-composer--rich trix-toolbar .trix-button-row { gap: 2px; }
.bo-composer--rich trix-toolbar .trix-button-group { margin-bottom: 4px; border-radius: var(--bo-r-xs); }
/* Trix's own attach button is removed, and the editor refuses files besides — see
   the no-trix-uploads controller. A file embedded in the body would be served from a
   signed Active Storage URL, and a signed blob id is not a tenant boundary; the
   composer's own upload is, because it is read back through the scoped controller. */
.bo-composer--rich trix-toolbar .trix-button-group--file-tools { display: none; }
.bo-composer--rich trix-editor { border: none; padding: 4px 10px 8px; min-height: 40px; max-height: 180px; overflow-y: auto; font-family: var(--bo-font); font-size: var(--bo-t-13); color: var(--bo-ink); }
.bo-composer--rich trix-editor:empty:not(:focus)::before { color: var(--bo-faint); }
.bo-composer--rich trix-editor ul { list-style: disc; padding-left: 18px; }
.bo-composer--rich trix-editor ol { list-style: decimal; padding-left: 22px; }

/* Formatting inside a message bubble. Action Text's sanitizer decides what survives;
   this gives what does the spacing a message needs. */
.bo-richtext { margin-top: 8px; font-size: var(--bo-t-13); color: var(--bo-ink); }
.bo-richtext > * + * { margin-top: 6px; }
.bo-richtext ul { list-style: disc; padding-left: 18px; }
.bo-richtext ol { list-style: decimal; padding-left: 22px; }
.bo-richtext a { text-decoration: underline; }
.bo-richtext blockquote { border-left: 2px solid var(--bo-border); padding-left: 8px; color: var(--bo-ink-soft); }

/* Below the CRM's desktop breakpoint the two panes stop competing: the list is
   the page, and opening a thread is a page of its own. The state comes from the
   server (`is-threading` when a conversation is open) rather than from a sibling
   selector, which cannot look backwards to the list. */
@media (max-width: 1024px) {
  .bo-messagerie { grid-template-columns: minmax(0, 1fr); }
  .bo-msg-list, .bo-msg-thread { border-right: none; }
  .bo-messagerie.is-threading .bo-msg-list { display: none; }
  .bo-messagerie:not(.is-threading) .bo-msg-thread { display: none; }
  /* The only way back to the list once the list is not on screen. */
  .bo-msg-back { display: inline-flex; }
}
.bo-msg-back { display: none; }

/* Roles & permissions. A long checklist read top to bottom, so the row is a label
   with the description under the name rather than beside it — a permission is only
   safe to grant if what it means is legible at a glance. */
.bo-permgroup { border-bottom: 1px solid var(--bo-border-soft); padding: 10px 0 12px; }
.bo-permgroup:last-child { border-bottom: none; padding-bottom: 2px; }
.bo-permgroup-head { font-size: var(--bo-t-11); font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--bo-muted); margin: 0 0 4px 8px; }
.bo-permrow { display: flex; align-items: flex-start; gap: 10px; padding: 5px 8px; border-radius: var(--bo-r-xs); cursor: pointer; }
.bo-permrow:hover { background: var(--bo-hover); }
.bo-permrow input { margin-top: 2px; flex: none; }
.bo-permrow-txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.bo-permrow-name { display: flex; align-items: center; gap: 6px; font-size: var(--bo-t-13); font-weight: 500; color: var(--bo-ink); }
.bo-permrow-desc { font-size: var(--bo-t-12); color: var(--bo-muted); }
/* Read-only rendering of the same list, for a role nobody may edit. */
.bo-permrow--static { cursor: default; }
.bo-permrow--static:hover { background: none; }

/* ==========================================================================
   JOURNEY RECORD SHELF
   Everything a journey's workflow has produced, tabbed by record type. Rows are
   scanned, not read: identity on the first line, the facts that tell two
   records of a type apart on the second, provenance and files under that.
   ========================================================================== */
.bo-rec-tabs { overflow-y: hidden; padding: 0 10px; }
.bo-rec-row { align-items: flex-start; padding: 10px 14px; }
.bo-rec-row .bo-typeicon { margin-top: 1px; }
.bo-rec-meta { align-self: center; }
/* Provenance — which task in the plan put this record here. */
.bo-rec-from { display: flex; align-items: center; gap: 4px; font-size: var(--bo-t-12); color: var(--bo-faint); margin-top: 1px; }
.bo-rec-from .bo-ic { flex: none; }
.bo-rec-from a { color: var(--bo-muted); }
.bo-rec-from a:hover { color: var(--bo-ink); text-decoration: underline; }
/* Files inline on the row: the point of the shelf is finding a document without
   opening anything first. */
.bo-rec-files { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.bo-rec-file { display: inline-flex; align-items: center; gap: 5px; max-width: 100%; padding: 2px 7px; border-radius: var(--bo-r-xs); background: var(--bo-blue-50); color: var(--bo-blue-700); font-size: 11px; font-weight: 550; }
.bo-rec-file:hover { background: var(--bo-blue-100); }
.bo-rec-file .bo-ic { flex: none; width: 11px; height: 11px; }
.bo-rec-file-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bo-rec-file-size { color: var(--bo-blue-600); font-weight: 450; opacity: .8; }
/* Category headings inside the documents tab — <summary> elements, closed until
   somebody asks for the pile behind them. */
.bo-rec-cat + .bo-rec-cat { border-top: 1px solid var(--bo-border-soft); }
.bo-rec-group { display: flex; align-items: center; gap: 8px; padding: 7px 14px 6px; cursor: pointer; user-select: none; list-style: none; }
.bo-rec-group::-webkit-details-marker { display: none; }
.bo-rec-group:hover { background: var(--bo-navy-200); color: var(--bo-ink); }
.bo-rec-group:focus-visible { outline: 2px solid var(--bo-blue-300); outline-offset: -2px; }
.bo-rec-caret { color: var(--bo-faint); transition: transform .12s ease; }
.bo-rec-cat[open] > .bo-rec-group .bo-rec-caret { transform: rotate(90deg); }
/* How much is behind a closed heading, so the shelf reads without opening it. */
.bo-rec-cat-files { margin-left: auto; font-size: var(--bo-t-11); font-weight: 500; letter-spacing: 0; text-transform: none; color: var(--bo-faint); }

/* ── Notification bell ─────────────────────────────────────────────────────
   Anchored to the topbar rather than floated over the page: this is a peek at a
   queue, not a dialog, and it must not take the page away from somebody who
   opened it by accident. Everything in it is a link that goes somewhere. */
.bo-bell { position: relative; }
.bo-bell-btn { position: relative; }
/* The count sits on the icon rather than beside it: the topbar is the one place in
   this layout where horizontal space is genuinely scarce. */
.bo-bell-count { position: absolute; top: -2px; right: -2px; min-width: 17px; height: 17px; padding: 0 4px; border-radius: var(--bo-r-full); background: var(--kernol-tan-600); color: var(--kernol-navy-900); font-size: var(--bo-t-10); font-weight: 700; line-height: 17px; text-align: center; border: 2px solid var(--bo-bg-shell); }
.bo-bell-panel { position: absolute; top: calc(100% + 6px); right: 0; width: 380px; max-height: 480px; display: flex; flex-direction: column; background: var(--kernol-surface); border: 1px solid var(--bo-border); border-radius: var(--bo-r); box-shadow: var(--bo-shadow-pop); z-index: 60; overflow: hidden; }
.bo-bell-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 9px 12px; border-bottom: 1px solid var(--bo-border-soft); font-size: var(--bo-t-13); }
.bo-bell-markall { margin: 0; }
.bo-bell-rows { overflow-y: auto; }
.bo-bell-foot { padding: 8px 12px; border-top: 1px solid var(--bo-border-soft); background: var(--bo-bg-shell); text-align: center; }

/* A notification row is a button posting a read receipt, so it has to be stripped
   back to looking like the queue row it sits in. */
.bo-notif-form { margin: 0; display: block; }
.bo-notif { width: 100%; border: none; background: transparent; text-align: left; font: inherit; color: inherit; cursor: pointer; align-items: flex-start; gap: 9px; }
.bo-notif:hover { background: var(--bo-hover); }
/* Unread is a dot and a weight, not a colour wash: a list where every unread row is
   tinted stops distinguishing anything once there are ten of them. */
.bo-notif-dot { flex: none; width: 6px; height: 6px; margin-top: 7px; border-radius: var(--bo-r-full); background: transparent; }
.bo-notif.is-unread .bo-notif-dot { background: var(--bo-blue-600); }
.bo-notif.is-unread .bo-qrow-title { font-weight: 650; color: var(--bo-ink); }

/* ── Automations ───────────────────────────────────────────────────────────
   One card per rule, grouped by the same categories the activity feed uses. The
   card carries the sentence and the mode; the detail is a click away. */
.bo-auto-group + .bo-auto-group { margin-top: 20px; }
.bo-auto-cards { display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
.bo-auto-card { display: flex; flex-direction: column; gap: 7px; padding: 13px 14px; }
/* A rule that is off keeps its place and its words, and stops shouting. */
.bo-auto-card.is-off { background: var(--bo-bg-shell); border-style: dashed; }
.bo-auto-card.is-off .bo-auto-name { color: var(--bo-muted); }
.bo-auto-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.bo-auto-name { font-size: var(--bo-t-14); font-weight: 600; color: var(--bo-ink); }
.bo-auto-when { font-size: var(--bo-t-12); color: var(--bo-muted); }
.bo-auto-foot { display: flex; align-items: center; gap: 8px; margin-top: 3px; padding-top: 8px; border-top: 1px solid var(--bo-border-soft); font-size: var(--bo-t-11); color: var(--bo-faint); }
.bo-auto-foot form { margin: 0; }

/* The template editor: wording on the left, what it will look like and what you may
   say on the right. Side by side because writing a template is a loop between the
   two, and a preview behind a tab is a preview nobody looks at. */
.bo-tpl-editor { display: grid; gap: 16px; grid-template-columns: minmax(0, 1fr) 320px; align-items: start; }
@media (max-width: 1100px) { .bo-tpl-editor { grid-template-columns: minmax(0, 1fr); } }
.bo-tpl-body { min-height: 320px; font-family: var(--bo-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: var(--bo-t-13); line-height: 1.6; }
.bo-tpl-preview { white-space: pre-wrap; font-size: var(--bo-t-13); line-height: 1.6; color: var(--bo-ink-soft); }
.bo-tpl-vars { display: flex; flex-direction: column; gap: 10px; }
.bo-tpl-vargroup-label { font-size: var(--bo-t-11); font-weight: 650; letter-spacing: .06em; text-transform: uppercase; color: var(--bo-muted); margin-bottom: 4px; }
.bo-tpl-var { display: inline-flex; align-items: center; margin: 0 4px 4px 0; padding: 2px 7px; border: 1px solid var(--bo-border); border-radius: var(--bo-r-xs); background: var(--bo-bg-shell); font-family: var(--bo-font-mono, ui-monospace, monospace); font-size: var(--bo-t-11); color: var(--bo-ink-soft); cursor: pointer; }
.bo-tpl-var:hover { background: var(--bo-blue-50); border-color: var(--bo-blue-300); color: var(--bo-blue-700); }

/* Provenance beside a message in the thread. Quiet by design — most messages are
   somebody's, and the tag matters only where it is not. */
.bo-msg-origin { display: inline-flex; align-items: center; gap: 3px; font-size: var(--bo-t-10); font-weight: 600; letter-spacing: .03em; text-transform: uppercase; color: var(--bo-faint); }
.bo-msg-origin--automatic { color: var(--bo-blue-600); }
.bo-msg-origin--review { color: var(--bo-violet-600); }

/* ─── Operational board (Command Center / My Work) ────────────────────────── */
/* Five stacked sections, each a full-bleed table with its own queue tabs. The page
   scrolls; the tables do not, so scanning down five queues is one gesture.
   See docs/WORK_QUEUES.md. */
/* A frame is inline by default, which would collapse the section it wraps. Declared
   here rather than leaning on a utility class, so the board does not depend on what
   Tailwind decides to keep. */
.bo-app turbo-frame { display: block; }

.bo-worksec { border-top: 1px solid var(--bo-border); }
.bo-worksec:first-of-type { border-top: none; }
.bo-worksec-head { display: flex; align-items: baseline; gap: 12px; padding: 16px var(--bo-page-x) 8px; flex-wrap: wrap; }
.bo-worksec-title { display: flex; align-items: baseline; gap: 8px; min-width: 0; flex: 1; flex-wrap: wrap; }
.bo-worksec-title .bo-ic { align-self: center; color: var(--bo-muted); }
.bo-worksec-meaning { font-size: var(--bo-t-12); color: var(--bo-faint); }
.bo-worksec-actions { display: flex; align-items: center; gap: 14px; white-space: nowrap; }

/* The queue itself. The inset, the border, the radius and the cell gutter are
   the table's own — the same ones it has on every other page — so the section
   only has to say where it ends. It used to restate all four as descendant
   overrides, which is how the board's tables came to look unlike every other
   table in the product. */
.bo-worksec-body { padding: 0 0 22px; }
.bo-worksec-body .bo-empty { margin: 12px var(--bo-page-x) 0; padding: 22px 20px; }

.bo-worksec--quiet .bo-worksec-head { padding: 10px var(--bo-page-x); }
.bo-worksec--quiet .bo-h2 { font-size: var(--bo-t-14); color: var(--bo-muted); font-weight: 600; }
.bo-worksec--quiet .bo-ic { color: var(--bo-navy-300); }

/* Waiting-on: the column that says who owes the next move. Two live states and one
   quiet one, so a table skimmed at speed reads as amber-us / blue-them / nothing. */
.bo-waiting { display: inline-flex; align-items: center; gap: 5px; font-size: var(--bo-t-12); font-weight: 600; white-space: nowrap; }
.bo-waiting .bo-ic { width: 12px; height: 12px; }
.bo-waiting--agency { color: var(--bo-amber-700); }
.bo-waiting--client { color: var(--bo-blue-700); }
.bo-waiting--none   { color: var(--bo-faint); font-weight: 500; }

/* Next action: the widest, strongest cell on every one of these tables. The reason
   underneath is what keeps a warning accountable — never a bare priority number. */
.bo-act { font-weight: 600; color: var(--bo-ink); }
.bo-act-why { font-size: var(--bo-t-11); color: var(--bo-muted); }
.bo-act-why--critical { color: var(--bo-red-600); font-weight: 600; }
.bo-act-why--high { color: var(--bo-amber-700); }

.bo-cell-link { display: block; color: inherit; }
.bo-unread-dot { display: inline-block; width: 6px; height: 6px; border-radius: var(--bo-r-full); background: var(--bo-primary); margin-right: 5px; vertical-align: middle; }
.bo-quiet { color: var(--bo-faint); }

/* My Work's cross-resource list: the six things to do next, in order. */
.bo-next { list-style: none; margin: 0; padding: 0; }
.bo-next-row { display: flex; align-items: center; gap: 12px; padding: 10px 14px; border-top: 1px solid var(--bo-border-soft); color: inherit; }
.bo-next-row:first-child { border-top: none; }
.bo-next-row:hover { background: var(--bo-hover); }
.bo-next-n { flex: none; width: 22px; height: 22px; border-radius: var(--bo-r-full); background: var(--bo-navy-100); color: var(--bo-muted); font-size: var(--bo-t-11); font-weight: 650; display: grid; place-items: center; font-variant-numeric: tabular-nums; }
.bo-next-txt { flex: 1; min-width: 0; }
.bo-next-title { font-size: var(--bo-t-13); font-weight: 600; color: var(--bo-ink); }
.bo-next-sub { font-size: var(--bo-t-12); color: var(--bo-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bo-next-meta { display: flex; align-items: center; gap: 8px; white-space: nowrap; }
.bo-next-row .bo-go { color: var(--bo-faint); opacity: 0; }
.bo-next-row:hover .bo-go { opacity: 1; }

/* Narrow screens: keep identity, the action and the urgency; drop the context columns
   rather than let the table scroll sideways past the thing you came to read. */
@media (max-width: 1180px) {
  .bo-worksec .bo-table thead th:nth-child(2),
  .bo-worksec .bo-table tbody td:nth-child(2) { display: none; }
}
@media (max-width: 900px) {
  .bo-worksec .bo-table thead th:nth-last-child(-n+2),
  .bo-worksec .bo-table tbody td:nth-last-child(-n+2) { display: none; }
  .bo-worksec-meaning { display: none; }
}

/* ==========================================================================
   19. PAGINATION
   The caption under a table answers "where am I in this list" on the left and
   "how do I move" on the right. Numbers are tabular so the range does not
   twitch as the page changes.
   ========================================================================== */
.bo-pager { display: flex; align-items: center; gap: var(--kernol-space-3); margin-left: auto; }
.bo-pager-range { font-size: var(--bo-t-12); color: var(--bo-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.bo-pager-pages { display: inline-flex; align-items: center; gap: var(--kernol-space-1); }
.bo-pager-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 32px; height: 32px; padding: 0 8px;
  border: 1px solid var(--bo-border); border-radius: var(--bo-r-sm);
  background: var(--kernol-surface); color: var(--bo-ink-soft);
  font-family: var(--bo-font); font-size: var(--bo-t-12); font-weight: 600;
  font-variant-numeric: tabular-nums; cursor: pointer;
}
.bo-pager-btn:hover { border-color: var(--bo-border-hover); color: var(--bo-ink); }
.bo-pager-btn:focus-visible { outline: none; border-color: var(--kernol-blue-400); box-shadow: var(--kernol-focus-ring); }
.bo-pager-btn .bo-ic { width: 14px; height: 14px; }
/* The current page is a bordered box rather than a filled one: a navy chip here
   would outrank the page's own primary button, which is the actual thing to do
   on the screen. `aria-current` carries it for a screen reader. */
.bo-pager-btn.is-current { border-color: var(--kernol-navy-900); color: var(--bo-ink); font-weight: 680; }
/* A step that has nowhere to go stays in place and stops responding, rather
   than disappearing and shifting every other control sideways. */
.bo-pager-btn.is-disabled, .bo-pager-btn[disabled] { color: var(--kernol-grey-500); border-color: var(--bo-border-soft); pointer-events: none; }
.bo-pager-gap { padding: 0 2px; color: var(--bo-faint); font-size: var(--bo-t-12); }
/* "Show [10] entries" — page size, on the left of the same strip. */
.bo-pagesize { display: inline-flex; align-items: center; gap: 7px; font-size: var(--bo-t-12); color: var(--bo-muted); }
.bo-pagesize .bo-select { height: 30px; width: auto; min-width: 68px; font-size: var(--bo-t-12); }

/* ==========================================================================
   20. TABLE AFFORDANCES — sorting, selection, row actions
   ========================================================================== */
/* A sortable column header is a button in the header cell, so it is reachable
   by keyboard and announces itself. The arrows sit at low contrast until the
   column is the one being sorted. */
.bo-sort { display: inline-flex; align-items: center; gap: 5px; background: none; border: none; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; white-space: nowrap; }
.bo-sort .bo-ic { width: 13px; height: 13px; color: var(--kernol-grey-500); }
.bo-sort:hover { color: var(--bo-ink); }
.bo-sort:hover .bo-ic { color: var(--bo-muted); }
.bo-sort:focus-visible { outline: none; border-radius: var(--bo-r-xs); box-shadow: var(--kernol-focus-ring); }
.bo-sort.is-sorted { color: var(--bo-ink); font-weight: 680; }
.bo-sort.is-sorted .bo-ic { color: var(--kernol-navy-900); }

/* Selection column. Fixed and narrow so the identity column starts at the same
   x on every table, whether or not that table can select. */
.bo-table thead th.bo-check-col,
.bo-table tbody td.bo-check-col { width: 40px; padding-right: 0; }

/* The row's own "..." — invisible until the row is under the cursor or the
   button itself has focus, so a table of forty rows is not a table of forty
   buttons. Keyboard users get it from focus, which is why the second selector
   is not optional. */
.bo-rowmenu { position: relative; display: inline-block; }
.bo-rowmenu > summary { list-style: none; display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: var(--bo-r-sm); color: var(--bo-faint); cursor: pointer; opacity: 0; }
.bo-rowmenu > summary::-webkit-details-marker { display: none; }
.bo-table tbody tr:hover .bo-rowmenu > summary,
.bo-rowmenu[open] > summary,
.bo-rowmenu > summary:focus-visible { opacity: 1; }
.bo-rowmenu > summary:hover { background: var(--bo-hover-strong); color: var(--bo-ink); }
.bo-rowmenu > summary:focus-visible { outline: none; box-shadow: var(--kernol-focus-ring); }
.bo-rowmenu > .bo-menu { position: absolute; right: 0; top: calc(100% + 4px); z-index: 60; }
/* Near the bottom of a long table the menu opens upward instead of off-screen. */
.bo-rowmenu--up > .bo-menu { top: auto; bottom: calc(100% + 4px); }

/* ==========================================================================
   21. OVERLAYS — modal, drawer, tooltip
   A modal for a short confirmation or a small form; a drawer for context,
   quick editing and previews. Neither is a place to put a forty-field form.
   ========================================================================== */
.bo-modal {
  position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%);
  z-index: 80; width: min(560px, calc(100vw - 32px)); max-height: calc(100vh - 64px);
  display: flex; flex-direction: column;
  background: var(--kernol-surface); border-radius: var(--bo-r-lg);
  box-shadow: var(--bo-shadow-modal); overflow: hidden;
}
.bo-modal--wide { width: min(820px, calc(100vw - 32px)); }
.bo-modal-head { display: flex; align-items: flex-start; gap: var(--kernol-space-3); padding: var(--kernol-space-5) var(--kernol-space-5) var(--kernol-space-3); }
.bo-modal-title { font-size: var(--bo-t-16); font-weight: 650; color: var(--bo-ink); }
.bo-modal-sub { font-size: var(--bo-t-13); color: var(--bo-muted); margin-top: 3px; }
.bo-modal-close { margin-left: auto; flex: none; }
.bo-modal-body { padding: 0 var(--kernol-space-5) var(--kernol-space-5); overflow-y: auto; display: flex; flex-direction: column; gap: var(--kernol-space-3); }
.bo-modal-foot { display: flex; align-items: center; gap: var(--kernol-space-2); padding: var(--kernol-space-4) var(--kernol-space-5); border-top: 1px solid var(--bo-border-soft); background: var(--kernol-grey-25); }
.bo-modal-foot .bo-modal-foot-spacer { flex: 1; }

.bo-drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 80;
  width: min(460px, 100vw); display: flex; flex-direction: column;
  background: var(--kernol-surface); border-left: 1px solid var(--bo-border);
  box-shadow: var(--bo-shadow-modal);
}
.bo-drawer--wide { width: min(680px, 100vw); }
.bo-drawer-head { display: flex; align-items: center; gap: var(--kernol-space-3); padding: var(--kernol-space-4) var(--kernol-space-5); border-bottom: 1px solid var(--bo-border-soft); flex: none; }
.bo-drawer-title { font-size: var(--bo-t-16); font-weight: 650; color: var(--bo-ink); min-width: 0; }
.bo-drawer-body { flex: 1; min-height: 0; overflow-y: auto; padding: var(--kernol-space-5); display: flex; flex-direction: column; gap: var(--kernol-space-4); }
.bo-drawer-foot { flex: none; display: flex; align-items: center; gap: var(--kernol-space-2); padding: var(--kernol-space-4) var(--kernol-space-5); border-top: 1px solid var(--bo-border-soft); }

/* CSS-only tooltip on a `title`-free element. Hover *and* focus, because a
   tooltip only a mouse can reach is a tooltip half the team cannot read. */
.bo-tip { position: relative; }
.bo-tip::after {
  content: attr(data-tip); position: absolute; bottom: calc(100% + 6px); left: 50%;
  transform: translateX(-50%); z-index: 70;
  background: var(--kernol-navy-900); color: var(--kernol-text-on-dark);
  font-size: var(--bo-t-11); font-weight: 550; line-height: 1.35;
  padding: 5px 8px; border-radius: var(--bo-r-xs); white-space: nowrap;
  opacity: 0; pointer-events: none; transition: opacity 0.1s;
  box-shadow: var(--bo-shadow-pop);
}
.bo-tip:hover::after, .bo-tip:focus-visible::after { opacity: 1; }

/* ==========================================================================
   22. LOADING
   Turbo already gives the page a progress bar; it only needed the brand's
   colour. Skeletons are for a frame that will be replaced in place, where a
   collapsing box would move everything under it.
   ========================================================================== */
.turbo-progress-bar { background: linear-gradient(to right, var(--kernol-teal-500), var(--kernol-blue-600)); height: 3px; }

.bo-skel { background: linear-gradient(90deg, var(--kernol-grey-100) 25%, var(--kernol-grey-200) 37%, var(--kernol-grey-100) 63%); background-size: 400% 100%; border-radius: var(--bo-r-xs); animation: bo-skel 1.4s ease-in-out infinite; }
.bo-skel-line { height: 11px; margin: 5px 0; }
.bo-skel-line--sm { width: 40%; }
.bo-skel-line--md { width: 65%; }
.bo-skel-row { display: flex; align-items: center; gap: var(--kernol-space-3); padding: var(--kernol-space-3) var(--kernol-space-4); border-bottom: 1px solid var(--bo-border-soft); }
.bo-skel-avatar { width: 28px; height: 28px; border-radius: var(--bo-r-full); flex: none; }
@keyframes bo-skel { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
/* Anybody who has asked their operating system to stop moving things gets a
   still placeholder rather than a shimmering one. */
@media (prefers-reduced-motion: reduce) {
  .bo-skel { animation: none; background: var(--kernol-grey-100); }
  .bo-btn:active { transform: none; }
}

/* ==========================================================================
   23. LIST ROWS — the dashboard's reading unit
   A tinted icon tile, two lines of text, and one chip on the right saying when.
   Every compact list Kernol shows on a dashboard or in a rail is this row:
   a task, an upcoming event, a recent quotation, an unread message.
   ========================================================================== */
.bo-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.bo-listrow { display: flex; align-items: center; gap: var(--kernol-space-3); padding: var(--kernol-space-3) var(--kernol-space-4); border-top: 1px solid var(--bo-border-soft); color: inherit; min-height: var(--bo-row-h); }
.bo-listrow:first-child { border-top: none; }
a.bo-listrow:hover { background: var(--bo-hover); }
.bo-listrow-tile { width: 32px; height: 32px; border-radius: var(--kernol-radius); display: inline-flex; align-items: center; justify-content: center; flex: none; background: var(--kernol-neutral-soft); color: var(--kernol-neutral-ink); }
.bo-listrow-tile .bo-ic { width: 16px; height: 16px; }
.bo-listrow-tile--info { background: var(--kernol-info-soft); color: var(--kernol-info-ink); }
.bo-listrow-tile--pos { background: var(--kernol-success-soft); color: var(--kernol-success-ink); }
.bo-listrow-tile--warn { background: var(--kernol-warning-soft); color: var(--kernol-warning-ink); }
.bo-listrow-tile--neg { background: var(--kernol-danger-soft); color: var(--kernol-danger-ink); }
.bo-listrow-tile--review { background: var(--kernol-plan-soft); color: var(--kernol-plan-ink); }
.bo-listrow-txt { flex: 1; min-width: 0; line-height: 1.4; }
.bo-listrow-title { font-size: var(--bo-t-13); font-weight: 600; color: var(--bo-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bo-listrow-sub { font-size: var(--bo-t-12); color: var(--bo-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bo-listrow-meta { flex: none; display: flex; align-items: center; gap: var(--kernol-space-2); }

/* The date chip on the right of a list row. Tone by urgency, never by colour
   alone — the words "Today", "Overdue", "in 5 days" carry it. */
.bo-when { display: inline-flex; align-items: center; height: 22px; padding: 0 8px; border-radius: var(--bo-r-badge); font-size: var(--bo-t-11); font-weight: 620; white-space: nowrap; font-variant-numeric: tabular-nums; background: var(--kernol-neutral-soft); color: var(--kernol-neutral-ink); }
.bo-when--now  { background: var(--kernol-warning-soft); color: var(--kernol-warning-ink); }
.bo-when--soon { background: var(--kernol-plan-soft); color: var(--kernol-plan-ink); }
.bo-when--late { background: var(--kernol-danger-soft); color: var(--kernol-danger-ink); }

/* A day/heading break inside a list: TODAY, TOMORROW, AUG 22. */
.bo-listgroup { font-size: var(--bo-t-11); font-weight: 650; text-transform: uppercase; letter-spacing: var(--bo-track-eyebrow); color: var(--bo-faint); padding: var(--kernol-space-3) var(--kernol-space-4) var(--kernol-space-1); }
.bo-listrow + .bo-listgroup { border-top: 1px solid var(--bo-border-soft); margin-top: var(--kernol-space-1); }
/* A list that ends in "View all →" rather than in a hard edge. */
.bo-listfoot { display: flex; align-items: center; gap: 6px; padding: var(--kernol-space-3) var(--kernol-space-4); border-top: 1px solid var(--bo-border-soft); font-size: var(--bo-t-12); font-weight: 600; color: var(--bo-muted); }
.bo-listfoot:hover { color: var(--kernol-accent); }

/* ==========================================================================
   24. FIGURES — money, read down a column
   Quotations, payments and invoices. One label, one figure, aligned on the
   decimal, with the one that matters set larger than the ones that explain it.
   ========================================================================== */
.bo-figures { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: var(--kernol-space-3); }
.bo-figure { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.bo-figure-label { font-size: var(--bo-t-11); font-weight: 650; text-transform: uppercase; letter-spacing: var(--bo-track-eyebrow); color: var(--bo-faint); }
.bo-figure-val { font-size: var(--bo-t-18); font-weight: 680; color: var(--bo-ink); font-variant-numeric: tabular-nums; letter-spacing: var(--kernol-tracking-tight); }
.bo-figure-sub { font-size: var(--bo-t-12); color: var(--bo-muted); font-variant-numeric: tabular-nums; }
.bo-figure--lead .bo-figure-val { font-size: var(--bo-t-24); }
.bo-figure--pos .bo-figure-val { color: var(--kernol-teal-700); }
.bo-figure--due .bo-figure-val { color: var(--kernol-tan-700); }
.bo-figure--late .bo-figure-val { color: var(--bo-red-700); }
/* Every money figure in the product, wherever it appears. */
.bo-money { font-variant-numeric: tabular-nums; letter-spacing: -0.005em; }

/* The payment schedule under the figures: one bordered row per installment, so
   the sequence reads as a sequence rather than as a paragraph of dates. */
.bo-installments { margin: var(--kernol-space-3) 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: var(--kernol-space-2); }
.bo-installment { border: 1px solid var(--bo-border); border-radius: var(--bo-r-sm); padding: var(--kernol-space-2) var(--kernol-space-3); }

/* ==========================================================================
   25. QUICK ACTIONS AND HELP
   ========================================================================== */
.bo-quickgrid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--kernol-space-2); }
.bo-quick { display: flex; align-items: center; gap: 7px; padding: 9px 10px; border: 1px solid var(--bo-border); border-radius: var(--bo-r-sm); background: var(--kernol-surface); font-size: var(--bo-t-12); font-weight: 570; color: var(--bo-ink-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bo-quick:hover { border-color: var(--kernol-blue-200); background: var(--kernol-blue-50); color: var(--kernol-blue-800); }
.bo-quick .bo-ic { width: 14px; height: 14px; color: var(--bo-faint); flex: none; }
.bo-quick:hover .bo-ic { color: var(--kernol-blue-600); }

/* The one place the mascot earns its keep on a working screen. */
.bo-helpcard { display: flex; align-items: flex-end; gap: var(--kernol-space-3); padding: var(--kernol-space-4); background: linear-gradient(180deg, var(--kernol-tan-soft), var(--kernol-surface) 70%); }
.bo-helpcard-txt { flex: 1; min-width: 0; }
.bo-helpcard-title { font-size: var(--bo-t-14); font-weight: 650; color: var(--bo-ink); }
.bo-helpcard-sub { font-size: var(--bo-t-12); color: var(--bo-muted); margin-top: 2px; line-height: 1.45; }
.bo-helpcard-mascot { width: 84px; height: auto; flex: none; margin-bottom: -4px; }

/* ==========================================================================
   26. ACCESSIBILITY AND RESPONSIVE
   Desktop is the job, but nothing may become unusable on a smaller screen.
   The order things are given up in: the sidebar's labels, then low-priority
   table columns, then the detail rail folds under the main column.
   ========================================================================== */
/* One visible focus treatment for everything that does not declare its own. */
.bo-app :where(a, button, [tabindex], summary):focus-visible {
  outline: 2px solid var(--kernol-blue-600);
  outline-offset: 2px;
  border-radius: var(--bo-r-xs);
}
.bo-visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
/* Lets a keyboard reach the table body without walking the whole navigation. */
.bo-skip { position: absolute; left: -9999px; top: 0; z-index: 100; }
.bo-skip:focus { left: var(--bo-page-x); top: 8px; padding: 8px 12px; background: var(--kernol-surface); border: 1px solid var(--bo-border); border-radius: var(--bo-r-sm); box-shadow: var(--bo-shadow-pop); font-size: var(--bo-t-13); font-weight: 600; }

@media (max-width: 1280px) {
  .bo-detail { grid-template-columns: minmax(0, 1fr) 300px; }
}
/* Below this the rail stops being a rail: it goes under the main column, where
   it keeps its content and loses only its position. */
@media (max-width: 1080px) {
  .bo-detail { grid-template-columns: minmax(0, 1fr); }
  .bo-detail-side { border-left: none; border-top: 1px solid var(--bo-border); }
  .bo-searchbtn { width: 200px; }
}
/* The sidebar becomes a rail of icons. Nothing is removed from it — the labels
   are what a narrow screen cannot afford, and the tooltip gives them back. */
@media (max-width: 900px) {
  :root { --bo-sidebar-w: 60px; --bo-page-x: 16px; }
  .bo-brand { justify-content: center; padding-inline: 0; }
  .bo-brand-word { display: none; }
  .bo-workspace, .bo-nav-group-label, .bo-nav-label,
  .bo-sidebar-foot .bo-grow { display: none; }
  .bo-nav { padding-inline: 8px; }
  .bo-nav-item { justify-content: center; padding-inline: 0; }
  .bo-nav-count { position: absolute; margin: 0; transform: translate(12px, -10px); }
  .bo-nav-item { position: relative; }
  .bo-sidebar-foot { justify-content: center; }
  .bo-searchbtn { width: 44px; padding: 0; justify-content: center; }
  .bo-searchbtn span, .bo-searchbtn { font-size: 0; }
  .bo-searchbtn .bo-ic { font-size: initial; }
  .bo-kbd { display: none; }
  .bo-formrow--2, .bo-formrow--3 { grid-template-columns: 1fr; }
  .bo-grid--2, .bo-grid--3 { grid-template-columns: 1fr; }
  .bo-metrics { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
}
@media (max-width: 700px) {
  .bo-setting .bo-card-head { flex-wrap: wrap; }
  .bo-setting-status { margin-left: 0; }
  .bo-pagehead { flex-wrap: wrap; }
  .bo-pagehead-actions { margin-left: 0; width: 100%; }
  .bo-quickgrid { grid-template-columns: 1fr; }
  .bo-tablefoot { flex-wrap: wrap; }
  .bo-pager { margin-left: 0; }
}

/* ── Time zones ────────────────────────────────────────────────────────────────
   A real-world time is shown with the zone it belongs to: inline ("22:35 GMT+8") beside
   a short stamp, stacked ("Kuala Lumpur time (GMT+8)") under a full one. */
.bo-tz { font-size: var(--bo-t-11); color: var(--bo-muted); white-space: nowrap; }
.bo-tz--stacked { display: block; margin-top: 1px; }
/* A datetime-local input and its zone select, as one control. */
.bo-zoned { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr); gap: 8px; }
@media (max-width: 700px) { .bo-zoned { grid-template-columns: 1fr; } }
.bo-zoned-zone { font-size: var(--bo-t-12); }

/* ── On/off switch ─────────────────────────────────────────────────────────── */
.bo-switch-form { margin: 0; }
.bo-switch { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; font-size: var(--bo-t-13); font-weight: 550; color: var(--bo-ink); }
.bo-switch-input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.bo-switch-track { position: relative; width: 38px; height: 22px; border-radius: 11px; background: var(--kernol-border-strong); transition: background .15s ease; flex: none; }
.bo-switch-thumb { position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.25); transition: transform .15s ease; }
.bo-switch-input:checked + .bo-switch-track { background: var(--kernol-accent); }
.bo-switch-input:checked + .bo-switch-track .bo-switch-thumb { transform: translateX(16px); }
.bo-switch-input:focus-visible + .bo-switch-track { outline: 2px solid var(--kernol-accent); outline-offset: 2px; }

/* ── Excluded clients panel ───────────────────────────────────────────────── */
.bo-suppress-search { margin-bottom: var(--kernol-space-2); }
.bo-suppress-results { border: 1px dashed var(--bo-border); border-radius: var(--bo-r-sm); padding: 6px 10px; margin-bottom: var(--kernol-space-3); background: var(--kernol-surface-secondary); }
.bo-suppress-row { display: flex; align-items: center; gap: var(--kernol-space-3); padding: 8px 0; border-top: 1px solid var(--bo-border-soft); }
.bo-suppress-row:first-child { border-top: none; }
.bo-suppress-txt { flex: 1; min-width: 0; }
.bo-suppress-name { font-size: var(--bo-t-13); font-weight: 550; color: var(--bo-ink); }
.bo-suppress-row form { margin: 0; }
.bo-suppress-empty { padding: 6px 0; }
