/* ==========================================================================
   ui.css — THE CONSOLE'S COMPONENT KIT (2026-09-22)

   One set of controls for every operator screen: Home, Knowledge, Builder,
   Scoping, Proposals, Applications, Branding, Partners, Tour, Handbook,
   Campaigns, Account, Team, Invoices, Pricing and Analytics. Fifteen pages
   had grown fifteen buttons, nine card corners (5 to 18px) and forty ways to
   write a hover transition; this file is the one place those are decided.

   HOW IT IS MEANT TO BE USED
   - New markup uses the classes below (`.btn`, `.card`, `.chip`, `.tab`,
     `.input`, `.switch`, `.overlay` + `.dialog`, `.empty`, `.note`).
   - Existing page classes (`.wsc__box`, `.kl__i`, `.fcard`, `.oh__toola`,
     ...) keep their names and READ THE TOKENS: `var(--radius)`,
     `var(--dur) var(--ease)`, `var(--ctl-h)`, `var(--card-pad)`. A page
     stylesheet may not write a literal corner or a literal duration.
   - It loads AFTER every page's own `{% block head %}` (base.html), so on a
     screen that also loads platform.css (Knowledge, Builder, Branding,
     Applications) the kit's `.btn` is the one that paints, not the tenant
     app's. Page rules that need to win do so by specificity, never by order.

   THE FOUR RULES
   1. ONE RADIUS. `--radius` (theme.css, 10px) on every button, card, field,
      chip, dialog and tab. `--radius-lg` and `--radius-pill` alias to it.
      The two exemptions: a CIRCLE (`50%`: avatars, dots, spinners) and a
      CONCENTRIC ring, written `calc(var(--radius) + Npx)` for a box that
      sits Npx outside another. Miniature mock-ups of the tenant app
      (`.bsprev__*`, `.oh__thumb*`) draw the app they depict and are exempt.
   2. ONE MOTION. `--dur` for a hover, `--dur-slow` for something that moves
      (a tab's underline, a switch's knob, a dialog rising); `--ease` for
      state, `--ease-out` for arrival. Reduced motion zeroes both durations
      in one place, so no component needs its own `prefers-reduced-motion`
      block for a transition. Progress bars and tours keep their own longer
      timings: they are content, not chrome.
   3. ONE CONTROL HEIGHT. `--ctl-h` 36px for a button, an input and a select
      on one row; `--ctl-h-sm` 30px beside dense text; `--ctl-h-lg` 44px for
      the one action a page is about. On a phone a button never goes under
      44px, because that is a fingertip.
   4. A HOVER CHANGES A BORDER AND A SHADOW, a press moves one pixel. Nothing
      glows, nothing scales, nothing has a gradient on it: the console's
      colour lives in the eight `data-hue` pairs on the heads and marks, and
      a control that also carried colour would compete with them.

   Colours are theme.css's tokens throughout. Two semantic tints the console
   already used (paid blue, end-to-end plum) are declared here once so the
   chips on Applications and Proposals cannot drift apart.
   ========================================================================== */

:root {
  /* THE CONSOLE RADIUS UNDER ITS OWN NAME. theme.css declares `--radius`
     for the console; platform.css re-declares it (12px) inside `.pf`, which
     the studios wrap themselves in. A `.pf` page re-points `--radius` to
     this rather than to a literal, so there is still one number. */
  --ui-radius: 10px;

  /* Motion */
  --dur:      .16s;
  --dur-slow: .28s;
  --ease:     cubic-bezier(.2, .7, .3, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);

  /* THE OPENING PAGE'S COLUMN (2026-09-28, customer request): Knowledge,
     Builder, Scoping, Proposals, Applications, Branding and Tour open on one
     shape, so they share one width and one lede measure. Each opener reads
     these; none writes its own number. */
  --opener-w:    940px;
  --opener-lede: 62ch;
  --opener-lede-size: .92rem;
  /* ONE DOOR IS A THIRD OF THE ROW, whatever the row holds (customer,
     2026-09-28: two doors stretched across three cards' width read as too
     long). The column's 4px side padding and two 14px gaps come off first. */
  --opener-gap:  14px;
  --opener-door: calc((var(--opener-w) - 8px - 2 * var(--opener-gap)) / 3);
  /* And ONE ILLUSTRATION HEIGHT on a door: three pages drew theirs at 96px,
     Knowledge at 110 and the Builder at 141, so the same sketch read as three
     sizes (customer, 2026-09-28). */
  --opener-art-h: 128px;

  /* Controls */
  --ctl-h:    36px;
  --ctl-h-sm: 30px;
  --ctl-h-lg: 44px;
  --ctl-px:   14px;
  --ctl-fs:   .84rem;

  /* Cards and chips */
  --card-pad: 14px 16px;
  --chip-fs:  .7rem;

  /* Two console tints beside theme.css's ok/warn/danger */
  --info:      #1c4d8f;
  --info-soft: #e8f0fb;
  --plum:      #4c3a99;
  --plum-soft: #efe9fb;
}
@media (prefers-reduced-motion: reduce) {
  :root { --dur: 0s; --dur-slow: 0s; }
}

/* --------------------------------------------------------------------------
   Buttons
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  height: var(--ctl-h);
  padding: 0 var(--ctl-px);
  font: inherit;
  font-size: var(--ctl-fs);
  font-weight: 550;
  line-height: 1;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  cursor: pointer;
  white-space: nowrap;
  /* `.btn` goes on anchors as well as buttons. A button is not underlined. */
  text-decoration: none;
  -webkit-appearance: none;
  appearance: none;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease),
              color var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}
.btn:hover:not(:disabled):not([aria-disabled="true"]) {
  background: var(--surface-2);
  border-color: var(--accent-line);
  color: var(--accent-ink);
}
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:focus-visible { outline: none; box-shadow: var(--ring); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }
/* A control that sets `display` needs its own `[hidden]` rule, or a script
   hiding it does nothing (the `.rpicks` trap, met five times). */
.btn[hidden] { display: none; }

/* `.btn--go` is the tenant app's word for the same thing; both paint alike
   so a template can use either. */
.btn--primary, .btn--go {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.btn--primary:hover:not(:disabled):not([aria-disabled="true"]),
.btn--go:hover:not(:disabled):not([aria-disabled="true"]) {
  background: var(--accent-2);
  border-color: var(--accent-2);
  color: #fff;
}

.btn--ghost { background: transparent; border-color: transparent; color: var(--ink-2); }
.btn--ghost:hover:not(:disabled):not([aria-disabled="true"]) {
  background: var(--surface-sunk); border-color: transparent; color: var(--ink);
}

.btn--quiet { background: transparent; border-color: transparent; color: var(--ink-3); }
.btn--quiet:hover:not(:disabled):not([aria-disabled="true"]) {
  background: var(--surface-2); border-color: transparent; color: var(--ink);
}

.btn--danger { color: var(--danger); }
.btn--danger:hover:not(:disabled):not([aria-disabled="true"]) {
  background: var(--danger-soft);
  border-color: color-mix(in srgb, var(--danger) 35%, var(--surface));
  color: var(--danger);
}
/* Armed: the first click relabels, the second acts (dashboards.js `armed()`). */
.btn[data-armed] {
  background: var(--danger-soft);
  border-color: var(--danger);
  color: var(--danger);
}

.btn--sm { height: var(--ctl-h-sm); padding: 0 11px; font-size: .78rem; }
.btn--lg { height: var(--ctl-h-lg); padding: 0 20px; font-size: .92rem; }
.btn--icon { width: var(--ctl-h); padding: 0; }
.btn--sm.btn--icon { width: var(--ctl-h-sm); }
.btn--block { width: 100%; }

.btn > svg {
  width: 15px; height: 15px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}

/* The icon button: a square control whose word rides in `aria-label` and a
   tooltip, never as visible text. 30px, so it stands level with a `.btn--sm`
   in the same row (a card's foot, a list row); `--sm` sits inside dense text;
   `--lg`/`--help` level with a `.btn`. */
.ib {
  display: inline-grid;
  place-items: center;
  width: var(--ctl-h-sm); height: var(--ctl-h-sm);
  padding: 0;
  font: inherit;
  color: var(--ink-2);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  cursor: pointer;
  text-decoration: none;
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease),
              background var(--dur) var(--ease);
}
.ib:hover { border-color: var(--accent); color: var(--accent); }
.ib:focus-visible { outline: none; box-shadow: var(--ring); }
.ib > svg, .ib .pf-ico {
  width: 14px; height: 14px;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}
.ib--sm { width: 24px; height: 24px; }
.ib--lg, .ib--help { width: 34px; height: 34px; border-radius: var(--radius); }
.ib--lg > svg, .ib--help > svg, .ib--help .pf-ico { width: 17px; height: 17px; }
.ib--danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 22%, var(--surface)); background: var(--danger-soft); }
.ib--danger:hover { border-color: var(--danger); color: var(--danger); background: color-mix(in srgb, var(--danger) 12%, var(--surface)); }

/* --------------------------------------------------------------------------
   Cards
   A card is a surface, a hairline and a soft shadow. The studios drop the
   hairline (`--flat` keeps the shadow, `--line` keeps the hairline); both
   are the same corner and the same padding.
   -------------------------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.card--flat  { border-color: transparent; }
.card--line  { box-shadow: none; }
.card--sunk  { background: var(--surface-2); box-shadow: none; }
.card--dashed { border-style: dashed; border-color: var(--rule-strong); box-shadow: none; background: transparent; }
/* A box whose one act is destructive: the hairline says so before the words do. */
.card--danger { border-color: color-mix(in srgb, var(--danger) 30%, var(--surface)); }
.card--hover {
  transition: box-shadow var(--dur) var(--ease), border-color var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}
.card--hover:hover { box-shadow: var(--shadow-lg); border-color: var(--accent-line); transform: translateY(-1px); }
.card[aria-current="true"], .card--current { box-shadow: var(--shadow-lg), inset 3px 0 0 var(--accent); }

.card__head {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 12px 16px;
  border-bottom: 1px solid var(--rule);
}
.card__title { margin: 0; font-size: .95rem; font-weight: 600; letter-spacing: -.01em; line-height: 1.3; }
.card__sub   { margin: 0; font-size: .78rem; color: var(--ink-3); }
.card__end   { margin-left: auto; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.card__body  { padding: var(--card-pad); }
.card__foot  {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 10px 16px;
  border-top: 1px solid var(--rule);
}

/* A section's heading with an optional sub-line and an end slot. */
.sechead {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  margin: 0 0 12px;
}
.sechead h2, .sechead h3 { margin: 0; font-size: 1.05rem; font-weight: 600; letter-spacing: -.01em; }
.sechead__sub { margin: 0; font-size: .82rem; color: var(--ink-2); flex: 1 1 100%; }
.sechead__end { margin-left: auto; display: flex; align-items: center; gap: 8px; }

.kicker {
  font-size: .68rem; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-3);
}

/* --------------------------------------------------------------------------
   Chips — a state, always with a word in it
   -------------------------------------------------------------------------- */

.chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px;
  font-size: var(--chip-fs); font-weight: 600; line-height: 1.5;
  white-space: nowrap;
  border-radius: var(--radius);
  border: 1px solid transparent;
  background: var(--surface-sunk);
  color: var(--ink-2);
}
.chip--ok     { background: var(--ok-soft);     color: var(--ok); }
.chip--warn   { background: var(--warn-soft);   color: var(--warn); }
.chip--danger { background: var(--danger-soft); color: var(--danger); }
.chip--accent { background: var(--accent-soft); color: var(--accent-ink); }
.chip--info   { background: var(--info-soft);   color: var(--info); }
.chip--plum   { background: var(--plum-soft);   color: var(--plum); }
.chip--line   { background: var(--surface); border-color: var(--rule); }
.chip > i { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }

/* --------------------------------------------------------------------------
   Fields
   `.field` and `.field__label` are theme.css's (the sign-in card's); these
   are the controls that go inside one at the console's height.
   -------------------------------------------------------------------------- */

.input, .textarea, .select {
  font: inherit;
  font-size: var(--ctl-fs);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.input { height: var(--ctl-h); padding: 0 12px; width: 100%; min-width: 0; }
/* The select draws its own arrow: `appearance: none` takes the browser's
   away, and the field family's `background` shorthand above would otherwise
   reset any arrow a page had painted (measured: a bare box on Analytics and
   Campaigns the day the kit landed). */
.select {
  height: var(--ctl-h);
  padding: 0 30px 0 12px;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%236b7a84' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
}
.textarea { padding: 9px 12px; min-height: 84px; width: 100%; resize: vertical; line-height: 1.5; }
.input::placeholder, .textarea::placeholder { color: var(--ink-faint); }
:is(.input, .textarea, .select):hover:not(:disabled) { border-color: var(--accent-line); }
:is(.input, .textarea, .select):focus { outline: none; border-color: var(--accent); box-shadow: var(--ring); }
:is(.input, .textarea, .select):disabled { background: var(--surface-sunk); color: var(--ink-faint); cursor: not-allowed; }
.input[aria-invalid="true"], .textarea[aria-invalid="true"] { border-color: var(--danger); }
.input--sm { height: var(--ctl-h-sm); font-size: .8rem; }

/* Two controls joined into one (a field and its Go button). The seam has no
   radius and no doubled border. */
/* --------------------------------------------------------------------------
   Combobox -- a typable chooser over a native select

   The markup is built by static/js/combobox.js over a `select[data-combo]`,
   which stays in the DOM and stays what submits. These rules dress what the
   script builds; with no script none of them match anything and the page has
   its native dropdown.

   THE NATIVE SELECT IS TAKEN OUT OF THE LAYOUT AND NOT `display: none`: a
   control with no box cannot be focused, and the form still has to be able to
   report a validation message against it.
   -------------------------------------------------------------------------- */

.combo { position: relative; min-width: 0; }
/* `.combo > .combo__native`, TWO CLASSES, and that is load-bearing: a page
   that sets a width on its own selects (`.sd .select { width: 100% }` on the
   scoping draft) ties with a single class and then wins on source order.
   Measured at a 400px window: the hidden select was 322px wide and, being
   absolutely positioned, pushed the DOCUMENT to 682px -- a page scrolling
   sideways because of a control nobody can see. */
.combo > .combo__native {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); border: 0;
  min-width: 0;
}
/* THE CARET IS THE AFFORDANCE. Without it the field is a text box, and a
   text box does not say that a list opens when you click it -- the `.select`
   above draws the same mark for the same reason, so the two read as one
   family. The right padding clears it. */
.combo__in {
  width: 100%;
  padding-right: 30px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%236b7a84' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
}
/* The list floats over what follows it, so a chooser near the foot of a card
   does not push the card open as somebody types. */
.combo__list {
  position: absolute; z-index: 20; left: 0; right: 0; top: calc(100% + 4px);
  margin: 0; padding: 4px; list-style: none;
  max-height: 232px; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--rule-strong);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
}
.combo__list[hidden] { display: none; }
.combo__opt {
  padding: 7px 10px; border-radius: var(--radius); cursor: pointer;
  font-size: .84rem; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.combo__opt:hover { background: var(--surface-2); }
/* The keyboard's place in the list, which is not the same thing as the
   chosen one: a reader walking the list with the arrows has not chosen
   anything yet. */
.combo__opt[data-at] { background: var(--accent-soft); color: var(--accent-ink); }
.combo__opt[data-on] { font-weight: 600; }
.combo__opt[data-on]::after { content: " \2713"; color: var(--accent); }
.combo__none { padding: 7px 10px; font-size: .8rem; color: var(--ink-3); }

.join { display: flex; }
.join > * + * { margin-left: -1px; }
.join > :not(:first-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; }
.join > :not(:last-child) { border-top-right-radius: 0; border-bottom-right-radius: 0; }

/* The switch: a track and a knob, the input clipped behind them. */
.switch { position: relative; display: inline-block; width: 34px; height: 20px; flex: none; vertical-align: middle; }
.switch > input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.switch__track {
  position: absolute; inset: 0;
  border-radius: var(--radius);
  background: var(--rule-strong);
  transition: background var(--dur) var(--ease);
}
.switch__track::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 16px; height: 16px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, .25);
  transition: transform var(--dur-slow) var(--ease-out);
}
.switch > input:checked + .switch__track { background: var(--accent); }
.switch > input:checked + .switch__track::after { transform: translateX(14px); }
.switch > input:focus-visible + .switch__track { box-shadow: var(--ring); }
.switch > input:disabled + .switch__track { opacity: .5; cursor: not-allowed; }

/* --------------------------------------------------------------------------
   Tabs and segmented controls
   -------------------------------------------------------------------------- */

.tabs {
  display: flex; gap: 2px; flex-wrap: wrap;
  margin: 0 0 16px;
  border-bottom: 1px solid var(--rule);
}
.tab {
  position: relative;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 9px 13px;
  font: inherit; font-size: .84rem; font-weight: 500;
  color: var(--ink-2);
  background: none; border: 0;
  border-radius: var(--radius) var(--radius) 0 0;
  text-decoration: none; white-space: nowrap; cursor: pointer;
  transition: color var(--dur) var(--ease), background var(--dur) var(--ease);
}
/* The underline grows in from the middle rather than appearing. */
.tab::after {
  content: ""; position: absolute; left: 10px; right: 10px; bottom: -1px; height: 2px;
  border-radius: var(--radius);
  background: var(--accent);
  transform: scaleX(0);
  transition: transform var(--dur-slow) var(--ease-out);
}
.tab:hover { color: var(--ink); background: var(--surface-2); }
.tab:focus-visible { outline: none; box-shadow: var(--ring); }
.tab[aria-current="page"], .tab[aria-selected="true"] { color: var(--ink); font-weight: 600; }
.tab[aria-current="page"]::after, .tab[aria-selected="true"]::after { transform: scaleX(1); }
.tab__n {
  font-size: .68rem; font-weight: 600; padding: 0 6px; line-height: 1.6;
  border-radius: var(--radius); background: var(--surface-sunk); color: var(--ink-3);
}

/* --------------------------------------------------------------------------
   Steps -- the chevron rail

   THE BUILDER'S OWN STEP, AS A KIT COMPONENT (2026-09-24, customer request on
   the scoping draft: "the Steps 1 - 5 use the same Step as the Builder").
   Copied in shape and in reasoning from `.rail` / `.st` in assembler.css,
   which the Builder draws and which no console page can read: that stylesheet
   is the public builder's own, five thousand lines, and loading it here to
   borrow one strip would be the wrong trade. Kept in step BY HAND, and the
   two are allowed to differ where the Builder's own states do not exist here.

   COLOUR IS NOT THE ONLY CARRIER: the step you are on is the only one with a
   ring round its disc, a done one wears a TICK, every other wears its NUMBER,
   and the page says which is which in `aria-current` as well.

   THE SHAPE IS `clip-path`, one polygon per element rather than a border
   trick: the element itself is painted the surface colour and `::before`,
   inset by two pixels and clipped to the same polygon, carries the state's
   fill -- so the white outline follows every edge of every arrow, point and
   notch included, whatever is drawn over it. (A `filter` drop-shadow cannot
   do it: filters apply BEFORE `clip-path`, so the silhouette is the
   element's rectangle and the clip then cuts it away.)

   A STEP IS A `<label>` HERE, not a button: the scoping draft's panels are
   switched by radios inside the form, so the open one survives a save with no
   script. That is also why the "on" state is not declared in this file for
   that page -- a label driven by a sibling radio cannot carry
   `aria-current`, so the page paints it through its own radio selectors, and
   `--step-fill` is the one property it has to set.
   -------------------------------------------------------------------------- */

.steps {
  display: flex; align-items: stretch;
  margin: 0 0 16px;
  overflow-x: auto; scrollbar-width: none;
}
.steps::-webkit-scrollbar { display: none; }
.step {
  position: relative;
  flex: 1 1 0; min-width: 0;
  display: flex; align-items: center; justify-content: center; gap: 9px;
  /* The left padding clears the notch the previous point sits in, the right
     clears this one's own point. */
  padding: 13px 20px 13px 24px;
  /* Each point overlaps the next one's notch, so five read as one strip
     rather than as five arrows with gaps between them. */
  margin-left: -14px;
  border: 0; cursor: pointer; font: inherit; text-align: left;
  color: var(--ink-3);
  --step-fill: var(--surface-sunk);
  background: var(--surface);
  clip-path: polygon(0 0, calc(100% - 14px) 0, 100% 50%, calc(100% - 14px) 100%, 0 100%, 14px 50%);
  transition: color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.step::before {
  content: ""; position: absolute; inset: 2px;
  background: var(--step-fill);
  clip-path: polygon(0 0, calc(100% - 13px) 0, 100% 50%, calc(100% - 13px) 100%, 0 100%, 13px 50%);
  transition: background var(--dur) var(--ease);
}
/* The disc and the name ride above the fill, which is a painted child and
   would otherwise cover them. */
.step__n, .step__l { position: relative; z-index: 1; }
/* Nothing points into the first, so it has a flat left edge and no overlap. */
.step:first-child {
  margin-left: 0; padding-left: 16px;
  clip-path: polygon(0 0, calc(100% - 14px) 0, 100% 50%, calc(100% - 14px) 100%, 0 100%);
}
.step:first-child::before {
  clip-path: polygon(0 0, calc(100% - 13px) 0, 100% 50%, calc(100% - 13px) 100%, 0 100%);
}
/* Each one ahead of the next in the stack, or its point is drawn over by the
   neighbour it points into. */
.step:nth-child(1) { z-index: 5; }
.step:nth-child(2) { z-index: 4; }
.step:nth-child(3) { z-index: 3; }
.step:nth-child(4) { z-index: 2; }
.step:nth-child(5) { z-index: 1; }
.step:nth-child(6) { z-index: 0; }
.step__n {
  width: 24px; height: 24px; flex: 0 0 auto;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1.5px solid var(--rule-strong);
  background: var(--surface);
  font-size: .64rem; font-weight: 600; letter-spacing: .02em;
  color: var(--ink-3);
  position: relative;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.step__l {
  font-size: .78rem; font-weight: 500; line-height: 1.25;
  color: var(--ink-3);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  transition: color var(--dur) var(--ease);
}
/* Hover lifts the step OUT of the strip rather than widening it: a transform
   does not reflow, so the one under the pointer grows over its neighbours
   instead of pushing the other four along the row. */
.step:hover { --step-fill: var(--accent-soft); color: var(--accent-ink);
              transform: scale(1.04); z-index: 7; }
.step:hover .step__l { color: var(--accent-ink); }
.step:hover .step__n { border-color: var(--accent); color: var(--accent-ink); }
.step:focus-visible { outline: none; box-shadow: var(--ring); }

/* DONE: the accent, filled, with a tick. The number stays in the markup and
   is made transparent rather than removed, so a screen reader still reads it
   -- the tick is a picture and has nothing to say to one. */
.step--done { --step-fill: var(--accent); color: #fff; }
.step--done .step__l { color: #fff; }
.step--done .step__n {
  background: rgba(255, 255, 255, .18);
  border-color: rgba(255, 255, 255, .55);
  color: transparent;
}
.step--done .step__n::after {
  content: ""; position: absolute; left: 8px; top: 5px;
  width: 5px; height: 10px;
  border-right: 2px solid #fff; border-bottom: 2px solid #fff;
  transform: rotate(40deg);
}
/* A step already done DEEPENS on hover rather than lightening: the soft tint
   above is for one that has not been coloured yet, and putting it on a filled
   step reads as undoing it. */
.step--done:hover { --step-fill: var(--accent-2); color: #fff; }
.step--done:hover .step__l { color: #fff; }
.step--done:hover .step__n { border-color: rgba(255, 255, 255, .55); color: transparent; }

/* THE STEP YOU ARE ON wins over done, so it is declared last: the deep end of
   the accent, the only disc with a ring round it, the only name at 600. */
.step[aria-current="step"], .step--on {
  --step-fill: var(--accent-2); color: #fff; z-index: 6;
}
.step[aria-current="step"] .step__n, .step--on .step__n {
  background: #fff; border-color: #fff; color: var(--accent-2);
  box-shadow: 0 0 0 3px rgba(255, 255, 255, .28);
}
.step[aria-current="step"] .step__n::after,
.step--on .step__n::after { display: none; }
.step[aria-current="step"] .step__l, .step--on .step__l { color: #fff; font-weight: 600; }
.step[aria-current="step"]:hover, .step--on:hover { --step-fill: var(--accent-2); color: #fff; }
.step[aria-current="step"]:hover .step__l, .step--on:hover .step__l { color: #fff; }
.step[aria-current="step"]:hover .step__n, .step--on:hover .step__n {
  background: #fff; border-color: #fff; color: var(--accent-2); }

/* A dock magnifies; a reader who has asked for less motion gets the
   highlight and not the journey to it. */
@media (prefers-reduced-motion: reduce) {
  .step { transition: color var(--dur) var(--ease); }
  .step:hover { transform: none; }
}

/* Under a phone the names cannot ride beside the discs: the strip scrolls
   sideways inside its own box rather than squeezing five labels to nothing. */
@media (max-width: 720px) {
  .step { flex: 0 0 auto; padding: 11px 18px 11px 22px; }
  .step__l { max-width: 108px; }
}

.seg {
  display: inline-flex; gap: 2px; padding: 2px;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
}
.seg__opt {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 12px;
  font: inherit; font-size: .78rem; font-weight: 500;
  color: var(--ink-2);
  background: none; border: 0;
  border-radius: calc(var(--radius) - 2px);   /* concentric inside the 2px pad */
  text-decoration: none; white-space: nowrap; cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.seg__opt:hover { color: var(--ink); }
.seg__opt[aria-current], .seg__opt[aria-pressed="true"], .seg__opt.on { background: var(--ink); color: #fff; }

/* --------------------------------------------------------------------------
   Tables, notes, empties, pagers
   -------------------------------------------------------------------------- */

.tablewrap {
  overflow-x: auto;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
}
.table--sticky thead th { position: sticky; top: 0; background: var(--surface); z-index: 1; }

/* One line in a tint: the outcome of an act, a fact to act on. */
.note {
  padding: 10px 12px;
  border-radius: var(--radius);
  font-size: .86rem;
  background: var(--surface-2);
  color: var(--ink-2);
  animation: ui-rise var(--dur-slow) var(--ease-out) both;
}
.note--ok     { background: var(--ok-soft);     color: var(--ok); }
.note--warn   { background: var(--warn-soft);   color: var(--warn); }
.note--danger { background: var(--danger-soft); color: var(--danger); }
.note--info   { background: var(--info-soft);   color: var(--info); }

/* What nothing looks like: a sentence in a dashed box, never an empty one. */
.empty {
  padding: 18px 16px;
  border: 1px dashed var(--rule-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink-2);
  font-size: .86rem;
}
/* CENTRED, AND THE SENTENCE KEPT TO A MEASURE (2026-09-24, customer request
   on the Proposals room's empty state: "beautify this also, make it center").
   The box is as wide as the list it stands in -- a narrow box under a wide
   heading reads as a card that failed rather than as a list with nothing in
   it -- so what has to be brought back to a reading width is the text inside
   it, not the box. The button centres on the text-align and wants no auto
   margin of its own, being inline-flex. */
.empty--center { text-align: center; padding: 30px 26px; }
/* The sketch above it, at a size that says "a picture of what goes here"
   rather than competing with the real cards.

   `display: block` IS THE RULE, not decoration: this is a `<span>`, and a
   width on an inline box does nothing -- measured, the sketch filled 1,070px
   of a 1,124px box. `> svg` rather than a page's own class, so the kit names
   nothing a page owns, and it beats the 96px those sketches carry in a grid
   by being one specificity step up. */
.empty__art { display: block; width: 172px; margin: 0 auto 14px; opacity: .85; }
.empty__art > svg { display: block; width: 100%; height: auto; }
.empty__title { margin: 0 0 4px; font-weight: 600; color: var(--ink); }
.empty p { margin: 0; }
.empty p + p, .empty .btn { margin-top: 10px; }
/* AFTER `.empty p`, and that is the whole reason it is down here: the two
   selectors tie on specificity, so the later one wins, and written above it
   the auto margins were being reset to 0 and every sentence sat against the
   left edge of a centred box (measured). */
.empty--center > p { max-width: 52ch; margin-inline: auto; }
.empty--center > p + p { margin-top: 10px; }
/* Two ways forward sit on one row rather than stacking into a column of
   buttons, which reads as a form. */
.empty__acts { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;
               margin-top: 14px; }
.empty__acts .btn { margin-top: 0; }

.pager {
  display: flex; align-items: center; justify-content: center; gap: 12px;
  margin: 14px 0 0;
  font-size: .82rem; color: var(--ink-2);
}

/* --------------------------------------------------------------------------
   The server-rendered modal: a fixed dim that scrolls, a dialog centred in
   it, Close a plain link back. No script required.
   -------------------------------------------------------------------------- */

.overlay {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(9, 18, 22, .52);
  overflow: auto; overscroll-behavior: contain;
  padding: 4vh 16px 6vh;
  animation: ui-fade var(--dur-slow) var(--ease) both;
}
.dialog {
  position: relative;
  width: min(var(--dialog-w, 620px), 100%);
  margin: 0 auto;
  padding: 22px 24px 24px;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  box-shadow: 0 28px 70px -18px rgba(9, 18, 22, .5);
  color: var(--ink);
  animation: ui-rise var(--dur-slow) var(--ease-out) both;
}
.dialog--wide { --dialog-w: 1360px; }
.dialog > h2 { margin: 0 0 4px; font-size: 1.15rem; padding-right: 40px; }
.dialog__close {
  position: absolute; top: 12px; right: 14px;
  width: 30px; height: 30px;
  display: grid; place-items: center;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink-2);
  font: inherit; font-size: 1.05rem; line-height: 1;
  text-decoration: none; cursor: pointer;
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.dialog__close:hover { color: var(--ink); border-color: var(--accent); }
.dialog__acts { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 16px; }

@keyframes ui-fade { from { opacity: 0; } }
@keyframes ui-rise { from { opacity: 0; transform: translateY(8px); } }

/* --------------------------------------------------------------------------
   THE OPENER: a tool's first screen is a question and its doors
   (2026-09-24, customer request for Scoping and Proposals: "like there are
   cards first in the middle").

   The Knowledge Studio opened this way first (`.fsopen`/`.fsdoor` in
   framework-studio.css), the Builder copied its metrics (`.bsopen`/`.bsdoor`
   in builder-studio.css) because that stylesheet is not loaded there and must
   not be, and this is the SHAPE stated a third time -- so it is stated in the
   kit instead, where every console page already reads it.

   THE TWO EXISTING COPIES ARE LEFT ALONE on purpose: each pins its own page's
   grid and scrolling around the doors, and repointing them is a change to two
   working screens that nobody asked for. Point them here when either is next
   opened; until then this is the one copy a NEW opener uses, and the number
   of statements went from two-about-to-be-three to two.

   A DOOR IS A LINK, so an opener needs no script, bookmarks, and opens in a
   new tab. `aria-current` marks one that is already where you are.
   -------------------------------------------------------------------------- */
.opener { max-width: var(--opener-w); margin-inline: auto; padding: min(6vh, 44px) 4px 0; }
.opener__head { text-align: center; margin-bottom: 22px; }
.opener__eyebrow {
  margin: 0 0 6px;
  font-size: .64rem; font-weight: 600; letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ta, var(--accent));
}
.opener__h { margin: 0 0 8px; font-size: 1.45rem; line-height: 1.2; }
.opener__lede {
  margin: 0 auto;
  max-width: var(--opener-lede);
  color: var(--ink-2);
  font-size: var(--opener-lede-size); line-height: 1.5;
}
/* `auto-fit` with a real floor AND a ceiling of one third: three fill the
   row, two sit as a centred pair at the same card width, and on a phone they
   stack without a query of their own. */
.opener__doors {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, var(--opener-door)));
  justify-content: center;
  gap: var(--opener-gap);
}
@media (max-width: 600px) { .opener__doors { grid-template-columns: 1fr; } }
.opener__doors .kg__art { height: var(--opener-art-h); }
/* The sketch's ground is the BOX, not a rect inside the drawing: at this
   height the 200x84 drawing is width-limited and its own rect stopped 8px
   short of the top and bottom, so its card read as sitting lower than the
   others (customer, 2026-09-28). The other openers' sketches already paint
   their ground this way. */
.opener__doors .kg__art { background: var(--tint, #eef4f5); border-radius: var(--radius); }
.opener__doors .kg__art-bg { fill: none; }
.odoor {
  display: flex; flex-direction: column; gap: 9px;
  padding: 16px 16px 18px;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(19, 45, 55, .04);
  color: inherit; text-decoration: none;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}
.odoor:hover {
  border-color: var(--ta, var(--accent));
  box-shadow: 0 6px 18px rgba(19, 45, 55, .09);
  transform: translateY(-1px);
}
.odoor:active { transform: translateY(0); }
.odoor[aria-current] { border-color: var(--ta, var(--accent)); }
.odoor__h { font-size: 1rem; font-weight: 600; color: var(--ink); }
.odoor__p { font-size: .84rem; line-height: 1.5; color: var(--ink-2); }
/* The tag is the door's own figure, so it says what is behind it before it
   is opened; the accent form marks the one to press first. */
.odoor__tag {
  align-self: flex-start;
  margin-top: auto;
  padding: 3px 9px;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface-2, #f4f7f8);
  font-size: .7rem; font-weight: 600; letter-spacing: .03em;
  color: var(--ink-3);
}
.odoor__tag--go {
  border-color: color-mix(in srgb, var(--ta, var(--accent)) 40%, var(--rule));
  background: color-mix(in srgb, var(--ta, var(--accent)) 10%, var(--surface));
  color: var(--ink-2);
}
.opener__note {
  margin: 18px auto 0;
  max-width: 62ch;
  text-align: center;
  font-size: .8rem; line-height: 1.55; color: var(--ink-3);
}
@media (prefers-reduced-motion: reduce) {
  .odoor, .odoor:hover, .odoor:active { transition: none; transform: none; }
}

/* --------------------------------------------------------------------------
   A phone: nothing interactive smaller than a fingertip.
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {
  .btn { min-height: 44px; }
  .btn--sm { min-height: 38px; }
  .input, .select { min-height: 42px; font-size: 16px; }
}


/* =========================================================================
   THE MORE MENU (.aiu): AI use and credit, for any tool's header
   (templates/core/_ai_usage_menu.html). A native details; the panel drops
   under the button, so its header must not clip overflow.
   ========================================================================= */
.aiu { position: relative; }
.aiu__btn { list-style: none; cursor: pointer; display: grid; place-items: center; width: 30px; height: 30px;
            border-radius: var(--radius); color: var(--ink-2); transition: background var(--dur) var(--ease); }
.aiu__btn::-webkit-details-marker { display: none; }
.aiu__btn:hover, .aiu[open] .aiu__btn { background: var(--surface-2); color: var(--ink); }
.aiu__btn svg { width: 16px; height: 16px; fill: currentColor; }
.aiu__panel { position: absolute; right: 0; top: calc(100% + 6px); z-index: 40; width: 270px; text-align: left;
              padding: 12px 14px; border-radius: var(--radius); border: 1px solid var(--rule);
              background: var(--surface); box-shadow: var(--shadow-lg, 0 10px 30px rgba(15, 30, 40, .14)); }
.aiu__h { margin: 0; font-size: 10px; font-weight: 500; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-3); }
.aiu__meter { margin: 10px 0 14px; }
.aiu__meterh { display: flex; justify-content: space-between; align-items: baseline; font-size: .82rem; margin-bottom: 6px; color: var(--ink); }
.aiu__meterh span { color: var(--ink-3); font-size: .74rem; font-variant-numeric: tabular-nums; }
.aiu__bar { height: 6px; border-radius: var(--radius); background: var(--rule); overflow: hidden; }
.aiu__bar > span { display: block; height: 100%; border-radius: inherit; background: var(--accent); transition: width var(--dur-slow) var(--ease-out); }
.aiu__bar--high > span { background: var(--warn, #b07a14); }
.aiu__bar--full > span { background: var(--danger); }
.aiu__note { margin: 5px 0 0; font-size: .72rem; color: var(--ink-3); }
.aiu__left { margin: 0 0 4px; font-size: .8rem; font-weight: 600; color: var(--ink); }
.aiu__facts { margin: 8px 0 0; display: grid; gap: 6px; }
.aiu__facts > div { display: flex; justify-content: space-between; gap: 12px; font-size: .8rem; }
.aiu__facts dt { color: var(--ink-2); }
.aiu__facts dd { margin: 0; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.aiu__fine { margin: 10px 0 0; font-size: .72rem; line-height: 1.4; color: var(--ink-3); }
.aiu__act { width: 100%; margin-top: 12px; text-decoration: none; }
