/* SC console. Deliberately not the Winterfield look: this is an internal tool
   for the people running demos, not part of the fiction. Since 2026-08-01
   (console redesign, Dave's ask) it borrows its palette and header shape from
   blueprism.com/platform, measured off the live page that day: brand blue
   #0077CB, deep navy #131E58 / #0B1235, amber CTA #F5AB0A, the teal-to-purple
   signature gradient, Barlow, pill buttons. It should feel first class and
   organized even though nobody outside the SC team ever sees it. */

:root {
  --c-blue: #0077cb;        /* brand blue: primary buttons, links */
  --c-blue-deep: #005ea2;   /* hover state of the above */
  --c-sky: #00abe6;
  --c-teal: #0a9ca6;
  --c-purple: #60499e;
  --c-navy: #131e58;        /* section band, footer */
  --c-navy-deep: #0b1235;   /* darkest band */
  --c-amber: #f5ab0a;       /* the attention color: demoing, key CTAs */
  --c-ink: #17171e;
  --c-dim: #5b6472;
  --c-line: #e1e6ef;
  --c-bg: #f5f7fa;
  --c-card: #ffffff;
  --c-ok: #14803c;
  --c-error: #c62134;
  --c-gradient: linear-gradient(90deg, #0a9ca6 0%, #00abe6 33%, #60499e 66%, #30254f 100%);

  /* Five names for values this file was already using literally, promoted to
     variables on 2026-08-06 so that css/diagnostics.css could reach them when
     Platform Diagnostics moved into the console. **Every value here is one
     that already appears above or below, chosen rather than invented**, so
     nothing on any existing page moves: #fdf1f2 is the error tint under
     `p.console-error` and `.console-mutates`, and #fff8e8 / #8a5f00 are the
     amber pair `.console-envswitch.is-to-live` uses. The two font stacks are
     the ones on `body.console` and on `code`. */
  --c-error-bg:  #fdf1f2;
  --c-amber-bg:  #fff8e8;
  --c-amber-ink: #8a5f00;
  --c-sans: Barlow, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
            Helvetica, Arial, sans-serif;
  --c-mono: ui-monospace, SFMono-Regular, Menlo, monospace;

  /* The demo toast's palette, `PLAT-95`. The component is in css/toast.css,
     shared with Veranta and the portal; only these five differ per site. */
  --toast-bg: var(--c-navy);
  --toast-edge: var(--c-amber);
  --toast-edge-bad: var(--c-error);
  --toast-text: #ffffff;
  --toast-note: #c3cbe0;
}

* { box-sizing: border-box; }

body.console {
  margin: 0;
  background: var(--c-bg);
  color: var(--c-ink);
  font: 15px/1.55 Barlow, -apple-system, BlinkMacSystemFont, "Segoe UI",
        Roboto, Helvetica, Arial, sans-serif;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

a { color: var(--c-blue); text-decoration: none; }
a:hover { text-decoration: underline; }

/* No width cap, Dave's call 2026-08-01 (session 5): demos and console work
   happen on computers over Zoom/Teams, so use the width a real screen has.
   Mobile is checked for embarrassment, not designed for. The login card
   keeps its own narrow cap because a form does not benefit from 2000px. */
.console-wrap {
  margin: 0 auto;
  padding: 0 32px;
  display: flex;
  align-items: center;
  gap: 16px;
  width: 100%;
}

/* An in-page anchor has to clear the pinned header, or #comments lands with
   the heading hidden behind the bar. Belongs on the scrolling element, which
   is the root and not body.console. */
html { scroll-padding-top: 96px; }

/* ------------------------------------------------------------- header */

/* Header and footer are pinned and the page scrolls through them. Dave's ask,
   2026-08-02: the nav and the mode chip are what you reach for mid-demo, and
   on the long pages (the backlog, a 77-row ideas table, a webhook response)
   reaching for them meant scrolling back to the top first.

   Sticky rather than fixed, deliberately. Fixed would take both out of the
   flow, and then every page needs padding equal to their heights, hard-coded,
   and wrong the moment the nav wraps. Sticky keeps them as flex items of
   body.console, so the layout is unchanged and the browser does the pinning:
   the header has nowhere to scroll to and stays put, and the footer, being
   last, pins to the bottom of the viewport until the page bottom reaches it.

   The z-index only has to beat page content; a modal <dialog> renders in the
   top layer and is above both whatever this says. */
.console-topfix {
  position: sticky;
  top: 0;
  z-index: 30;
}

/* White bar, brand lockup left, nav beside it, controls right, and the
   signature gradient as a 4px rule underneath: the blueprism.com header
   shape, sized down to a tool. */
.console-topbar {
  background: #fff;
  border-bottom: 1px solid var(--c-line);
}
.console-topbar .console-wrap { min-height: 62px; }

.console-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--c-ink);
  white-space: nowrap;
}
.console-brand:hover { text-decoration: none; }
.console-brand-mark {
  width: 34px;
  height: 34px;
  border-radius: 8px;
  background: linear-gradient(135deg, var(--c-blue) 0%, var(--c-navy) 100%);
  color: #fff;
  font-weight: 700;
  font-size: 14px;
  letter-spacing: 0.02em;
  display: flex;
  align-items: center;
  justify-content: center;
}
.console-brand-word { line-height: 1.15; }
.console-brand-word strong { display: block; font-size: 16px; font-weight: 700; }
.console-brand-word span {
  display: block;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--c-dim);
}

.console-nav { display: flex; align-items: center; gap: 4px; margin-left: 12px; }
.console-nav a {
  color: var(--c-ink);
  font-size: 14.5px;
  font-weight: 500;
  padding: 6px 12px;
  border-radius: 999px;
  white-space: nowrap;
}
.console-nav a:hover { color: var(--c-blue); text-decoration: none; background: #f0f5fb; }
.console-nav a.is-current { color: #fff; background: var(--c-blue); }

/* Admin-only destinations, grouped and tinted so it is obvious at a glance
   which items a colleague signing in will not have. Dave's ask, 2026-08-02.4.
   Teal rather than a fresh green: it is already the first stop of the brand
   gradient, so the console gains a meaning for a colour it already uses rather
   than a sixth colour. The tag is aria-hidden because the group carries the
   same fact as an accessible label; a screen reader should hear it once. */
.console-nav { flex: 1; }
.console-nav-admin {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 6px;
  border: 1px solid #b7e0e3;
  background: #f1fafb;
  border-radius: 999px;
}
.console-nav-admin-tag {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--c-teal);
  padding: 0 2px 0 6px;
}
.console-nav-admin a { color: #0b6f77; }
.console-nav-admin a:hover { color: var(--c-teal); background: #ddf2f4; }
.console-nav-admin a.is-current { color: #fff; background: var(--c-teal); }

.console-controls {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 12px;
  white-space: nowrap;
}
.console-who { font-weight: 600; font-size: 14px; }
.console-signout { color: var(--c-dim); font-size: 13.5px; }

/* The dev/live switch, admins and contributors only: a quiet outline pill
   that says where you would land, not where you are. */
.console-envswitch {
  font-size: 12.5px;
  font-weight: 600;
  padding: 5px 12px;
  border: 1.5px solid var(--c-line);
  border-radius: 999px;
  color: var(--c-dim);
}
.console-envswitch:hover { border-color: var(--c-blue); color: var(--c-blue); text-decoration: none; }
.console-envswitch.is-to-live { border-color: var(--c-amber); color: #8a5f00; }
.console-envswitch.is-to-live:hover { background: #fff8e8; color: #8a5f00; }

.console-mode-chip {
  font-size: 12.5px;
  font-weight: 600;
  padding: 4px 12px;
  border-radius: 999px;
  text-transform: capitalize;
}
.console-mode-chip.is-demoing { background: var(--c-amber); color: var(--c-ink); }
.console-mode-chip.is-practicing { background: #e8ecf4; color: var(--c-dim); }

.console-rule { height: 4px; background: var(--c-gradient); }

/* The navy page band: page title and one line of purpose, hero-shaped like
   the reference page but a fraction of the height. */
.console-hero {
  background: radial-gradient(120% 200% at 85% -40%, #1d3aa6 0%, var(--c-navy) 45%, var(--c-navy-deep) 100%);
  color: #fff;
  padding: 22px 0 24px;
}
.console-hero .console-wrap { display: block; }
.console-hero h1 { margin: 0; font-size: 26px; font-weight: 600; letter-spacing: 0.01em; }
.console-hero p { margin: 4px 0 0; color: #aeb8d8; font-size: 14px; max-width: 900px; }

.console-main { flex: 1; margin: 26px auto; padding: 0 32px; width: 100%; }
.console-narrow { max-width: 460px; }

/* Signed-out pages sit on the navy instead of the gray: the login card is
   the one place the console gets to look like a product. */
body.console.is-auth {
  background: radial-gradient(110% 160% at 80% -30%, #1d3aa6 0%, var(--c-navy) 50%, var(--c-navy-deep) 100%);
}
body.console.is-auth .console-main { display: flex; align-items: center; justify-content: center; }
body.console.is-auth .console-topbar { border-bottom: 0; }
body.console.is-auth .console-footer { border-top: 0; }

.console-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(380px, 1fr));
  gap: 20px;
  align-items: start;
}
.console-wide { grid-column: 1 / -1; }

/* The dashboard's own grid: You in a fixed rail, Recent Activity taking the
   rest of a real screen's width beside it, instead of a wide blank where
   grid columns went unused. Collapses to one column when narrow. */
.console-dash { grid-template-columns: minmax(360px, 430px) minmax(0, 1fr); }
@media (max-width: 1050px) {
  .console-dash { grid-template-columns: 1fr; }
}

/* The Team page: the invite card takes the width its Pending Invites table
   needs, instead of an equal share of the grid. `console-nowrap` keeps a name
   or a date on one line (Dave's ask, 2026-08-01), so on Windows, where the UI
   font is wider than the Mac equivalent, the table outgrew its track and
   scrolled sideways inside its own card while the page had width going spare.
   Dave's report and call, 2026-08-03.6: spend the width. The track grows to
   the table and no further, and the second track keeps a floor so Roles cannot
   be crushed to make room.

   **The minimum is 0 and that is the load-bearing half.** Written first as
   `minmax(380px, max-content)`, which fixed the reported bug and introduced a
   worse one: `max-content` is a demand rather than a preference, so between
   about 1050px and the width the table actually wants, the grid grew past the
   viewport and took the whole page sideways with it. Measured at 1100px with a
   100-character invite note: the document scrolled to 1247. A zero minimum lets
   the track shrink when there is not enough room, at which point the card's own
   `overflow-x: auto` takes over and scrolls inside itself, which is the
   fallback `.console-card` already documents and the reason the page never
   scrolls sideways. */
.console-team { grid-template-columns: minmax(0, max-content) minmax(280px, 1fr); }
@media (max-width: 1050px) {
  .console-team { grid-template-columns: 1fr; }
}

.console-card {
  background: var(--c-card);
  border: 1px solid var(--c-line);
  border-radius: 12px;
  padding: 20px 24px;
  box-shadow: 0 1px 3px rgba(19, 30, 88, 0.06);
  /* A wide table scrolls inside its card; the page itself never scrolls
     sideways, which on a phone would drag the header off the screen. */
  overflow-x: auto;
}
.console-card h1 { margin: 0 0 8px; font-size: 22px; font-weight: 600; }
.console-card h2 { margin: 0 0 12px; font-size: 17px; font-weight: 600; }
.console-card h3 { margin: 20px 0 8px; font-size: 12px; font-weight: 700;
                   text-transform: uppercase; letter-spacing: 0.07em; color: var(--c-dim); }

.console-dim { color: var(--c-dim); }
.console-small { font-size: 13px; }
.console-ok { color: var(--c-ok); }
.console-error { color: var(--c-error); }
p.console-error { background: #fdf1f2; border: 1px solid #f3c6cc;
                  border-radius: 8px; padding: 9px 14px; }
p.console-ok { background: #f0fdf4; border: 1px solid #bbf7d0;
               border-radius: 8px; padding: 9px 14px; }

label { display: block; margin: 12px 0; font-weight: 600; font-size: 13px; }
label input, label select {
  display: block;
  width: 100%;
  margin-top: 4px;
  padding: 9px 12px;
  font: inherit;
  font-weight: 400;
  border: 1px solid var(--c-line);
  border-radius: 8px;
  background: #fff;
}
/* **A checkbox is not a text field, and this is the fix for a real collision
   rather than a tidy-up.** Dave's report, 2026-08-04.3: on Needs You, "Select
   all shown" and "0 selected" were drawn on top of each other. The cause was
   the rule above reaching the checkbox beside the words: `width: 100%` resolved
   to 59px against the label's own box, which left the text 53px of the 92px it
   asked for, so it wrapped to three lines and the count landed in the middle of
   it. Measured, not inferred. Every checkbox in the console sits inside a
   label, so that rule had been stretching all of them and padding and
   bordering them as well.

   Written as a reset after the rule rather than as `:not([type=checkbox])` on
   it, deliberately. The `:not()` form raises that selector from (0,0,2) to
   (0,2,2), which would have made it start beating `.activity-filters input`
   (0,1,1) and quietly restyled the whole dashboard filter bar. A separate rule
   at (0,1,2) beats `label input` and nothing else. */
label input[type="checkbox"], label input[type="radio"] {
  display: inline-block;
  width: auto;
  flex: none;
  margin-top: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
}

label input:focus { outline: 2px solid var(--c-blue); border-color: var(--c-blue); }

/* Pill buttons, the blueprism.com shape: brand blue as the default, amber
   where attention is the point, ghost for the quiet second action. */
button {
  font: inherit;
  font-weight: 600;
  font-size: 14px;
  padding: 9px 20px;
  border: 1.5px solid transparent;
  border-radius: 999px;
  background: var(--c-blue);
  color: #fff;
  cursor: pointer;
}
button:hover { background: var(--c-blue-deep); }
.console-btn-amber { background: var(--c-amber); color: var(--c-ink); }
.console-btn-amber:hover { background: #e09c00; }
.console-btn-ghost { background: transparent; border-color: var(--c-line); color: var(--c-ink); }
.console-btn-ghost:hover { background: #f0f5fb; border-color: var(--c-blue); color: var(--c-blue); }
.console-btn-small { font-size: 12.5px; padding: 4px 12px; }

/* An <a> that has to look like the primary button: the ideas page's Submit an
   Idea opener is a real link to a real URL, so it works before its script
   runs, and a link is the one thing that cannot be a <button>. */
a.console-btn-link {
  display: inline-block;
  font-weight: 600;
  font-size: 14px;
  padding: 9px 20px;
  border: 1.5px solid transparent;
  border-radius: 999px;
  background: var(--c-blue);
  color: #fff;
  white-space: nowrap;
}
a.console-btn-link:hover { background: var(--c-blue-deep); color: #fff; text-decoration: none; }

.console-linkish {
  background: none;
  color: var(--c-blue);
  padding: 0;
  border: 0;
  font-weight: 500;
  text-decoration: underline;
  font-size: 13px;
}
.console-linkish:hover { background: none; color: var(--c-blue-deep); }

/* Practicing / Demoing: a segmented pair. The current mode is solid, and
   demoing is solid amber because it is the state worth noticing. The
   audience field sits beside the Demoing button (Dave's design: set who is
   watching in the same gesture as going on stage). */
.console-mode-form { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.console-mode-pick { background: #fff; color: var(--c-ink); border-color: var(--c-line); }
.console-mode-pick:hover { background: #f0f5fb; }
.console-mode-pick.is-current { cursor: default; }
.console-mode-pick.is-current[value="practicing"],
.console-mode-pick.is-current.is-practicing { background: var(--c-navy); border-color: var(--c-navy); color: #fff; }
.console-mode-pick.is-current[value="demoing"],
.console-mode-pick.is-current.is-demoing { background: var(--c-amber); border-color: var(--c-amber); color: var(--c-ink); }
.console-audience-input {
  font: inherit;
  padding: 8px 12px;
  border: 1px solid var(--c-line);
  border-radius: 999px;
  width: 200px;
}
.console-audience-input:focus { outline: 2px solid var(--c-blue); border-color: var(--c-blue); }

/* A freshly minted invite is one panel holding its own message and its link,
   with real space beneath it. Dave's report, 2026-08-03.6: the link and its
   Copy button ran straight into the "Who Is It For" field under them, so the
   two read as one control and it was not obvious which box was the new invite
   and which was the form for the next one. The panel edge is the barrier; the
   green is the same pair `p.console-ok` already uses, moved out to the
   container so the message inside it stops being a second box. */
.console-minted {
  background: #f0fdf4;
  border: 1px solid #bbf7d0;
  border-radius: 10px;
  padding: 12px 14px;
  margin: 0 0 22px;
}
.console-minted p.console-ok { background: none; border: 0; padding: 0; margin: 0 0 10px; }
.console-minted .console-copyrow { margin-bottom: 0; }

.console-copyrow { display: flex; gap: 8px; margin-bottom: 10px; }
.console-copy { flex: 1; min-width: 0; padding: 8px 12px; font: 13px/1.4 ui-monospace,
                SFMono-Regular, Menlo, monospace; border: 1px solid var(--c-line);
                border-radius: 8px; }

/* Tables of short identifying facts (the team, pending invites): a wrapped
   name or date reads worse than a card that scrolls. Dave's ask, 2026-08-01.
   Not applied to Recent Activity, whose detail column is prose and wraps. */
.console-nowrap th, .console-nowrap td { white-space: nowrap; }
/* The same on one cell rather than a whole table, for a table that is mostly
   prose with an identifier or a count in it. Added session 7b, where the code
   column was wrapping IDEA-074 onto two lines. */
th.console-nowrap, td.console-nowrap, span.console-nowrap { white-space: nowrap; }

/* The Team gets two columns of the grid when the screen has them. Dave's
   ask, 2026-08-01: prefer width to a scrollbar; the scrollbar is the
   fallback for when even two columns are not enough, not the design. */
@media (min-width: 1200px) {
  .console-span2 { grid-column: span 2; }
}

.console-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.console-table th { text-align: left; color: var(--c-dim); font-weight: 600;
                    font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.06em;
                    padding: 6px 12px 6px 0; border-bottom: 2px solid var(--c-line); }
.console-table td { padding: 8px 12px 8px 0; border-bottom: 1px solid var(--c-line);
                    vertical-align: top; }
.console-table tr:last-child td { border-bottom: 0; }
.console-table tbody tr:hover td, .console-table tr:hover td { background: #f8fafd; }
.console-table form { margin: 0; }

code { font: 13px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
       background: #eef2f7; padding: 2px 6px; border-radius: 6px; }

/* Pinned to the bottom of the viewport and scrolled through, the other half of
   the sticky chrome. See .console-topfix for why sticky rather than fixed.
   It is opaque navy, so content passing under it disappears cleanly rather
   than showing through. */
.console-footer {
  background: var(--c-navy-deep);
  color: #8d97b8;
  font-size: 13px;
  padding: 16px 0;
  margin-top: 34px;
  position: sticky;
  bottom: 0;
  z-index: 20;
}
/* The footer's links sit right, as a group. margin-left: auto belongs to the
   first one only: on all of them, each would push off the one before it and
   the pair would end up at opposite ends of a 2000px footer. */
.console-footer a { color: #aeb8d8; }
.console-footer a:first-of-type { margin-left: auto; }
body.console.is-auth .console-footer { background: transparent; color: #8d97b8; }

/* Stories, actions, and overrides, session 6. */

select {
  font: inherit;
  padding: 8px 12px;
  border: 1px solid var(--c-line);
  border-radius: 8px;
  background: #fff;
}
select:focus { outline: 2px solid var(--c-blue); border-color: var(--c-blue); }

.console-story-form { display: flex; gap: 8px; align-items: center; }
.console-story-form select { flex: 1; min-width: 0; }

/* Section headings between card grids. */
.console-section {
  margin: 30px 0 4px;
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--c-navy);
}
.console-section:first-child { margin-top: 0; }
.console-section-sub { margin: 0 0 14px; max-width: 900px; }

.console-mutates {
  font-size: 11px;
  font-weight: 600;
  vertical-align: middle;
  margin-left: 6px;
  padding: 2px 10px;
  border-radius: 999px;
  background: #fdf1f2;
  color: var(--c-error);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.console-break { word-break: break-all; }

/* Inline row edit (story, audience, notes on an activity row; user rows on
   the Team page): closed it is one quiet word, open it is a small form. */
.console-rowedit { display: inline-block; margin-left: 6px; font-size: 12px; }
.console-rowedit summary {
  color: var(--c-blue);
  cursor: pointer;
  list-style: none;
  display: inline;
}
.console-rowedit summary::-webkit-details-marker { display: none; }
.console-rowedit form { display: flex; gap: 6px; margin-top: 6px; flex-wrap: wrap; align-items: center; }
.console-rowedit select, .console-rowedit input { font-size: 12px; padding: 4px 8px;
  border: 1px solid var(--c-line); border-radius: 6px; }
.console-rowedit button { font-size: 12px; padding: 4px 12px; }

/* The way through to event.php from an activity row (PLAT-84). On its own
   line under the action's own scalars, because the two say different things
   about the same run and running them together reads as one sentence. */
.console-detaillink { display: block; margin-top: 3px; white-space: nowrap; }

/* Bulk edit on Recent Activity: the checkbox column plus a toolbar that
   applies a story and an audience to every checked row at once (Dave's
   design: never make them do it row by row). */
.console-bulkbar {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
  background: #f0f5fb;
  border: 1px solid var(--c-line);
  border-radius: 10px;
  padding: 10px 14px;
  margin: 0 0 14px;
  font-size: 13px;
}
.console-bulkbar select, .console-bulkbar input[type="text"] {
  font-size: 13px;
  padding: 6px 10px;
}
.console-bulkbar .console-bulkcount { font-weight: 600; color: var(--c-navy); min-width: 90px; }
.console-checkcol { width: 28px; }

/* Admin edit forms on registry cards and the idea page: hidden behind a
   details toggle so the reading view stays a reading view. */
.console-adminedit { margin-top: 14px; border-top: 1px dashed var(--c-line); padding-top: 10px; }
.console-adminedit summary { color: var(--c-blue); cursor: pointer; font-size: 13px; font-weight: 600; }
.console-adminedit textarea, .console-adminedit input[type="text"] {
  display: block;
  width: 100%;
  margin-top: 4px;
  padding: 8px 10px;
  font: inherit;
  /* After `font: inherit` the weight comes from the enclosing <label>, which
     is 600, so every value in these forms rendered bold. Restated here rather
     than by dropping the shorthand, because the shorthand is what pulls the
     Barlow stack into a textarea. Same on every .reg- form below. */
  font-weight: 400;
  font-size: 13.5px;
  border: 1px solid var(--c-line);
  border-radius: 8px;
  resize: vertical;
}

/* Role chips on the Team page. */
.console-role {
  font-size: 11.5px;
  font-weight: 700;
  padding: 2px 10px;
  border-radius: 999px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.console-role.is-admin { background: var(--c-navy); color: #fff; }
.console-role.is-contributor { background: #e6f2fb; color: var(--c-blue-deep); }
.console-role.is-sc { background: #e8ecf4; color: var(--c-dim); }

/* The webhook tester. The response pane expects big JSON and renders it in a
   scrollable well; telemetry reads as a stat row above it. */

/* Two panes with a divider the SC drags, replacing the dashboard's fixed
   rail here (Dave's ask #2, 2026-08-02: the left pane was too narrow to read
   built JSON in). The JS owns --wh-split and remembers it per browser; this
   50% is only what shows before the JS runs, and is also the whole layout if
   the JS never runs. minmax(0, ...) on both sides so a long unbroken JSON
   line scrolls inside its own well instead of widening the column. */
.console-split {
  display: grid;
  grid-template-columns: minmax(0, var(--wh-split, 50%)) 14px minmax(0, 1fr);
  gap: 0 6px;
  align-items: start;
}
.console-splitbar {
  align-self: stretch;
  min-height: 260px;
  border-radius: 999px;
  background: transparent;
  cursor: col-resize;
  position: relative;
  touch-action: none;
}
/* The grabbable line is drawn thin and the hit area left wide: a 2px target
   is a fight, and a 14px one that looks 2px is not. Both parts are drawn in a
   mid gray rather than --c-line, which is a card border color and disappears
   against --c-bg; a divider nobody can see is a divider nobody drags. */
.console-splitbar::before {
  content: "";
  position: absolute;
  top: 0; bottom: 0; left: 50%;
  width: 2px;
  margin-left: -1px;
  border-radius: 999px;
  background: #c6cede;
}
.console-splitbar:hover::before,
.console-splitbar:focus::before { background: var(--c-blue); width: 4px; margin-left: -2px; }
.console-splitbar:focus { outline: none; }
/* A pill at the midpoint, so it reads as a handle rather than a border. */
.console-splitbar::after {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 8px; height: 40px;
  margin: -20px 0 0 -4px;
  border-radius: 999px;
  background: #a8b3c9;
  border: 1px solid #f5f7fa;
}
.console-splitbar:hover::after,
.console-splitbar:focus::after { background: var(--c-blue); }
/* While dragging, the pointer owns the page: no text selection anywhere, and
   the resize cursor everywhere rather than only over the 14px bar. */
body.is-splitting { user-select: none; cursor: col-resize; }
@media (max-width: 1050px) {
  .console-split { grid-template-columns: 1fr; gap: 20px; }
  .console-splitbar { display: none; }
}

/* The in-flight state: shown from the moment Send is clicked until the
   response replaces the page. The spinner says "moving", the elapsed counter
   beside it says "and this is how long for", which is the pair that stops a
   45-second sync webhook reading as a hang. */
.wh-waiting-line { display: flex; align-items: center; gap: 10px; margin: 0 0 4px; }
.wh-spinner {
  width: 16px; height: 16px; flex: none;
  border: 2px solid var(--c-line);
  border-top-color: var(--c-blue);
  border-radius: 50%;
  animation: wh-spin 0.7s linear infinite;
}
@keyframes wh-spin { to { transform: rotate(360deg); } }
/* Respect a reduced-motion preference: the counter alone still carries the
   message, so the spinner can simply stop rather than needing a substitute. */
@media (prefers-reduced-motion: reduce) {
  .wh-spinner { animation: none; border-top-color: var(--c-line); }
}
button:disabled { opacity: 0.6; cursor: default; }

/* Method beside the URL, not above it: the method is a two-word choice and
   the URL needs every pixel of the row. */
.console-methodrow { display: flex; gap: 12px; align-items: flex-end; }
.console-methodrow label { flex: 1; min-width: 0; }
.console-methodrow .console-methodpick { flex: 0 0 110px; }

/* The client id and secret. Hidden, not dimmed, while WorkHQ owns
   authentication: they were dimmed at first so it stayed visible that another
   way exists, and Dave overruled that on 2026-08-02 because three dead fields
   cost more screen than the hint was worth. The fields stay disabled as well
   as hidden, so a secret typed before the box was ticked cannot post. */
.console-creds { margin: 4px 0 8px; }
.console-creds.is-off { display: none; }

/* Build and Send repeated beside the pane's heading, so a long body does not
   have to be scrolled past to send it. */
.console-cardhead {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 14px; flex-wrap: wrap;
}
.console-cardhead-actions { display: flex; gap: 8px; flex: none; }

/* Saved calls, PLAT-51. The picker and its two verbs on one line above the
   method row, so the first thing on the pane is "which call is this". */
.console-savedrow { display: flex; gap: 8px; align-items: flex-end; margin-bottom: 4px; }
.console-savedrow .console-savedpick { flex: 1; min-width: 0; max-width: 460px; }
.console-savedrow button { margin-bottom: 12px; }

/* The name and description, revealed once there is a URL to name. Dimmed
   background rather than a card, because it belongs to the URL above it. */
.console-nameblock {
  background: #f0f5fb;
  border: 1px solid var(--c-line);
  border-radius: 10px;
  padding: 2px 14px 10px;
  margin: 10px 0 4px;
}
.console-nameblock.is-off { display: none; }
.console-nameblock p { margin: 4px 0 0; }
.console-namerow { display: flex; gap: 10px; align-items: flex-end; }
.console-namerow label { flex: 1; min-width: 0; }
.console-namerow button { margin-bottom: 12px; flex: none; }
@media (max-width: 1050px) {
  .console-namerow { flex-wrap: wrap; }
}

/* The one highlighted sentence on the page: the contract a workflow author
   has to satisfy, which was reading as a footnote inside a gray paragraph.
   Dave's ask, 2026-08-02.2. Amber because that is this console's attention
   color, and used exactly once so it keeps meaning something. */
.console-mark {
  background: #fff3d1;
  color: var(--c-ink);
  box-shadow: inset 0 -2px 0 var(--c-amber);
  padding: 0 3px;
  border-radius: 2px;
  font-weight: 600;
}

.console-kvrow { display: flex; gap: 8px; margin-bottom: 8px; }
.console-kvrow input { flex: 1; min-width: 0; padding: 8px 10px; font: 13px/1.4 ui-monospace,
  SFMono-Regular, Menlo, monospace; border: 1px solid var(--c-line); border-radius: 8px; }
.console-kvrow .console-kv-key { flex: 0 0 220px; }
.console-json {
  background: var(--c-navy-deep);
  color: #d7e3f4;
  border-radius: 10px;
  padding: 14px 16px;
  font: 12.5px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  overflow: auto;
  max-height: 480px;
  white-space: pre;
}
.console-json .j-key { color: #7fc8f5; }
.console-json .j-str { color: #a5d6a7; }
.console-json .j-num { color: #f5ab5e; }
.console-json .j-lit { color: #ce93d8; }
/* ---------- click to copy ----------
   The FHIR diagnostics console's control, in the console's palette (Dave's
   ask, 2026-08-02.2: "the one we use"). The reasoning that shaped it there
   applies unchanged here and is worth keeping next to the code:

   **The icon belongs to the block under the pointer and nothing else.** It
   first shipped visible on every block at once, on the argument that a touch
   screen has no hover and an affordance nobody can discover is not one. Dave
   read that on 2026-07-30 and it did not survive contact: a column of dim
   icons is noise and stops pointing at anything. The touch half of the
   argument was right, so `@media (hover: hover)` scopes the reveal to devices
   that can hover and a touch screen still gets every icon, permanently. */

.copyable { position: relative; }
.console-copyblock { margin: 0 0 10px; }
/* Room for the button so it never sits on the first line of JSON. */
.console-copyblock .console-json { padding-right: 52px; }

.copy-btn {
  position: absolute;
  top: 8px;
  right: 10px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: #8fa2c4;
  opacity: .55;
  padding: 3px 6px;
  line-height: 0;
  cursor: pointer;
  transition: opacity .12s, color .12s, background .12s, border-color .12s;
}
@media (hover: hover) {
  .copy-btn { opacity: 0; }
  /* focus-within, not just :focus, so tabbing to the button reveals it while
     the pointer is somewhere else entirely. */
  .copyable:hover .copy-btn,
  .copyable:focus-within .copy-btn { opacity: 1; }
}
.copy-btn:hover {
  opacity: 1;
  color: #fff;
  background: rgba(255, 255, 255, .12);
  border-color: rgba(255, 255, 255, .28);
}
.copy-btn:focus-visible {
  opacity: 1;
  outline: 2px solid var(--c-sky);
  outline-offset: 1px;
}

/* The whole well is the click target once the script is running. Text
   selection still wins; the handler ignores a click that ends a selection. */
.js-copy .copyable { cursor: pointer; }

.copy-ok, .copy-said, .copy-said-failed { display: none; }
.copy-said, .copy-said-failed {
  font: 700 10px Barlow, sans-serif;
  letter-spacing: .06em;
  text-transform: uppercase;
  line-height: 1;
}
.copyable.is-copied .copy-btn {
  opacity: 1;
  color: #fff;
  background: rgba(20, 128, 60, .85);
  border-color: rgba(255, 255, 255, .3);
}
.copyable.is-copied .copy-i { display: none; }
.copyable.is-copied .copy-ok,
.copyable.is-copied .copy-said { display: inline-block; }
/* A refused clipboard write says so rather than lying. It said "Copied" in
   red on the diagnostics console until 2026-07-30, which is the worst of both:
   the reader walks away believing they hold a value they do not. */
.copyable.is-failed .copy-btn {
  opacity: 1;
  color: #fff;
  background: rgba(198, 33, 52, .9);
  border-color: rgba(255, 255, 255, .3);
}
.copyable.is-failed .copy-said-failed { display: inline-block; }

/* On a light surface (the backlog page's detail dialog) the same button needs
   the light palette instead. */
.copyable.is-light .copy-btn { color: var(--c-dim); }
.copyable.is-light .copy-btn:hover {
  color: var(--c-blue);
  background: #f0f5fb;
  border-color: var(--c-line);
}

.console-stats { display: flex; gap: 14px; flex-wrap: wrap; margin: 12px 0; }
.console-stat {
  background: #f0f5fb;
  border: 1px solid var(--c-line);
  border-radius: 10px;
  padding: 8px 16px;
  min-width: 110px;
}
.console-stat .s-label { display: block; font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--c-dim); }
.console-stat .s-value { font-size: 17px; font-weight: 600; color: var(--c-navy); }
.console-stat.is-bad .s-value { color: var(--c-error); }
.console-stat.is-good .s-value { color: var(--c-ok); }

/* The ideas module, session 7b. */

/* Textareas match the inputs above; the submit form and every comment box is
   one, and an unstyled textarea in a styled form is the thing people notice. */
label textarea {
  display: block;
  width: 100%;
  margin-top: 4px;
  padding: 9px 12px;
  font: inherit;
  font-weight: 400;
  border: 1px solid var(--c-line);
  border-radius: 8px;
  resize: vertical;
}
label textarea:focus { outline: 2px solid var(--c-blue); border-color: var(--c-blue); }

/* A chip is a label and a filter at the same time: sites, functions and
   statuses all appear on a row and all narrow the table when clicked. */
.idea-chip {
  display: inline-block;
  font-size: 12px;
  line-height: 1.6;
  padding: 1px 10px;
  margin: 1px 4px 1px 0;
  border-radius: 999px;
  background: #e8ecf4;
  color: var(--c-ink);
  text-decoration: none;
  white-space: nowrap;
}
a.idea-chip:hover { background: #d7e2f2; text-decoration: none; }
.idea-chip.is-on { background: var(--c-blue); color: #fff; }
/* A facet value that would show nothing, dimmed rather than removed: the
   vocabulary is part of what this page teaches, so the empty ones still say
   what exists to file an idea under. */
.idea-chip.is-empty { opacity: 0.45; }
.idea-count { opacity: 0.65; font-variant-numeric: tabular-nums; }

.idea-chip.is-new { background: #fff7ed; color: #9a3412; }
.idea-chip.is-status-new { background: #fff7ed; color: #9a3412; }
.idea-chip.is-status-approved,
.idea-chip.is-status-built { background: #f0fdf4; color: var(--c-ok); }
.idea-chip.is-status-rejected,
.idea-chip.is-status-retired { background: #fdf1f2; color: var(--c-error); }
.idea-chip.is-status-waiting-on-tech { background: #fefce8; color: #854d0e; }

.idea-filters { margin-bottom: 20px; }
.idea-search { display: flex; gap: 8px; align-items: center; margin-bottom: 12px; }
.idea-search input[type="search"] {
  flex: 1;
  min-width: 0;
  max-width: 420px;
  padding: 8px 12px;
  font: inherit;
  border: 1px solid var(--c-line);
  border-radius: 999px;
}
.idea-facet { display: flex; align-items: baseline; gap: 8px; padding: 3px 0; }
.idea-facet-label {
  flex: 0 0 84px;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--c-dim);
}

/* The page heading and the Submit an Idea opener on one line, so the primary
   action is above the filters rather than below a 77-row table. */
.idea-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
}
.idea-head-text { min-width: 0; }
.idea-head .console-btn-link { flex: none; margin-top: 2px; }
@media (max-width: 720px) {
  .idea-head { flex-wrap: wrap; }
}

/* Sortable headers, the same control as the backlog view's: the whole label
   is the link and the arrow says which column is sorting and which way. Only
   Code, Submitted and Votes carry one. Dave's call, 2026-08-02: sorting by
   Sites or Functions answers no question anybody has, and a header that looks
   clickable and sorts by nothing useful is worse than a plain one. */
.idea-sort { color: var(--c-dim); }
.idea-sort:hover { color: var(--c-blue); text-decoration: none; }
.idea-sort.is-on { color: var(--c-navy); }

.idea-table td { vertical-align: top; }
.idea-chips { max-width: 260px; }
.idea-actions { display: flex; gap: 6px; align-items: flex-start; }

/* ---------- the fresh block ----------
   Anything submitted or fleshed in the last 30 days (Dave's ask, 2026-08-02).
   Amber, because it is this console's attention color and the meaning here is
   the same one it carries on the demoing chip: look at this one. Kept to a
   tint and a left edge rather than a filled row, because a table where six
   rows shout is a table with no emphasis in it. */
.idea-table tr.is-fresh td { background: #fffaf0; }
.idea-table tr.is-fresh:hover td { background: #fff4e0; }
.idea-table tr.is-fresh td:first-child { box-shadow: inset 3px 0 0 var(--c-amber); }
.idea-fresh {
  display: inline-block;
  font-size: 10.5px;
  font-weight: 700;
  line-height: 1.6;
  padding: 0 8px;
  border-radius: 999px;
  background: var(--c-amber);
  color: var(--c-ink);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  vertical-align: 1px;
}

/* ---------- Flesh Out ----------
   Admin only, on ideas still at status new. It is a .copyable, so lib.php's
   delegated click-to-copy handler drives it and there is no new script; the
   label swap rides on the same .is-copied / .is-failed classes that handler
   sets, which is why a refused clipboard write can say so instead of lying
   about having copied. */
.idea-flesh {
  background: #fff;
  color: var(--c-blue);
  border: 1.5px solid var(--c-line);
  font-size: 13px;
  padding: 4px 14px;
  white-space: nowrap;
}
.idea-flesh:hover { background: #f0f5fb; border-color: var(--c-blue); }
.idea-flesh.is-copied { background: var(--c-ok); border-color: var(--c-ok); color: #fff; }
.idea-flesh.is-failed { background: var(--c-error); border-color: var(--c-error); color: #fff; }
.idea-flesh-done, .idea-flesh-failed { display: none; }
.idea-flesh.is-copied .idea-flesh-idle,
.idea-flesh.is-failed .idea-flesh-idle { display: none; }
.idea-flesh.is-copied .idea-flesh-done { display: inline; }
.idea-flesh.is-failed .idea-flesh-failed { display: inline; }

/* ---------- the submit panel ----------
   Same dialog mechanics as the backlog view's detail panel, including the
   reason the display property goes on [open] and nowhere else: the browser's
   own `dialog:not([open]) { display: none }` wins the specificity tie
   otherwise and the closed panel sits visible in the page.

   No fixed height here, unlike that one. This dialog's content is a short
   form of known size, so it should be as tall as the form and no taller. */
.idea-dialog {
  width: min(680px, 92vw);
  max-width: none;
  max-height: 88vh;
  padding: 0;
  border: 0;
  border-radius: 12px;
  background: var(--c-card);
  color: var(--c-ink);
  font: 15px/1.55 Barlow, -apple-system, BlinkMacSystemFont, sans-serif;
  box-shadow: 0 24px 70px rgba(11, 18, 53, 0.34);
  overflow: hidden;
}
.idea-dialog[open] { display: flex; flex-direction: column; }
.idea-dialog::backdrop { background: rgba(11, 18, 53, 0.5); }
.idea-dialog-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 16px 24px 14px;
  border-bottom: 1px solid var(--c-line);
  background: var(--c-navy);
  color: #fff;
}
.idea-dialog-head h2 { margin: 0; font-size: 19px; font-weight: 600; letter-spacing: 0.02em; }
.idea-dialog-head button { background: transparent; border-color: rgba(255,255,255,.4); color: #fff; }
.idea-dialog-head button:hover { background: rgba(255,255,255,.14); border-color: #fff; color: #fff; }
.idea-dialog-body { overflow-y: auto; padding: 4px 24px 24px; flex: 1 1 auto; min-height: 0; }
.idea-dialog-body p:first-child { margin-top: 14px; }
/* Without a script the dialog is rendered `open` and is an ordinary block in
   the page flow, which is the whole no-JavaScript fallback. Give it the card
   treatment there so it does not read as a form that escaped its container. */
.idea-dialog[open] {
  margin: 20px auto;
  border: 1px solid var(--c-line);
}
.idea-dialog[open]:modal { margin: auto; border: 0; }
.idea-vote { background: #e8ecf4; color: var(--c-ink); font-size: 13px; padding: 4px 14px; }
.idea-vote:hover { background: #d7e2f2; }
.idea-vote.is-mine { background: var(--c-blue); color: #fff; }
.idea-vote.is-mine:hover { background: var(--c-blue-deep); }

/* The detail page reads like a document, so it gets a measure. Everything
   else in this console is a dashboard and deliberately does not. */
.idea-detail p, .idea-detail ul, .idea-detail ol { max-width: 78ch; }
.idea-detail h1 { margin-bottom: 4px; }
.idea-meta { display: flex; flex-wrap: wrap; gap: 4px 20px; font-size: 13px; }
.idea-meta span { color: var(--c-dim); }
.idea-voterow { display: flex; align-items: center; gap: 10px; margin: 14px 0; }
.idea-voterow form { margin: 0; }
.idea-note { border-left: 3px solid var(--c-line); padding-left: 12px; }
.idea-list { padding-left: 22px; }
.idea-list li { margin: 4px 0; }
.idea-sources { padding-left: 22px; }
.idea-sources li { margin: 4px 0; }

.idea-comments { list-style: none; margin: 0 0 18px; padding: 0; }
.idea-comments li {
  border-top: 1px solid var(--c-line);
  padding: 10px 0 2px;
  max-width: 78ch;
}
.idea-comments p:first-child { margin: 0 0 4px; }
.idea-inline-form { display: inline; margin-left: 8px; }

/* ---------------------------------------------------- the backlog view
   PLAT-53. Built to be read across a meeting room rather than alone: the
   table is the conversation and everything that would make it a document
   (the argument, the blast radius, the rejected decisions) is one click
   away in a dialog. Same chip vocabulary as the ideas module, its own
   class names, because the two lists share a look and nothing else. */

.bl-filters { margin-bottom: 20px; }
.bl-search { display: flex; gap: 8px; align-items: center; margin-bottom: 12px; }
.bl-search input[type="search"] {
  flex: 1;
  min-width: 0;
  max-width: 520px;
  padding: 8px 14px;
  font: inherit;
  border: 1px solid var(--c-line);
  border-radius: 999px;
}
.bl-search input[type="search"]:focus { outline: 2px solid var(--c-blue); border-color: var(--c-blue); }
.bl-facet { display: flex; align-items: baseline; gap: 8px; padding: 3px 0; flex-wrap: wrap; }
.bl-facet-label {
  flex: 0 0 74px;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--c-dim);
}
.bl-chip {
  display: inline-block;
  font-size: 12.5px;
  line-height: 1.7;
  padding: 1px 11px;
  border-radius: 999px;
  background: #e8ecf4;
  color: var(--c-ink);
  white-space: nowrap;
}
a.bl-chip:hover { background: #d7e2f2; text-decoration: none; }
.bl-chip.is-on { background: var(--c-blue); color: #fff; }
/* A facet value that would show nothing is dimmed rather than removed: the
   vocabulary is part of what the page says, and "no P1s left" is an answer. */
.bl-chip.is-empty { opacity: 0.45; }
.bl-count { opacity: 0.62; font-variant-numeric: tabular-nums; }

/* Sortable headers: the whole label is the link, and the arrow says which
   column is doing the sorting and which way. */
.bl-sort { color: var(--c-dim); }
.bl-sort:hover { color: var(--c-blue); text-decoration: none; }
.bl-sort.is-on { color: var(--c-navy); }

.bl-table td { vertical-align: top; }
/* The row is the button. Nothing else on it is clickable except the ID
   cross-references inside the item text, which the handler lets through. */
.bl-row { cursor: pointer; }
.bl-row:focus-visible { outline: 2px solid var(--c-blue); outline-offset: -2px; }
.bl-item { line-height: 1.5; }
.bl-item code { font-size: 12px; }

.bl-pri, .bl-status, .bl-live {
  display: inline-block;
  font-size: 11.5px;
  font-weight: 700;
  padding: 1px 9px;
  border-radius: 999px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* Severity reads at a glance or it is not doing its job: P1 is the console's
   error red, P2 the brand blue, P3 the quiet gray. */
.bl-pri.is-p1 { background: #fdf1f2; color: var(--c-error); }
.bl-pri.is-p2 { background: #e6f2fb; color: var(--c-blue-deep); }
.bl-pri.is-p3 { background: #e8ecf4; color: var(--c-dim); }
.bl-chip.is-pri-p1.is-on { background: var(--c-error); }
.bl-status.is-open { background: #fff7ed; color: #9a3412; }
.bl-status.is-in-progress { background: var(--c-amber); color: var(--c-ink); }
.bl-status.is-done { background: #f0fdf4; color: var(--c-ok); }
.bl-status.is-dropped { background: #e8ecf4; color: var(--c-dim); }
.bl-live { background: var(--c-amber); color: var(--c-ink); }

.bl-notecount, .bl-follow {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--c-dim);
  background: #f0f5fb;
  border: 1px solid var(--c-line);
  border-radius: 999px;
  padding: 0 8px;
  margin-left: 4px;
}
.bl-follow { color: #8a5f00; background: #fff8e8; border-color: #f0dcae; }

/* ---------- the detail dialog ----------
   The display property goes on [open] and nowhere else, or the browser's own
   `dialog:not([open]) { display: none }` loses the specificity tie and the
   closed panel sits visible at the top of the page. The diagnostics console
   met that one on 2026-07-30; it is written down there too.

   Definite height for the same reason it has one there: with height auto, a
   flex body with a zero basis contributes nothing to the container's
   intrinsic height and some browsers collapse the whole dialog to its
   header. */
.bl-dialog {
  width: min(1000px, 92vw);
  max-width: none;
  height: min(86vh, 940px);
  max-height: 88vh;
  padding: 0;
  border: 0;
  border-radius: 12px;
  background: var(--c-card);
  color: var(--c-ink);
  font: 15px/1.55 Barlow, -apple-system, BlinkMacSystemFont, sans-serif;
  box-shadow: 0 24px 70px rgba(11, 18, 53, 0.34);
  overflow: hidden;
}
.bl-dialog[open] { display: flex; flex-direction: column; }
.bl-dialog::backdrop { background: rgba(11, 18, 53, 0.5); }
.bl-dialog-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 16px 24px 14px;
  border-bottom: 1px solid var(--c-line);
  background: var(--c-navy);
  color: #fff;
}
.bl-dialog-head h2 { margin: 0; font-size: 19px; font-weight: 600; letter-spacing: 0.02em; }
.bl-dialog-head button { background: transparent; border-color: rgba(255,255,255,.4); color: #fff; }
.bl-dialog-head button:hover { background: rgba(255,255,255,.14); border-color: #fff; color: #fff; }
/* min-height: 0 is what lets a flex item scroll instead of growing past its
   parent; without it the automatic minimum is the content size. */
.bl-dialog-body { overflow-y: auto; padding: 18px 24px 24px; flex: 1 1 auto; min-height: 0; }
.bl-dialog-body:focus { outline: none; }
.bl-dialog-body h4 {
  margin: 20px 0 6px;
  font-size: 11.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--c-dim);
}
.bl-dialog-body h4:first-of-type { margin-top: 14px; }
/* The dialog is the one place in this console that reads like a document, so
   it gets a measure. The table behind it deliberately does not. */
.bl-d-prose p { margin: 0 0 10px; max-width: 82ch; }
.bl-d-meta { display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: center;
             margin: 0; font-size: 13px; color: var(--c-dim); }
.bl-note { border-left: 3px solid var(--c-line); padding: 2px 0 2px 14px; margin: 0 0 14px; }
.bl-note-from {
  margin: 0 0 4px;
  font-size: 11.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--c-dim);
}
.bl-d-source { border-top: 1px solid var(--c-line); padding-top: 10px; margin-top: 20px; }

/* ================================================================ registry
   Stories & Actions, relaid out 2026-08-03 (PLAT-65, Dave's ask). The page's
   own reasoning is in console/registry.php; this is what the shape needs.

   Prefixed .reg- for the same reason the backlog view is .bl-: a page with a
   layout of its own gets its own namespace, so a later change here cannot
   reach the dashboard by accident. */

/* Rail and pane. The rail is a fixed column because a list you scan wants a
   stable left edge; the pane takes the rest, and on this console that is as
   much as the screen has (no width cap, Dave's call 2026-08-01). Below
   1100px they stack, rail first, which is the reading order anyway. */
.reg-layout {
  display: grid;
  grid-template-columns: minmax(260px, 320px) minmax(0, 1fr);
  gap: 24px;
  align-items: start;
}
@media (max-width: 1100px) {
  .reg-layout { grid-template-columns: minmax(0, 1fr); }
  .reg-rail { position: static; max-height: none; }
}

/* Sticky under the pinned header: 67px of chrome (62px bar + 4px gradient
   rule + 1px border) plus breathing room. A long registry scrolls inside the
   rail rather than taking the page with it, so the detail pane stays put
   while you look for something. */
.reg-rail {
  position: sticky;
  top: 82px;
  max-height: calc(100vh - 104px);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--c-card);
  border: 1px solid var(--c-line);
  border-radius: 12px;
  padding: 14px;
  box-shadow: 0 1px 2px rgba(19, 30, 88, 0.05);
}

.reg-search { margin-bottom: 14px; }
.reg-search input {
  width: 100%;
  font: inherit;
  font-size: 13.5px;
  padding: 8px 12px;
  border: 1px solid var(--c-line);
  border-radius: 8px;
  background: #fff;
}
.reg-search input:focus { outline: 2px solid var(--c-blue); border-color: var(--c-blue); }

.reg-group + .reg-group { margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--c-line); }
.reg-group-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.reg-group-head h2 {
  margin: 0;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--c-navy);
}
.reg-count {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0;
  color: var(--c-dim);
  background: #f0f5fb;
  border-radius: 999px;
  padding: 1px 8px;
  margin-left: 4px;
}
.reg-group-sub { margin: 4px 0 10px; font-size: 12px; line-height: 1.45; color: var(--c-dim); }

/* Create forms, folded away. Open they are a normal stack of labels; closed
   they are one blue word, so the rail stays a list of what exists. */
.reg-new { margin: 0 0 10px; }
.reg-new summary {
  color: var(--c-blue);
  cursor: pointer;
  font-size: 12.5px;
  font-weight: 600;
  padding: 4px 0;
}
.reg-new[open] {
  background: #f8fafd;
  border: 1px solid var(--c-line);
  border-radius: 10px;
  padding: 8px 12px 12px;
}
.reg-new label { margin: 8px 0; font-size: 12px; }
.reg-new input[type="text"], .reg-new textarea {
  display: block;
  width: 100%;
  margin-top: 4px;
  padding: 6px 10px;
  font: inherit;
  font-weight: 400;
  font-size: 13px;
  border: 1px solid var(--c-line);
  border-radius: 8px;
  resize: vertical;
}

.reg-list { list-style: none; margin: 0; padding: 0; }

/* One row. Two lines: the name, and the fact that tells you whether to care
   (how many actions a story runs, how many stories an action is in). */
.reg-item {
  display: block;
  padding: 7px 10px;
  border-radius: 8px;
  border-left: 3px solid transparent;
  color: var(--c-ink);
}
.reg-item:hover { background: #f0f5fb; text-decoration: none; }
.reg-item.is-current {
  background: #e6f2fb;
  border-left-color: var(--c-blue);
}
.reg-item.is-current .reg-item-name { color: var(--c-blue-deep); }
.reg-item-name { display: block; font-size: 13.5px; font-weight: 600; line-height: 1.35; }
.reg-item-sub { display: block; font-size: 11.5px; color: var(--c-dim); margin-top: 1px; }
.reg-empty { padding: 6px 10px; font-size: 13px; color: var(--c-dim); }
.reg-nomatch { margin: 14px 4px 0; font-size: 13px; color: var(--c-dim); }

/* A mutating action is marked in the rail too, but as a dot: the word would
   be longer than most of the names beside it. */
.reg-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--c-error);
  margin-left: 6px;
  vertical-align: middle;
}
/* Amber, not red: an action with no story is worth noticing and is not a
   problem. Dave, 2026-08-03: this is where useful things normally start. */
.reg-tag-orphan {
  display: inline-block;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  background: #fff8e8;
  color: #8a5f00;
  border-radius: 999px;
  padding: 0 7px;
}

/* ---------- the detail pane ---------- */

.reg-detail { min-width: 0; }
.reg-detail .console-section:first-child { margin-top: 0; }

/* The section index. Dave's ask, 2026-08-07.2: a story and an action have
   each grown several sections and now have to be scrolled through, so put
   their headings at the top of the panel.

   Sticky rather than a plain list, and at the same 82px the rail already
   pins to, because the complaint is about scrolling: an index that scrolls
   away with the page answers "what is on here" once and then stops. Pinned,
   it is also the way back up, which is the other half of a long panel.

   The type is `.console-section`'s own, deliberately. These are not links
   about the sections, they are the section headings lifted to the top, and
   matching the heading they point at is what says so without a label. */
.reg-sections {
  position: sticky;
  top: 82px;
  z-index: 10;
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 0 0 8px;
  padding: 7px 8px;
  background: var(--c-card);
  border: 1px solid var(--c-line);
  border-radius: 12px;
  box-shadow: 0 1px 2px rgba(19, 30, 88, 0.05);
}
.reg-sections a {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--c-navy);
  text-decoration: none;
  white-space: nowrap;
  padding: 4px 12px;
  border-radius: 999px;
}
.reg-sections a:hover { background: #eef2f7; text-decoration: none; }
.reg-sections a:focus-visible { outline: 2px solid var(--c-blue); }

/* Clear the index too, or a jump lands the heading underneath it. **This
   number is the index only, not the whole of the chrome:** `html` up at the
   top of this file already carries `scroll-padding-top: 96px` for the pinned
   header, the two add, and writing the full clearance here lands the heading
   96px too low. Measured 2026-08-07.2, having made exactly that mistake.

   43px of index and 8px of its margin sit below the 96px, so 40px is the
   index plus a little air. Recount if the index ever wraps to two lines. */
.reg-detail .console-section { scroll-margin-top: 40px; }

/* Considered and not done, 2026-08-07.2: `scroll-behavior: smooth` on the
   jump. It is one line and it reads well, but `html` is the only element it
   can go on, so it would change every console page for a control on one of
   them, and the browser pane this repo tests in reports every page as hidden,
   which throttles the animation it drives. Unverifiable and wider than the
   ask. An instant jump is what every anchor on this estate already does. */

/* Which of the two things you are looking at, said before the name rather
   than left to be inferred from the shape of the page below it. */
.reg-kind {
  display: inline-block;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  border-radius: 999px;
  padding: 2px 10px;
  margin-bottom: 8px;
}
.reg-kind.is-story { background: var(--c-navy); color: #fff; }
.reg-kind.is-action { background: #e6f2fb; color: var(--c-blue-deep); }

.reg-title { margin: 0 0 4px; font-size: 24px; font-weight: 600; line-height: 1.25; }
.reg-meta { margin: 0 0 12px; font-size: 12.5px; color: var(--c-dim); }
/* The one place on this page that reads like prose gets a measure. */
.reg-prose { margin: 0 0 12px; max-width: 78ch; }

.reg-blank { border-style: dashed; }
.reg-blank p { margin: 0 0 6px; }
.reg-blank p:last-child { margin-bottom: 0; }

/* ---------- the running order ----------
   Dave's "pills", with his own correction: a box around the name and the
   description that spans the width of the pane, not the little pill the
   header buttons are. */
.reg-pills { list-style: none; margin: 0 0 16px; padding: 0; counter-reset: none; }

.reg-pill {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  background: var(--c-card);
  border: 1px solid var(--c-line);
  border-radius: 12px;
  padding: 12px 14px;
  margin-bottom: 10px;
  box-shadow: 0 1px 2px rgba(19, 30, 88, 0.05);
}
.reg-pill:hover { border-color: #c9d5e8; }
/* The plain variant is the same box with nothing to drag: an action's list of
   stories, and the orphan list on the landing pane. */
.reg-pills.is-plain .reg-pill { cursor: default; }

/* Semi-transparent rather than hidden while dragging: the row keeps its
   height, so the list does not jump under the cursor as it moves. */
.reg-pill.is-dragging { opacity: 0.4; border-color: var(--c-blue); }

.reg-grip {
  cursor: grab;
  color: #a9b4c8;
  font-size: 17px;
  line-height: 1.4;
  user-select: none;
  flex: 0 0 auto;
}
.reg-grip:active { cursor: grabbing; }

.reg-step {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--c-navy);
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 1px;
}

.reg-pill-body { flex: 1 1 auto; min-width: 0; }
.reg-pill-name { margin: 0; font-size: 15px; font-weight: 600; line-height: 1.35; }
.reg-pill-note { margin: 4px 0 0; font-size: 13px; line-height: 1.5; color: var(--c-ink); max-width: 78ch; }
/* Borrowed text is set apart from the story's own, or the two read as one
   thing and nobody notices the story has not been written yet. */
.reg-pill-note.is-fallback { color: var(--c-dim); font-style: italic; }
.reg-fallback-tag {
  font-style: normal;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--c-dim);
  background: #f0f5fb;
  border-radius: 999px;
  padding: 0 7px;
  margin-left: 4px;
  white-space: nowrap;
}
.reg-pos { font-size: 11.5px; font-weight: 600; color: var(--c-dim); margin-left: 6px; }

.reg-pill-tools { flex: 0 0 auto; display: flex; align-items: center; gap: 4px; }
.reg-inline { margin: 0; }
.reg-move, .reg-remove {
  background: transparent;
  border: 1px solid var(--c-line);
  color: var(--c-dim);
  border-radius: 7px;
  padding: 2px 8px;
  font-size: 13px;
  line-height: 1.5;
}
.reg-move:hover:not(:disabled), .reg-remove:hover {
  background: #f0f5fb;
  border-color: var(--c-blue);
  color: var(--c-blue);
}
.reg-move:disabled { opacity: 0.35; cursor: default; }
.reg-remove:hover { border-color: var(--c-error); color: var(--c-error); background: #fdf1f2; }

/* Editing a step's wording, folded away inside the step it belongs to. */
.reg-noteedit { margin-top: 8px; }
.reg-noteedit summary { color: var(--c-blue); cursor: pointer; font-size: 12.5px; font-weight: 600; }
.reg-noteedit textarea, .reg-noteedit input[type="text"] {
  display: block;
  width: 100%;
  max-width: 70ch;
  margin: 6px 0 8px;
  padding: 8px 10px;
  font: inherit;
  font-weight: 400;
  font-size: 13.5px;
  border: 1px solid var(--c-line);
  border-radius: 8px;
  resize: vertical;
}
/* The WorkHQ name is a 50-character box, Dave's spec, and its input carries
   size="50" for it. width:auto lets that size win over the 100% above. */
.reg-nameform input[type="text"] { width: auto; max-width: 100%; }

/* Adding an existing action to a story, or a story to an action. One row,
   sat on the tinted strip so it reads as the end of the list rather than
   another item in it. */
.reg-add {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  background: #f0f5fb;
  border: 1px solid var(--c-line);
  border-radius: 10px;
  padding: 10px 14px;
}
.reg-add label { margin: 0; font-size: 12.5px; white-space: nowrap; }
.reg-add select { flex: 1 1 240px; min-width: 0; font-size: 13.5px; padding: 6px 10px; }
.reg-add-none { margin: 0; }

/* ---------- assets ---------- */

.reg-asset h3 { margin-top: 16px; }
.reg-asset h3:first-of-type { margin-top: 12px; }
.reg-tag-override {
  font-size: 10.5px;
  font-weight: 700;
  vertical-align: middle;
  margin-left: 6px;
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--c-amber);
  color: var(--c-ink);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.reg-flowname { margin: 4px 0 0; font-size: 13px; }

.reg-overrideform { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 14px; }
.reg-flabel { margin: 0; font-size: 12px; }
.reg-flabel input[type="text"] {
  display: block;
  margin-top: 4px;
  padding: 8px 12px;
  font: inherit;
  font-weight: 400;
  font-size: 13.5px;
  border: 1px solid var(--c-line);
  border-radius: 8px;
  width: auto;
  max-width: 100%;
}
/* The URL is the one field with no natural width, so it takes what is left. */
.reg-overrideform .reg-flabel:first-of-type { flex: 1 1 340px; min-width: 0; }
.reg-overrideform .reg-flabel:first-of-type input { width: 100%; }
.reg-mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace !important; font-size: 13px !important; }
.reg-overridebtns { display: flex; gap: 8px; }

/* ---------- ownership, lifecycle, files (PLAT-67, 2026-08-03) ----------
   Everything below exists to answer one question at a glance: is this thing
   everyone's, mine, or somebody else's, and what state is it in. The colours
   are the console's existing three: navy for shared and settled, blue for
   mine, amber for anything waiting on a person. */

/* The scope filter. Admin only, because nobody else has a second scope, and
   deliberately a set of links rather than a control: each one is a URL you
   can land on. */
.reg-scope {
  display: flex;
  gap: 2px;
  margin: 0 0 14px;
  background: #eef2f8;
  border-radius: 999px;
  padding: 3px;
}
.reg-scope-btn {
  flex: 1;
  text-align: center;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--c-dim);
  padding: 5px 6px;
  border-radius: 999px;
  white-space: nowrap;
}
.reg-scope-btn:hover { color: var(--c-blue); text-decoration: none; background: #fff; }
.reg-scope-btn.is-on { background: var(--c-navy); color: #fff; }

/* Rail chips. Small, quiet, and never more than two on a row in practice. */
.reg-tag-mine, .reg-tag-sub, .reg-tag-arch, .reg-tag-ph, .reg-tag-off {
  display: inline-block;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  border-radius: 999px;
  padding: 0 7px;
}
.reg-tag-mine { background: #e6f2fb; color: var(--c-blue-deep); }
.reg-tag-sub  { background: var(--c-amber); color: var(--c-ink); }
.reg-tag-arch { background: #e8ecf4; color: var(--c-dim); }
.reg-tag-ph   { background: #f2effa; color: var(--c-purple); }
/* PLAT-110. Deliberately louder than `archived`, which is a quiet grey: a
   disabled story is one the admin is still working on and is looking straight
   at, and it has to read differently from the row above it at a glance. */
.reg-tag-off  { background: #fdf1f2; color: #a3323f; }

/* The ownership strip under a detail title: who owns it, then the verbs. */
.reg-own {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 14px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--c-line);
}
.reg-own form { margin: 0; }
.reg-own-tag {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 3px 10px;
  border-radius: 999px;
}
.reg-own-tag.is-global { background: var(--c-navy); color: #fff; }
.reg-own-tag.is-mine   { background: var(--c-blue); color: #fff; }
.reg-own-tag.is-theirs { background: #e8ecf4; color: var(--c-dim); }

/* Destructive verbs are outlined, never filled: a solid red button beside a
   solid blue one gets clicked by muscle memory. Red arrives on hover, once
   the pointer is already committed to that spot. */
.reg-danger {
  background: transparent;
  border: 1px solid var(--c-line);
  color: var(--c-dim);
}
.reg-danger:hover {
  background: #fdf1f2;
  border-color: var(--c-error);
  color: var(--c-error);
}

/* Retire and Delete on an idea, set off from the page by a rule rather than
   sitting under the admin edit form as though they were more of it. Added
   2026-08-03.6 with `PLAT-73`. The note beside them wraps under them when the
   line runs out of room, which is plain flex wrapping and all that is wanted
   here. */
.idea-endstate {
  margin-top: 24px;
  padding-top: 16px;
  border-top: 1px solid var(--c-line);
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
}
.idea-endstate form { margin: 0; }
/* **No `width: 100%` here, and it is worth saying why so nobody adds one.**
   Two attempts to pin the note onto its own line both silently did nothing:
   `.idea-detail p` caps every paragraph in this column at `78ch` for
   readability, so the note can never be wider than about 573px whatever this
   rule asks for, and at that width it simply fits beside the buttons. The cap
   is the older and better rule; this one defers to it. */
.idea-endstate p { margin: 6px 0 0; }

/* Promotion: the one approval queue in the design, so it gets a real box
   rather than another quiet toggle. */
.reg-promo {
  background: #f8fafd;
  border: 1px solid var(--c-line);
  border-radius: 10px;
  padding: 12px 14px;
  margin: 0 0 14px;
}
.reg-promo p { margin: 0 0 8px; font-size: 13.5px; }
.reg-promo p:last-child { margin-bottom: 0; }
.reg-promo summary { color: var(--c-blue); cursor: pointer; font-size: 13px; font-weight: 600; }
.reg-promo textarea {
  display: block;
  width: 100%;
  max-width: 70ch;
  margin: 8px 0;
  padding: 8px 10px;
  font: inherit;
  font-weight: 400;
  font-size: 13.5px;
  border: 1px solid var(--c-line);
  border-radius: 8px;
  resize: vertical;
}
.reg-promo.is-waiting { background: #fff8e8; border-color: #f0dcae; }
.reg-promo.is-returned { background: #fdf1f2; border-color: #f3c6cc; }
.reg-promo.is-review { background: #e6f2fb; border-color: #b6d7f0; }
/* PLAT-110's panel, the same box as a returned promotion because it says the
   same kind of thing: this exists, it is yours to act on, and it is not
   currently doing its job. */
.reg-promo.is-off { background: #fdf1f2; border-color: #f3c6cc; }

/* Taking a global story out of circulation. Folded away by default: it is an
   admin verb used rarely, and an open form offering to hide a story is not
   what the page should look like at rest. */
.reg-off {
  background: #f8fafd;
  border: 1px solid var(--c-line);
  border-radius: 10px;
  padding: 10px 14px;
  margin: 0 0 14px;
}
.reg-off summary { color: var(--c-blue); cursor: pointer; font-size: 13px; font-weight: 600; }
.reg-off p { margin: 8px 0; }
.reg-off label { display: block; font-size: 13px; font-weight: 600; margin: 8px 0; }
.reg-off input[type=text] {
  display: block;
  width: 100%;
  max-width: 70ch;
  margin-top: 4px;
  padding: 8px 10px;
  font: inherit;
  font-weight: 400;
  font-size: 13.5px;
  border: 1px solid var(--c-line);
  border-radius: 8px;
}
.reg-promo-btns { display: flex; gap: 10px; align-items: flex-start; flex-wrap: wrap; }
.reg-promo-btns form { margin: 0; }

/* The admin's inbox, above the two lists, because a submission waiting on you
   is not something you should have to go looking for. */
.reg-queue { border-left: 4px solid var(--c-amber); margin-bottom: 20px; }
.reg-queue h2 { display: flex; align-items: center; gap: 8px; }
.reg-queue-list { list-style: none; margin: 10px 0 0; padding: 0; }
.reg-queue-list li {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 6px 0;
  border-top: 1px solid var(--c-line);
}
.reg-queue-list li:first-child { border-top: 0; }

/* A placeholder step reads as provisional: dashed edge, purple accent, so a
   running order full of them looks like the draft it is. */
.reg-pill.is-placeholder { border-style: dashed; background: #fcfbff; }
.reg-pill.is-placeholder .reg-step { background: var(--c-purple); }
.reg-kind.is-placeholder { background: #f2effa; color: var(--c-purple); }

.reg-convert {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px dashed var(--c-line);
}
.reg-convert h3 { margin-top: 0; }
.reg-convert p { margin: 0 0 10px; max-width: 78ch; }

.reg-newstep { margin-top: 12px; }
.reg-newstep summary { color: var(--c-blue); cursor: pointer; font-size: 13px; font-weight: 600; }
.reg-newstep[open] {
  background: #f8fafd;
  border: 1px solid var(--c-line);
  border-radius: 10px;
  padding: 10px 14px 14px;
  margin-top: 10px;
}
.reg-newstep label { margin: 8px 0; font-size: 12.5px; }
.reg-newstep input[type="text"], .reg-newstep textarea {
  display: block;
  width: 100%;
  max-width: 70ch;
  margin-top: 4px;
  padding: 8px 10px;
  font: inherit;
  font-weight: 400;
  font-size: 13.5px;
  border: 1px solid var(--c-line);
  border-radius: 8px;
  resize: vertical;
}

/* Files. One row each: name, then the facts, then what it is for. */
.reg-files { list-style: none; margin: 0 0 14px; padding: 0; }
.reg-files li {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding: 8px 0;
  border-top: 1px solid var(--c-line);
}
.reg-files li:first-child { border-top: 0; }
.reg-files form { margin: 0 0 0 auto; }
.reg-files a { font-weight: 600; font-size: 13.5px; }
.reg-file-meta { font-size: 11.5px; color: var(--c-dim); }
.reg-file-label { flex-basis: 100%; font-size: 13px; color: var(--c-ink); }

.reg-upload {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
  background: #f0f5fb;
  border: 1px solid var(--c-line);
  border-radius: 10px;
  padding: 10px 14px;
  margin-top: 4px;
}
.reg-upload input[type="file"] { font-size: 12.5px; max-width: 100%; }
.reg-upload input[type="text"] {
  flex: 1 1 220px;
  min-width: 0;
  font: inherit;
  font-size: 13px;
  padding: 6px 10px;
  border: 1px solid var(--c-line);
  border-radius: 8px;
  background: #fff;
}

/* Make a Story, on a fleshed idea (PLAT-67). Sits under the vote row and
   reads as an offer rather than another control: the amber is the console's
   attention colour and this is the one button on the page that creates
   something somewhere else. */
.idea-makestory {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin: 14px 0 0;
  padding: 12px 14px;
  background: #f8fafd;
  border: 1px solid var(--c-line);
  border-radius: 10px;
}
.idea-makestory form { margin: 0; }
.idea-makestory p { margin: 0; }
.idea-makestory span { flex: 1 1 320px; min-width: 0; color: var(--c-dim); }

/* --------------------------------------------------- Needs You (PLAT-68) */

/* The nav count. The only number in the header bar, so it reads as an alert
   rather than as decoration, and it sits inside the admin group's teal so it
   does not compete with the demoing chip's amber on the other side. */
.console-nav-count {
  display: inline-block;
  min-width: 17px;
  margin-left: 6px;
  padding: 0 5px;
  border-radius: 9px;
  background: var(--c-amber);
  color: var(--c-ink);
  font-size: 11.5px;
  font-weight: 700;
  line-height: 17px;
  text-align: center;
  vertical-align: 1px;
}
.console-nav-admin a.is-current .console-nav-count { background: #fff; color: var(--c-teal); }

.needs-summary {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 22px;
  align-items: baseline;
}
.needs-stat { color: var(--c-dim); font-size: 14px; }
.needs-stat strong {
  font-size: 22px;
  font-weight: 600;
  color: var(--c-ink);
  margin-right: 4px;
}
.needs-stat.is-open strong { color: var(--c-blue); }
.needs-stat.is-failed strong { color: var(--c-error); }
.needs-stat.is-soon strong { color: #8a5f00; }
.needs-intro { margin: 12px 0 0; max-width: 900px; }

.needs-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--c-line);
}
.needs-filter-label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--c-dim);
  margin-right: 4px;
}
.needs-filter-gap { margin-left: 18px; }
.needs-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  border: 1px solid var(--c-line);
  border-radius: 999px;
  background: #fff;
  color: var(--c-ink);
  font-size: 13px;
}
.needs-chip:hover { border-color: var(--c-blue); color: var(--c-blue); text-decoration: none; }
.needs-chip.is-on { background: var(--c-navy); border-color: var(--c-navy); color: #fff; }
.needs-chip-n { font-size: 11.5px; font-weight: 700; color: var(--c-dim); }
.needs-chip.is-on .needs-chip-n { color: #aeb8d8; }

.needs-bulkbar {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 12px;
  padding: 10px 14px;
  background: #f8fafd;
  border: 1px solid var(--c-line);
  border-radius: 10px;
}
/* `flex: none` and `nowrap` are belt to the checkbox reset's braces. The reset
   is what actually fixed the collision Dave reported on 2026-08-04.3; this
   stops the label being the thing that gives way if anything else in the bar
   ever grows, which is the same failure the Recent Activity stamp had. */
.needs-selectall { font-size: 13px; color: var(--c-dim); display: flex;
                   align-items: center; gap: 6px; flex: none; white-space: nowrap; }
.needs-bulkbar .console-bulkcount { flex: none; min-width: 90px; font-weight: 600;
                                    color: var(--c-navy); }

/* The copy control. Dave's ask: check a batch and paste it into a session.
   Dimmed until something is checked, because an empty clipboard write that
   reports Copied is the same lie the shared handler already refuses to tell
   when the clipboard is blocked. */
.needs-copy {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
  padding: 5px 12px;
  border: 1px solid var(--c-line);
  border-radius: 999px;
  background: #fff;
  font-size: 13px;
  color: var(--c-dim);
  opacity: 0.55;
  pointer-events: none;
}
.needs-copy.is-ready {
  opacity: 1;
  pointer-events: auto;
  cursor: pointer;
  border-color: var(--c-blue);
  color: var(--c-blue);
}
.needs-copy.is-ready:hover { background: #f0f5fb; }
.needs-copy.is-copied { border-color: var(--c-ok); color: var(--c-ok); }
.needs-copy.is-failed { border-color: var(--c-error); color: var(--c-error); }

.needs-table td { vertical-align: top; padding-top: 12px; padding-bottom: 12px; }
.needs-row.is-done { opacity: 0.62; }
.needs-item { max-width: 620px; }
.needs-item-head { font-weight: 500; }
.needs-how { margin-top: 4px; }
.needs-how summary {
  color: var(--c-blue);
  cursor: pointer;
  font-size: 12.5px;
  font-weight: 600;
  list-style: none;
}
.needs-how summary::-webkit-details-marker { display: none; }
.needs-how summary::before { content: "\25B8\00a0"; }
.needs-how[open] summary::before { content: "\25BE\00a0"; }
.needs-how p { margin: 8px 0 0; }

.needs-kind {
  display: inline-block;
  padding: 1px 9px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.03em;
  white-space: nowrap;
}
.needs-kind.is-check { background: #e6f2fb; color: var(--c-blue-deep); }
.needs-kind.is-question { background: #ede9f7; color: var(--c-purple); }

.needs-due { color: var(--c-dim); }
.needs-due.is-soon { color: #8a5f00; font-weight: 600; }
.needs-due.is-overdue { color: var(--c-error); font-weight: 600; }

.needs-status { min-width: 240px; }
.needs-answer input[type="text"] {
  width: 100%;
  font: inherit;
  font-size: 13px;
  padding: 6px 10px;
  border: 1px solid var(--c-line);
  border-radius: 8px;
  background: #fff;
}
.needs-answer input[type="text"]:focus { outline: 2px solid var(--c-blue); border-color: var(--c-blue); }
.needs-answer-btns { display: flex; gap: 6px; margin-top: 6px; }

.needs-badge {
  display: inline-block;
  padding: 2px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 700;
}
.needs-badge.is-done { background: #e3f5ea; color: var(--c-ok); }
.needs-badge.is-failed { background: #fdeaec; color: var(--c-error); }
.needs-said { margin: 6px 0 2px; font-size: 13.5px; }

/* ------------------------------- Files and Templates (migration session 7) */

/* The kind filter. Chips rather than a select, because there are three of
   them and the counts are the useful part: an action with no template should
   say so before anybody clicks. */
.reg-file-filters { display: flex; gap: 6px; margin-bottom: 12px; flex-wrap: wrap; }
.reg-file-chip {
  font: inherit;
  font-size: 12.5px;
  padding: 3px 11px;
  border: 1px solid var(--c-line);
  border-radius: 999px;
  background: #fff;
  color: var(--c-ink);
  cursor: pointer;
}
.reg-file-chip:hover { border-color: var(--c-blue); color: var(--c-blue); }
.reg-file-chip.is-on { background: var(--c-navy); border-color: var(--c-navy); color: #fff; }
.reg-file-chip span { color: var(--c-dim); font-weight: 700; margin-left: 4px; }
.reg-file-chip.is-on span { color: #aeb8d8; }

.reg-file-group + .reg-file-group { margin-top: 16px; }
.reg-file-group h3 {
  margin: 0 0 4px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--c-dim);
}
.reg-file-group p { margin: 0 0 8px; }

.reg-file-ver {
  display: inline-block;
  margin-left: 6px;
  padding: 0 7px;
  border-radius: 999px;
  background: #ede9f7;
  color: var(--c-purple);
  font-size: 11.5px;
  font-weight: 700;
}
/* Amber, not red: baseline moving is information, never a problem. The clone
   is a physical copy and its demo keeps working (A7). */
.reg-file-moved {
  display: inline-block;
  margin-left: 6px;
  padding: 0 8px;
  border-radius: 999px;
  background: #fff8e8;
  color: #8a5f00;
  font-size: 11.5px;
  font-weight: 600;
  cursor: help;
}
.reg-upload select {
  font: inherit;
  font-size: 13px;
  padding: 6px 10px;
  border: 1px solid var(--c-line);
  border-radius: 8px;
  background: #fff;
}

/* ------------------------ Recent Activity refresh and live (2026-08-03.5) */

/* **The stamp yields; the controls never do.** Dave's report, 2026-08-04.3:
   Live and Refresh were drawn on top of each other. `min-width: 150px` on the
   stamp was a hard floor, so when the row came under pressure the shrinking had
   to fall somewhere else, and the only other shrinkable thing in it was the
   Live label, which had no floor of its own. The label lost its last characters
   and the button came down on what was left.

   The floor is gone and the stamp takes the slack instead: the controls box
   grows to the full width the card head can spare, `.activity-when` absorbs
   whatever the two controls do not use, and the controls sit hard right. That
   fixes the jiggle the 150px was there for **better** than the 150px did, since
   Live and Refresh now never move at all rather than merely moving less. A
   fixed reserve would also have had to truncate the longest thing this element
   ever says, "signed out, reload to sign in again", which is the one message
   here that a reader has to be able to finish. */
.activity-controls { align-items: center; flex-wrap: nowrap; flex: 1 1 auto; min-width: 0; }
.activity-when {
  font-size: 12.5px;
  color: var(--c-dim);
  flex: 1 1 auto;
  min-width: 0;
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.activity-live {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: none;
  white-space: nowrap;
  font-size: 13px;
  color: var(--c-dim);
  cursor: pointer;
  user-select: none;
}
.activity-controls .console-btn-small { flex: none; }

.activity-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 8px 12px;
  margin-bottom: 14px;
  padding: 12px 14px;
  background: #f8fafd;
  border: 1px solid var(--c-line);
  border-radius: 10px;
}
.activity-filters label {
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--c-dim);
}
/* **One height, stated once, for four controls that do not agree on their
   own.** Dave's report, 2026-08-04.3: the filter bar had three different
   heights in it. Measured before the fix at 1280px: `select` 30px, `input
   type=date` 34px, `input type=search` 32px, the Apply button 29px. Nothing was
   setting a height, so each control got whatever its own engine thinks a form
   field should be, and a date field thinks it needs room for a spinner. The bar
   is `align-items: flex-end`, so the disagreement showed up twice over: the
   controls sat at four different heights and their uppercase labels above them
   sat at three different baselines. Height plus the global `box-sizing:
   border-box` settles all of it; the labels line up because the boxes do. */
.activity-filters select,
.activity-filters input {
  font: inherit;
  font-size: 13px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--c-ink);
  height: 32px;
  padding: 5px 9px;
  border: 1px solid var(--c-line);
  border-radius: 8px;
  background: #fff;
}
.activity-filters select:focus,
.activity-filters input:focus { outline: 2px solid var(--c-blue); border-color: var(--c-blue); }
.activity-filters .activity-q { flex: 1 1 220px; min-width: 0; }
.activity-filters .activity-q input { width: 100%; }
/* The button and the Clear link join the same 32px row. The `margin-bottom: 1px`
   that used to sit here was nudging a 29px button towards a 30px select by eye;
   with a stated height there is nothing left to nudge. */
.activity-filters button {
  height: 32px;
  padding-top: 0;
  padding-bottom: 0;
}
.activity-filters .console-linkish { line-height: 32px; }

.activity-pager {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--c-line);
}
.activity-pagebtns { display: flex; gap: 8px; margin-left: auto; }
/* A disabled pager end is rendered, not hidden: the buttons should not move
   under the cursor when you reach the last page. */
.activity-pagebtns .is-off { opacity: 0.4; pointer-events: none; }

/* ---------------------------------------------------------------- shield.php
   The blocklist page, PLAT-86. Three tables and a form; almost all of it is
   .console-table and .needs-summary, already defined above, so what is here is
   only the verdict coloring and the add-by-hand row.

   The verdict column is the one thing on the page that has to read at a
   glance, because the question it answers is "would this address be blocked,
   and if not, why not". Three states and no others: fine, would trip, already
   blocked. Green is deliberately used for "vetoed" as well as "under
   threshold", because from the SC's point of view those are the same fact. */
.shield-is-ok { color: var(--c-ok); }
.shield-is-trip { color: var(--c-error); font-weight: 600; }
.shield-is-blocked { color: var(--c-error); font-weight: 600; }

.shield-add {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  align-items: flex-end;
  margin-top: 10px;
}
.shield-add label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
  color: var(--c-dim);
}
.shield-add input {
  font-size: 13px;
  padding: 6px 10px;
  border: 1px solid var(--c-line);
  border-radius: 6px;
}
.shield-add input[type="text"] { min-width: 220px; }
.shield-add input[type="number"] { width: 90px; }
.shield-add-btns { display: flex; gap: 8px; }

/* Where an inherited attachment came from (PLAT-93). A story lists the files
   of every action in it, and the row has to say which action owns it or the
   list reads as one flat pile the story can edit. It sits where the remove
   button sits on an owned row, because those two are the same question
   answered differently: what can I do with this one. */
.reg-file-via { font-size: 12px; color: var(--c-dim); white-space: nowrap; }
.reg-file-via a { color: var(--c-blue); }

/* ---------- reset demo data (PLAT-95) ---------- */

/* One block per story listing every reset it needs, down beside the files.
   Dave's ask, 2026-08-06. Denser than the file list on purpose: a file row is
   a link with metadata trailing it, and a reset row is a decision with
   consequences, so each one gets its own explanation on its own line and the
   run controls sit under that rather than beside it. */

.reg-resets { list-style: none; margin: 0; padding: 0; }
.reg-resets li {
  padding: 12px 0;
  border-top: 1px solid var(--c-line);
}
.reg-resets li:first-child { border-top: 0; padding-top: 2px; }

.reg-reset-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 10px; }
.reg-reset-name { font-weight: 600; font-size: 13.5px; }
.reg-reset-site { font-size: 11.5px; color: var(--c-dim); }

/* The scope badge. `yours` is quiet because it is the expected answer;
   `everyone` is loud because it is the one an SC must not learn about
   afterwards, and it is the same red the mutates-data tag uses for the same
   reason. */
.reg-reset-scope {
  font-size: 11px;
  font-weight: 600;
  padding: 2px 9px;
  border-radius: 999px;
  background: #eef2f7;
  color: var(--c-dim);
}
.reg-reset-scope.is-wide { background: #fdf1f2; color: var(--c-error); }

.reg-reset-what {
  margin: 5px 0 8px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--c-dim);
  max-width: 82ch;
}

.reg-reset-tools { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; }
.reg-reset-tools form { margin: 0; }
.reg-reset-tools .reg-remove { margin-left: auto; }
.reg-reset-where { font-size: 12px; }

/* A reset in a story whose code has gone. Kept visible and removable rather
   than filtered out, so a story that would half-clean says so. */
.reg-resets li.is-missing .reg-reset-name { color: var(--c-error); }
.reg-resets li.is-missing .reg-reset-name::after {
  content: ' — missing';
  font-weight: 400;
  font-size: 12px;
}

.reg-reset-all {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 12px;
  margin: 14px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--c-line);
}
.reg-reset-all span { flex: 1 1 320px; margin: 0; }

/* The one button on this page that destroys several things at once. Amber
   rather than blue: it is not the dangerous-red of an unrecoverable action,
   because every one of these is a demo overlay by definition, but it must not
   look like Save either. */
.console-btn-small.is-strong {
  background: var(--c-amber);
  border-color: var(--c-amber);
  color: var(--c-ink);
  font-weight: 700;
}
.console-btn-small.is-strong:hover { background: #dd9700; border-color: #dd9700; }

/* ---------------------------------------------------------------- changelog

   `PLAT-103`. Release notes read as prose, not as a table, so this borrows the
   search bar off the Backlog page and nothing else from it: one card per day,
   a bolded lead-in per change, and generous line length. The measure is capped
   here and only here, against the standing no-width-caps rule (Dave, session
   5), because that rule is about tables and dashboards on a demo screen and
   this is a paragraph somebody actually reads left to right. */

.cl-controls { margin-bottom: 20px; }
.cl-search { display: flex; gap: 8px; align-items: center; }
.cl-search input[type="search"] {
  flex: 1;
  min-width: 0;
  max-width: 520px;
  padding: 8px 14px;
  font: inherit;
  border: 1px solid var(--c-line);
  border-radius: 999px;
}
.cl-search input[type="search"]:focus { outline: 2px solid var(--c-blue); border-color: var(--c-blue); }
.cl-scope { margin: 12px 0 0; font-size: 13.5px; color: var(--c-dim); }
.cl-scope strong { color: var(--c-ink); }
.cl-scope .console-linkish { margin-left: 6px; }

.cl-entry { margin-bottom: 16px; }
.cl-date {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin: 0 0 4px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--c-line);
  font-size: 19px;
}
.cl-date a { color: var(--c-ink); }
.cl-date a:hover { color: var(--c-blue); text-decoration: none; }
.cl-count {
  margin-left: auto;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--c-dim);
}

.cl-change { margin-top: 16px; max-width: 90ch; }
/* `.console-card h3` is the console's small-caps section label, and that is
   the wrong thing for these: a change title is a sentence carrying proper
   nouns and sometimes a path, and uppercasing it destroys both. All four of
   that rule's properties are overridden deliberately rather than by accident
   of order. */
.cl-change h3 {
  margin: 0 0 2px;
  font-size: 15px;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0;
  color: var(--c-ink);
}
.cl-change p { margin: 0; }
.cl-change code {
  background: #eef2f8;
  padding: 1px 5px;
  border-radius: 4px;
  font-size: 13px;
}

.cl-paging { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.cl-paging .cl-older { margin-left: auto; }
.cl-foot { margin: 14px 2px 0; }
