:root {
  --ink: #1c2430;
  --ink-soft: #5b6675;
  --paper: #ffffff;
  --bg: #f4f5f7;
  --border: #dde1e7;
  --accent: #2f6f4f;
  --accent-ink: #ffffff;
  --refusal-bg: #fdf1ee;
  --refusal-ink: #9a3b1f;
  --answered-bg: #eef6f0;
  --answered-ink: #235c3d;
  --radius: 10px;

  /* TF-126 — True Fix Advisory brand tokens. Additive: everything above this
     line is the pre-existing chat-shell palette and is untouched by this
     card. These drive the login screen and the app shell (header +
     empty-state) only; the conversation transcript, badges, and citations
     keep the tokens above exactly as they were. */
  --navy: #0a1628;
  --amber: #c8882a;
  --cream: #f0ead6;
  --canvas: #e8e2d0;
  --nearblack: #1a1208;
  --midgray: #5a5040;
  --tan: #d4cbb0;
  --lighttext: #f8f4ed;

  --font-serif: "Libre Baskerville", Georgia, "Times New Roman", serif;
  --font-body: "Work Sans", Arial, Helvetica, sans-serif;
  --font-mono: "DM Mono", "Courier New", Courier, monospace;
}

/* --- TF-126: self-hosted brand fonts -------------------------------------
   Latin-subset woff2 only, OFL-licensed (Libre Baskerville, Work Sans, DM
   Mono — all Google Fonts catalog entries under the SIL Open Font License).
   Every src is same-origin under /static/fonts/ — CSP is style-src/font-src
   'self' and stays that way; nothing here is a Google Fonts network request
   at runtime. Each filename carries a content hash computed from that exact
   file's bytes (see cockpit/app.py's _ASSET_FILES comment for why: swapping
   a font's content without renaming it is exactly the kind of same-URL
   staleness BUG-1b's asset-versioning exists to prevent, and style.css's own
   url() references are NOT touched by the server-side ?v= stamping — that
   only rewrites index.html's src=/href= attributes). Weights are the ones
   actually used below: Baskerville 400/700, Work Sans 400/500/600, DM Mono
   400/500. */
@font-face {
  font-family: "Libre Baskerville";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/librebaskerville-400.3eb80fbca704.woff2") format("woff2");
}
@font-face {
  font-family: "Libre Baskerville";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/static/fonts/librebaskerville-700.96fa63376eed.woff2") format("woff2");
}
@font-face {
  font-family: "Work Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/worksans-400.418b06936717.woff2") format("woff2");
}
@font-face {
  font-family: "Work Sans";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/static/fonts/worksans-500.943a4b2d8b49.woff2") format("woff2");
}
@font-face {
  font-family: "Work Sans";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/static/fonts/worksans-600.c536b548fdcb.woff2") format("woff2");
}
@font-face {
  font-family: "DM Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/dmmono-400.e1896b13b2b1.woff2") format("woff2");
}
@font-face {
  font-family: "DM Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/static/fonts/dmmono-500.9964608a8493.woff2") format("woff2");
}

* {
  box-sizing: border-box;
}

/* Chat-harness shell (C-9 / UAT-6): the page itself never scrolls. The header
   and the ask bar are pinned by being fixed-size flex items in a viewport-tall
   column; only the transcript between them scrolls. Before this, the whole
   document scrolled, so a long conversation pushed the input off-screen. */
html,
body {
  height: 100%;
}

body {
  margin: 0;
  font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  color: var(--ink);
  background: var(--bg);
  height: 100dvh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* TF-126: two shell backdrops, chosen from which section the markup already
   shows/hides via the `hidden` attribute — no JS change. `:has()` is a CSS4
   selector (all evergreen browsers); fine for this internal tool, matching
   the precedent already in the codebase for state-driven CSS. Full-viewport
   navy while logged out (no app-header showing at all); canvas behind the
   chat shell once signed in. Neither branch touches an individual turn's own
   background (--paper / --refusal-bg / --answered-bg), which are untouched
   chat-internal tokens. */
body:has(#login-section:not([hidden])) {
  background: var(--navy);
}

body:has(#login-section:not([hidden])) .app-header {
  display: none;
}

body:has(#login-section:not([hidden])) main {
  align-items: center;
  justify-content: center;
}

body:has(#chat-section:not([hidden])) {
  background: var(--canvas);
}

/* The shell sets display on elements the markup toggles with the `hidden`
   attribute, which would otherwise beat the UA's [hidden] { display: none }.
   Without this the login form and the chat pane render at the same time. */
[hidden] {
  display: none !important;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.app-header {
  flex: 0 0 auto; /* pinned: never scrolls with the conversation */
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1rem 1.5rem;
  /* TF-126: navy masthead + 1.5pt (~2px) amber bottom border, the
     established brand pattern. Only relevant post-login — the login-state
     rule above hides this element entirely, so it never has to coexist with
     the full-viewport navy login screen. */
  background: var(--navy);
  border-bottom: 2px solid var(--amber);
}

.app-header-logo {
  height: 32px;
  width: 32px;
  display: block;
  flex: 0 0 auto;
}

.app-header-titles {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.app-header h1 {
  font-family: var(--font-serif);
  font-size: 1.25rem;
  margin: 0;
  color: var(--lighttext);
}

.app-subtitle {
  margin: 0;
  color: var(--tan);
  font-size: 0.9rem;
  font-family: var(--font-body);
}

.app-header-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  font-size: 0.85rem;
  color: var(--tan);
}

.link-button {
  background: none;
  border: none;
  color: var(--lighttext);
  cursor: pointer;
  padding: 0;
  font: inherit;
  text-decoration: underline;
}

/* TF-200: the header nav link between the chat shell ("/") and the Owner's
   Dashboard ("/dashboard"), on both shells. */
.header-nav-link {
  color: var(--lighttext);
  font: inherit;
  text-decoration: none;
}

/* TF-252: both header nav controls (the `.header-nav-link` <a>s above and
   the `.link-button` <button>s — "Log out", the rail's own header excepted,
   see its own override below) get the V1-1 wireframe's own `header.top
   .btn` treatment — a small bordered pill against the navy bar — instead of
   reading as bare underlined text bolted onto the header. Scoped to
   `.app-header` specifically so `.link-button`'s other, unrelated uses
   elsewhere in this file (the foreman rail header's own "Close", the recent-
   queries list, etc.) are untouched. */
.app-header .header-nav-link,
.app-header .link-button {
  font-size: 0.85rem;
  font-weight: 600;
  padding: 0.4rem 0.85rem;
  border-radius: 8px;
  background: rgba(248, 244, 237, 0.08);
  border: 1px solid rgba(248, 244, 237, 0.28);
}

.app-header .header-nav-link:hover,
.app-header .link-button:hover {
  background: rgba(248, 244, 237, 0.16);
}

.app-header a:focus-visible,
.app-header button:focus-visible {
  outline: 3px solid var(--amber);
  outline-offset: 2px;
}

/* TF-1147: "Sign out" while its request is in flight, and the message a
   failed sign-out leaves beside it. The message sits on the navy bar, so it
   uses the light header text (about 17:1 on --navy), bold, with an amber
   rule to set it apart from the identity. Empty takes no space. */
.app-header .link-button:disabled {
  cursor: default;
  opacity: 0.6;
}

.sign-out-status:empty {
  display: none;
}

.app-header .sign-out-status {
  color: var(--lighttext);
  font-weight: 600;
  border-left: 3px solid var(--amber);
  padding-left: 0.5rem;
}

/* TF-1166: Settings and Users carry three things on the right of the
   header ("Back to dashboard", who is signed in, "Sign out"), so at phone
   width the bar wraps instead of pushing the page sideways. Scoped to those
   two pages; the dashboard and chat headers are unchanged. A long email
   breaks rather than overflowing. */
.owner-settings-body .app-header,
.owner-users-body .app-header {
  flex-wrap: wrap;
  row-gap: 0.5rem;
}

.owner-settings-body .app-header-right,
.owner-users-body .app-header-right {
  flex-wrap: wrap;
  justify-content: flex-end;
  row-gap: 0.4rem;
  min-width: 0;
}

.owner-settings-body .whoami,
.owner-users-body .whoami {
  min-width: 0;
  overflow-wrap: anywhere;
}

/* TF-1377 (ADR-062 S1): on a phone the header's right-hand group (Chat,
   Settings, Users, who is signed in, Sign out) is wider than the screen, which
   made the dashboard and the chat page 675px wide on a 375px phone and pushed
   the Foreman rail and its Ask button off screen. Below 600px the bar wraps:
   the logo and title keep the first row and the group takes the row under
   them, wrapping again if it must. Nothing is hidden or moved into a menu, so
   no control becomes unreachable. Settings and Users already wrap (TF-1166) and
   their own rules above are more specific, so they are untouched. Above 600px
   none of this applies and the desktop bar is unchanged. */
@media (max-width: 600px) {
  .app-header {
    flex-wrap: wrap;
    row-gap: 0.5rem;
  }

  .app-header-titles {
    min-width: 0;
    flex: 1 1 0;
  }

  .app-header h1,
  .app-subtitle {
    overflow-wrap: anywhere;
  }

  .app-header-right {
    flex: 1 1 100%;
    margin-left: 0;
    flex-wrap: wrap;
    row-gap: 0.4rem;
    min-width: 0;
  }

  .app-header .whoami {
    min-width: 0;
    overflow-wrap: anywhere;
  }
}

/* TF-365 (ROLE-2): the client-facing role label rendered next to the
   signed-in email (cockpit/web/role_labels.js's `renderWhoami`), never the
   internal tier word — see that file's own map for the exact vocabulary,
   not spelled out here. A small bordered pill, the same visual family as
   .header-nav-link, so it reads as a badge rather than as part of the
   email string it sits beside. Its accessible name ("Role: Member") comes
   from the element's own `aria-label` (role_labels.js), independent of
   this rule. */
.role-badge {
  display: inline-block;
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  background: rgba(248, 244, 237, 0.14);
  border: 1px solid rgba(248, 244, 237, 0.32);
}

main {
  width: 100%;
  max-width: 960px;
  margin: 0 auto;
  padding: 1.5rem;
  /* Takes the remaining column height. min-height:0 is what lets a nested
     overflow container actually shrink instead of stretching the flex item. */
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

#login-section {
  max-width: 400px;
  width: 100%;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.5rem;
  overflow-y: auto; /* logged out there is no transcript; keep it reachable */
}

#chat-section {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* TF-126: the login card. Wraps the compass mark / heading / micro-label /
   one-liner (new, presentational-only markup) plus the two UNCHANGED
   sign-in surfaces (#oidc-signin, #login-form) — exactly one of which the
   instance shows, per C-11, same as before. */
.login-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.65rem;
  width: 100%;
  background: var(--cream);
  border-bottom: 3px solid var(--amber); /* ~1.5pt accent rule */
  border-radius: var(--radius);
  padding: 2.5rem 2rem 2rem;
  box-shadow: 0 12px 32px rgba(10, 22, 40, 0.35);
  text-align: center;
}

.login-mark {
  width: 44px;
  height: 44px;
}

.login-microlabel {
  display: inline-block;
  margin: 0;
  padding: 0.25rem 0.7rem;
  background: var(--navy);
  color: var(--amber);
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  border-radius: 999px;
  /* amber-on-cream measures ~2.5:1 and fails WCAG AA at any size — this
     label sits on a small navy chip instead (amber-on-navy is ~6:1) so the
     brand's "micro-labels in amber" pattern still holds without the
     contrast failure. See the PR description for the full contrast table. */
}

.login-heading {
  margin: 0.35rem 0 0;
  font-family: var(--font-serif);
  font-weight: 700;
  font-size: 2rem;
  color: var(--navy);
}

.login-lede {
  margin: 0 0 0.5rem;
  font-family: var(--font-body);
  color: var(--midgray);
  font-size: 0.95rem;
}

.login-form {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  width: 100%;
}

/* C-11: the OIDC sign-in affordances. Same shape as .login-form so the two
   postures look like one product — never both on screen at once. Both now
   live inside .login-card, which supplies the visible card surface, so
   neither draws its own background/border any more. */
.oidc-signin {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  width: 100%;
}

.signin-lede {
  margin: 0;
  font-family: var(--font-body);
  font-size: 0.85rem;
  color: var(--midgray);
}

/* TF-1445: the invited-owner hint under the provider buttons. Same quiet footing as the lede. */
.signin-hint {
  margin: 0;
  font-family: var(--font-body);
  font-size: 0.8rem;
  line-height: 1.4;
  color: var(--midgray);
}

.provider-button {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  min-height: 40px;
  padding: 0.6rem 1rem;
  border: 1px solid var(--tan);
  border-radius: 6px;
  text-align: center;
  text-decoration: none;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 0.95rem;
  color: var(--nearblack);
  background: #ffffff;
}

.provider-button:hover {
  box-shadow: 0 1px 3px rgba(10, 22, 40, 0.18);
}

/* Google's own third-party sign-in button guidelines: white surface, this
   exact neutral border tone, the standard multicolor G mark, "Sign in with
   Google" in the button's own prescribed style. #dadce0 is Google's
   specified border color, not a True Fix brand token — reproduced literally
   because the compliance requirement is Google's, not ours. */
#signin-google {
  border-color: #dadce0;
}

.google-g {
  flex: 0 0 auto;
  display: block;
}

.login-form label {
  align-self: flex-start;
  font-family: var(--font-body);
  font-size: 0.85rem;
  color: var(--midgray);
}

.login-form input {
  padding: 0.65rem 0.8rem;
  border: 1px solid var(--tan);
  border-radius: 6px;
  font-family: var(--font-body);
  font-size: 1rem;
  background: #ffffff;
  color: var(--nearblack);
}

button {
  cursor: pointer;
  border: none;
  border-radius: 6px;
  padding: 0.6rem 1rem;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 0.95rem;
}

button:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

/* TF-126: the login submit button gets its own brand treatment, scoped to
   #login-form specifically so the shared `button` rule above — and every
   other button on the page, including #ask-button in the chat pipeline,
   which is explicitly out of scope — is untouched. */
#login-form button[type="submit"] {
  background: var(--navy);
  color: var(--lighttext);
  font-family: var(--font-body);
  font-weight: 600;
}

#login-form button[type="submit"]:hover:not(:disabled) {
  filter: brightness(1.15);
}

#login-section a:focus-visible,
#login-section button:focus-visible,
#login-section input:focus-visible {
  outline: 3px solid var(--navy);
  outline-offset: 2px;
}

/* TF-505 scope item 6: the server-rendered OIDC denial page
   (`cockpit/oidc_edge.py::render_denial_page`, TF-504). Gives it the SAME
   full-bleed navy backdrop and centered `.login-card` the sign-in screen
   uses, WITHOUT this page carrying `#login-section` itself (it is not the
   sign-in form) — three presentational hook classes only, never touching
   that page's copy, links, or the `.app-header` bar it deliberately keeps
   visible. */
.oidc-denial-body {
  background: var(--navy);
}

.oidc-denial-main {
  align-items: center;
  justify-content: center;
}

.oidc-denial-card {
  max-width: 440px;
}

.error-text {
  color: var(--refusal-ink);
  font-size: 0.85rem;
  min-height: 1.1em;
  margin: 0;
}

/* TF-126: the failure-state panel treatment. Scoped to #login-section only
   — .turn-error .error-text (the chat transcript's own system-error rows,
   `appendSystemError` in app.js) is chat message rendering and stays exactly
   as it was; this rule never reaches it. Covers all three failure paths
   (#login-error and #signin-error are the only two elements this selector
   can match), including the reportBrokenAssets() emergency path, since both
   write through plain textContent with no class changes. Collapses to
   nothing (:empty) so the card does not carry a permanent blank panel. */
#login-section .error-text {
  margin-top: 0.25rem;
  font-family: var(--font-body);
  font-size: 0.9rem;
  color: var(--nearblack);
}

#login-section .error-text:not(:empty) {
  padding: 0.65rem 0.85rem;
  border: 1px solid var(--tan);
  border-radius: 6px;
  background: var(--canvas);
  text-align: left;
}

.login-footer {
  margin: 0;
  text-align: center;
  color: var(--tan);
  font-family: var(--font-body);
  font-size: 0.8rem;
  line-height: 1.6;
}

.chat-layout {
  display: grid;
  grid-template-columns: 1fr 280px;
  grid-template-rows: minmax(0, 1fr);
  gap: 1.5rem;
  align-items: stretch;
  flex: 1 1 auto;
  min-height: 0;
}

.chat-main {
  display: flex;
  flex-direction: column;
  min-height: 0; /* lets .conversation scroll rather than push the ask bar down */
  position: relative; /* anchor for the floating "new response" affordance */
}

/* UAT-9: shown only when a response arrives while the reader has scrolled up.
   Floats just above the pinned ask bar; clicking it jumps to the newest turn. */
.new-response {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: 4.5rem;
  z-index: 2;
  font-size: 0.8rem;
  padding: 0.35rem 0.8rem;
  border-radius: 999px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.18);
}

@media (max-width: 720px) {
  .chat-layout {
    grid-template-columns: 1fr;
    grid-template-rows: minmax(0, 1fr) auto;
  }

  /* Narrow screens stack the sidebar under the transcript; cap it so the
     recent-queries list can't crowd out the conversation. */
  .chat-sidebar {
    max-height: 30vh;
  }
}

.conversation {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  min-height: 0;
  margin-bottom: 1rem;
  /* The ONLY scrolling region in the shell. */
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* TF-126: a branded empty-state, shown before the first question and never
   again — CSS-only, no JS. #conversation ships with zero children
   (<div id="conversation" ...></div>, no whitespace) and app.js only ever
   clears it back to that same state (`el.conversation.textContent = ""` in
   showLogin()); the FIRST appendChild of a real turn permanently disqualifies
   :empty for the rest of that session. This does not touch how a turn itself
   renders — .turn/.turn-body/.disposition-badge/.citation-list and the rest
   of the answer pipeline are untouched, out of scope, and this rule cannot
   match anything inside them. */
.chat-empty-state {
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  color: var(--midgray);
  font-family: var(--font-body);
  font-size: 0.9rem;
  text-align: center;
}

.chat-empty-mark {
  opacity: 0.55;
}

#conversation:empty {
  flex: 0 0 0;
  min-height: 0;
  margin-bottom: 0;
}

#conversation:empty + .chat-empty-state {
  display: flex;
  flex: 1 1 auto;
  min-height: 0;
}

.turn {
  border-radius: var(--radius);
  padding: 0.75rem 1rem;
  background: var(--paper);
  border: 1px solid var(--border);
}

.turn-user {
  background: #eef2fb;
  border-color: #d7e0f5;
}

/* BUG-1b: a submit that could not proceed renders as a turn in the transcript,
   so a failure is never invisible. Tinted toward the refusal palette so it
   reads as "this did not happen" at a glance rather than as an answer. */
.turn-error {
  background: #fdf0ee;
  border-color: #f3cfc8;
}

.turn-error .error-text {
  font-size: 0.95rem;
}

.turn-label {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--ink-soft);
  margin-bottom: 0.25rem;
}

.turn-body {
  white-space: pre-wrap;
  word-break: break-word;
}

.answer-text {
  margin-top: 0.5rem;
}

/* TF-476 — the safe-markdown-subset render path (cockpit/web/markdown.js)
   now inserts <p>/<ul>/<ol>/<li> instead of one flat text node. Reset their
   default UA margins/indent down to something that fits a chat bubble
   rather than a document; .turn-body's own `white-space: pre-wrap` still
   applies (inherited) and is harmless here since markdown.js no longer
   leaves a raw "\n" inside a paragraph's rendered text (it becomes an
   explicit <br> instead). */
.markdown-body p {
  margin: 0 0 0.5em;
}

.markdown-body p:last-child,
.markdown-body > *:last-child {
  margin-bottom: 0;
}

.markdown-body ul,
.markdown-body ol {
  margin: 0 0 0.5em;
  padding-left: 1.25em;
}

.markdown-body li {
  margin: 0.15em 0;
}

.markdown-body code {
  font-family: var(--font-mono, monospace);
  background: var(--tan, #f0ece4);
  border-radius: 3px;
  padding: 0.05em 0.3em;
  font-size: 0.9em;
}

/* TF-923 — the checked-facts display (cockpit/web/markdown.js,
   answerPartNodeSpecs). A fact or calculation is a value the app took from a
   record, so it reads as a record line: field, value, then where it came from.
   An interpretation is the model's own words and must look different: a dashed
   rule, its fixed label first, no emphasis on the text. A notice (stale,
   conflict, incomplete) is set apart from the value it qualifies. */
.answer-part {
  margin: 0 0 0.6em;
  white-space: normal;
}

.answer-part:last-child {
  margin-bottom: 0;
}

.answer-part-field {
  color: var(--ink-soft);
  margin-right: 0.4em;
}

.answer-part-value {
  font-weight: 600;
  font-family: var(--font-mono, monospace);
  overflow-wrap: anywhere;
}

.answer-part-value.is-text {
  font-family: inherit;
  font-weight: 400;
  font-style: italic;
}

.answer-part-source {
  display: block;
  font-size: 0.8rem;
  color: var(--ink-soft);
  overflow-wrap: anywhere;
}

.answer-part-notice {
  margin-top: 0.2em;
  padding: 0.2em 0.5em;
  font-size: 0.85rem;
  background: var(--refusal-bg);
  color: var(--refusal-ink);
  border-radius: 4px;
}

.answer-part-inputs {
  margin-top: 0.3em;
  font-size: 0.85rem;
}

.answer-part-inputs-toggle {
  cursor: pointer;
  color: var(--ink-soft);
}

.answer-part-input-list,
.answer-part-conflict-values {
  margin: 0.2em 0 0;
  padding-left: 1.25em;
}

.answer-part-conflict-values li {
  font-family: var(--font-mono, monospace);
  overflow-wrap: anywhere;
}

.answer-part-interpretation {
  border-left: 3px dashed var(--border);
  padding-left: 0.6em;
}

.answer-part-label {
  display: block;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--ink-soft);
}

.answer-part-text {
  overflow-wrap: anywhere;
}

.answer-part-withheld {
  font-size: 0.85rem;
  color: var(--ink-soft);
}

.disposition-badge {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0.2rem 0.55rem;
  border-radius: 999px;
}

.disposition-badge.is-answered {
  background: var(--answered-bg);
  color: var(--answered-ink);
}

.disposition-badge.is-refusal {
  background: var(--refusal-bg);
  color: var(--refusal-ink);
}

.disposition-badge.small {
  font-size: 0.7rem;
}

.citation-list {
  list-style: none;
  margin: 0.6rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.citation {
  font-size: 0.8rem;
  color: var(--ink-soft);
  word-break: break-word;
}

.citation-system {
  font-weight: 600;
}

/* TF-1150: an answer's sources sit behind one closed "Sources (n)" toggle. */
.citation-sources {
  margin: 0.6rem 0 0;
}

.citation-sources-toggle {
  cursor: pointer;
  font-size: 0.8rem;
  color: var(--ink-soft);
  width: fit-content;
}

.citation-sources-toggle:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}

.citation-sources .citation-list {
  margin-top: 0.35rem;
}

.citation-ref[href] {
  color: inherit;
  text-decoration: underline;
}

/* TF-1205: the Foreman conversation bar above the transcript, and the one-line note under an answer. The bar
   keeps its height with an empty status so the transcript does not jump when the first sentence appears. */
.conversation-bar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.4rem;
  min-height: 1.75rem;
}

.conversation-status {
  margin: 0;
  font-size: 0.8rem;
  color: var(--midgray);
  min-width: 0;
  overflow-wrap: anywhere;
}

.conversation-new {
  flex: 0 0 auto;
  background: none;
  border: 1px solid var(--midgray);
  border-radius: 999px;
  color: var(--ink);
  cursor: pointer;
  font: inherit;
  font-size: 0.8rem;
  padding: 0.25rem 0.8rem;
}

.conversation-new:hover {
  background: var(--paper);
}

.conversation-new:focus-visible,
.conversation-note-link:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}

.conversation-note {
  margin: 0.6rem 0 0;
  font-size: 0.8rem;
  color: var(--ink-soft);
}

.conversation-note-link {
  color: inherit;
  text-decoration: underline;
}

.turn-divider {
  align-self: stretch;
  text-align: center;
  font-size: 0.75rem;
  color: var(--midgray);
  background: none;
  border: 0;
  border-top: 1px solid var(--border);
  border-radius: 0;
  padding: 0.4rem 0 0;
}

@media (max-width: 600px) {
  .conversation-new {
    min-height: 44px;
    min-width: 44px;
  }
  .conversation-note-link {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

.ask-form {
  flex: 0 0 auto; /* pinned at the bottom of the chat column */
  display: flex;
  gap: 0.5rem;
  align-items: flex-end;
  /* TF-126: matches the app shell's new --canvas backdrop (was --bg) so the
     ask bar doesn't sit on a mismatched gray strip; the input/button inside
     it are unchanged. */
  background: var(--canvas);
  padding-top: 0.5rem;
  border-top: 1px solid var(--border);
}

.ask-form textarea {
  flex: 1;
  resize: vertical;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  font: inherit;
}

.chat-sidebar {
  min-height: 0;
  overflow-y: auto; /* scrolls independently; never pushes the shell taller */
}

.chat-sidebar h2 {
  font-size: 0.9rem;
  color: var(--ink-soft);
  margin: 0 0 0.5rem;
}

.recent-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.recent-item {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  background: var(--paper);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.5rem 0.65rem;
  font-size: 0.85rem;
}

.recent-query-text {
  word-break: break-word;
}

.recent-empty {
  color: var(--ink-soft);
  font-size: 0.85rem;
}

/* ---------------------------------------------------------------------
   C-201 (TF-98) — the Owner's Dashboard. Additive only: everything above
   this line is the chat shell (untouched). Reuses the TF-126 brand tokens
   (--navy/--amber/--cream/--canvas/--tan/--midgray/--nearblack/--lighttext/
   --font-*) rather than inventing new ones.
   --------------------------------------------------------------------- */

.dashboard-body {
  background: var(--canvas);
}

.dashboard-main {
  width: 100%;
  max-width: 1100px;
  margin: 0 auto;
  padding: 1.5rem;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

.dashboard-auth-gate {
  max-width: 480px;
  margin: 3rem auto;
  padding: 2rem;
  background: var(--cream);
  border-bottom: 3px solid var(--amber);
  border-radius: var(--radius);
  text-align: center;
  font-family: var(--font-body);
}

.dashboard-auth-message {
  color: var(--nearblack);
  margin: 0 0 0.75rem;
}

.dashboard-signin-link {
  color: var(--navy);
  font-weight: 600;
}

/* TF-1143: the "Reload" button in the Crew suggestions can't-load state reads like the link beside it. */
button.dashboard-signin-link {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-weight: 600;
  text-decoration: underline;
  cursor: pointer;
}

.dashboard-grid {
  display: grid;
  /* TF-314: closes TF-252's deviation #2 — the V1-1 wireframe's own
     12-column grid, now that `layout.span` exists on the spec schema to
     drive it (widget_schema.js). `.widget-tile`'s own default `grid-column:
     span 4` below is the wireframe's un-suffixed `.card`; a spec with no
     `layout.span` at all renders at that same width, unchanged from the
     pre-TF-314 auto-fill layout this replaces. */
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 0.9rem;
  align-items: start;
}

/* TF-314: closes TF-252's deviation #1 — the wireframe's own `.qhead`
   section header, a full-width row inside the SAME flat `.dashboard-grid`
   (dashboard.js's renderDashboard groups tiles by `layout.section` but
   keeps every tile a direct grid child — see that function's own comment
   for why). */
.dashboard-qhead {
  grid-column: 1 / -1;
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  margin: 0.4rem 0 0;
}

.dashboard-qhead:first-child {
  margin-top: 0;
}

.dashboard-qhead-title {
  font-family: var(--font-body);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: var(--navy);
}

.dashboard-qhead-sub {
  font-family: var(--font-body);
  font-size: 0.75rem;
  color: var(--midgray);
}

/* Lane B Major 4b: an explicit message when the payload resolves to zero
   tiles, rather than a silent blank grid. */
.dashboard-empty-message {
  grid-column: 1 / -1;
  padding: 2rem;
  text-align: center;
  color: var(--midgray);
  font-family: var(--font-body);
}

.widget-tile {
  background: var(--paper);
  border: 1px solid var(--border);
  /* TF-252: V1-1 wireframe's own `.card` radius (12px), a dedicated
     override rather than raising the shared `--radius` (10px) token, which
     also drives buttons/panels/forms this card does not touch. */
  border-radius: 12px;
  /* TF-252: matches the design preview's own `.card` shadow token
     (`0 1px 2px rgba(26,18,8,.05)`) so a tile reads the same weight of
     "raised surface" the operator console's cards already carry. */
  box-shadow: 0 1px 2px rgba(26, 18, 8, 0.05);
  padding: 1rem 1.15rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-height: 120px;
  font-family: var(--font-body);
  position: relative; /* TF-268: anchor for the "Configure" corner button */
  /* TF-314: the wireframe's own un-suffixed `.card` default — a spec with no
     `layout.span` (every pre-TF-314 spec) renders at exactly this width. */
  grid-column: span 4;
}

/* TF-314: the wireframe's own `.span6`/`.span8`/`.span12` modifiers,
   applied by dashboard.js's tileClassName() from `layout.span`. */
.widget-tile--span-6 {
  grid-column: span 6;
}

.widget-tile--span-8 {
  grid-column: span 8;
}

.widget-tile--span-12 {
  grid-column: span 12;
}

/* TF-314: mirrors the wireframe's own sub-1100px breakpoint (`.card,
   .card.span6, .card.span8 { grid-column: span 12 }`) — below this width a
   6- or 8-wide tile is narrower in practice than the wireframe's own design
   assumed, so every span collapses to full-width, one tile per row, rather
   than rendering cramped. `.widget-tile--span-12` is already full-width and
   needs no rule here. */
@media (max-width: 900px) {
  .widget-tile,
  .widget-tile--span-6,
  .widget-tile--span-8 {
    grid-column: span 12;
  }
}

.widget-tile--needs-update {
  background: var(--tan);
  border-style: dashed;
}

.widget-tile--error {
  background: var(--refusal-bg);
  border-color: #f3cfc8;
}

.widget-tile--empty {
  color: var(--ink-soft);
}

/* TF-1643 (D8 of TF-1620): the four tile states each have their own look as well as their own words. The lead line
   sits under the tile title; the reason sentence a tile already carried stays under it. Empty is quiet, unavailable is
   dashed (the tile's own border is already dashed), stale carries an amber edge, error keeps the tile's pink. */
.widget-state {
  font-size: 0.85rem;
  font-weight: 600;
}

.widget-state--empty {
  color: var(--ink-soft);
}

.widget-state--unavailable {
  color: var(--ink-soft);
  font-style: italic;
}

/* Empty is a calm, finished tile (no box round its sentence); unavailable has a solid grey edge and keeps the dashed box
   round its reason, so the two differ in shape and not only in words or colour. */
.widget-tile--state-empty .widget-empty-state {
  border: 0;
  padding: 0.25rem 0;
  text-align: left;
}

.widget-tile--state-unavailable {
  border-left: 4px solid #8a8f98;
}

.widget-state--stale {
  color: #7a4b00;
}

.widget-tile--stale {
  border-left: 4px solid #d99a2b;
}

.widget-state--error {
  color: var(--refusal-ink);
}

/* TF-570: a tile whose read is still in flight. It keeps the tile's own
   span and min-height, so the grid doesn't shift as tiles land. */
.widget-tile--loading .widget-loading-note {
  color: var(--midgray);
  font-size: 0.85rem;
}

.widget-title {
  font-family: var(--font-serif);
  font-size: 1rem;
  color: var(--navy);
}

/* TF-252: found on the real running dashboard (a long wrapping title —
   "Days Sales Outstanding (new metric, unbanded)" in the demo fixture set —
   ran its second line straight under the absolutely-positioned "Configure"
   pill). Reserves the pill's own footprint out of the title's line box,
   scoped to tiles that actually carry the button so a tile without one
   (error/needs_update kinds never get it) keeps its full-width title. */
.widget-tile:has(.widget-configure-button) .widget-title {
  padding-right: 4.25rem;
}

.widget-needs-update-detail {
  font-size: 0.85rem;
  color: var(--midgray);
}

/* TF-252: matches the V1-1 wireframe's own `.empty` refusal treatment
   (dashed box, centered, muted text) — a `no_metric_mapped`/`no_rows`/
   `stale_beyond_threshold`/`refused`/`thresholds_not_set` empty state reads
   as a deliberate refusal, never as a blank or broken tile. Wording itself
   is untouched (EMPTY_STATE_LABELS in dashboard.js) — presentation only,
   per this card's own honest-state-semantics boundary. */
.widget-empty-state {
  font-size: 0.85rem;
  color: var(--midgray);
  text-align: center;
  border: 1px dashed var(--tan);
  border-radius: 10px;
  padding: 0.85rem 0.75rem;
}

.widget-error-banner {
  font-size: 0.85rem;
  color: var(--refusal-ink);
}

.widget-retry-button {
  align-self: flex-start;
  background: var(--navy);
  color: var(--lighttext);
  font-family: var(--font-body);
  font-size: 0.8rem;
  padding: 0.4rem 0.75rem;
}

/* TF-268 (UX-F5): the discoverable per-tile "Configure" affordance — a
   small corner button on every populated/empty grid tile, wired (in
   foreman_rail.js) to fill and submit the SAME `configure <title>` rail
   command a user previously had to know to type by hand. */
.widget-configure-button {
  position: absolute;
  top: 0.6rem;
  right: 0.65rem;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--midgray);
  font-family: var(--font-body);
  font-size: 0.7rem;
  line-height: 1;
  padding: 0.25rem 0.6rem;
  cursor: pointer;
}

.widget-configure-button:hover,
.widget-configure-button:focus-visible {
  color: var(--navy);
  border-color: var(--navy);
}

.widget-kpi-value {
  font-family: var(--font-mono);
  font-size: 1.75rem;
  color: var(--nearblack);
}

/* TF-252: the V1-1 wireframe's own `.card-foot` pattern — chips + drill
   link + as-of stamp pinned to the bottom of the card behind a divider,
   rather than sitting wherever it falls in normal document flow. */
.widget-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  align-items: center;
  margin-top: auto;
  padding-top: 0.5rem;
  border-top: 1px solid var(--border);
}

/* TF-252: the wireframe's explicit "Ask the Foreman about this →" per-tile
   affordance. Added only for the tile types whose body already carries
   `data-drilldown-widget-id` (kpi_scalar, threshold_band, milestone_elapsed
   — see dashboard.js's own DRILLDOWN_HINT_TYPES) so this text is never
   painted onto a tile that isn't actually clickable. Nested inside the
   already-clickable container: no new attribute, no new listener, no
   behavior change — purely a visible label for a click path that already
   exists (findDrilldownTarget walks the parentNode chain regardless of
   which descendant node the click landed on).

   TF-483: now a real `<button>` (dashboard.js), so the generic `button {}`
   rule earlier in this file (background/border/padding/cursor) would
   otherwise paint it as a solid pill — reset those four properties back to
   the plain-text-link look this affordance has always had, the same way
   `.widget-configure-button`/`.widget-retry-button` already reset them for
   their own button elements. */
.widget-drill-hint {
  background: transparent;
  border: none;
  padding: 0;
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 650;
  color: var(--brass-deep, #7a4e00);
  /* Renders as its own row, right-aligned, directly under the chip row —
     `align-self`, not `margin-left: auto`, because `.widget-tile` is a
     COLUMN flex container: this is a cross-axis alignment, not a main-axis
     one. */
  align-self: flex-end;
  white-space: nowrap;
  cursor: pointer;
}

.widget-drill-hint:hover {
  text-decoration: underline;
}

/* TF-483: the same visible-focus-ring idiom `.app-header a/button`,
   `#login-section`'s controls and `.foreman-rail-toggle` already use in
   this file (a solid amber outline, not the browser default), so a
   keyboard user tabbing across the dashboard grid sees exactly the same
   focus treatment on this control as on every other one. */
.widget-drill-hint:focus-visible {
  outline: 3px solid var(--amber);
  outline-offset: 2px;
  text-decoration: underline;
}

.widget-chip {
  font-size: 0.7rem;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  background: var(--canvas);
  color: var(--midgray);
  border: 1px solid var(--tan);
}

.widget-chip--stale {
  background: #fdf1ee;
  color: var(--refusal-ink);
  border-color: #f3cfc8;
}

.widget-chip--pinned {
  background: var(--cream);
  border-color: var(--amber);
  color: var(--nearblack);
}

.widget-freshness {
  font-size: 0.7rem;
  color: var(--ink-soft);
  font-family: var(--font-mono);
}

/* TF-486: the wireframe's `.card-foot .asof` "refused, not zero" honesty
   note on the unmapped tile — same small/muted/mono treatment as
   `.widget-freshness` just above (this file's own existing footer-text
   pattern), right-aligned. A direct child of `.widget-tile` (a COLUMN flex
   container, `display:flex; flex-direction:column;`, default
   `align-items: stretch`), so this div already spans the tile's full width
   and `text-align: right` places the text at the right edge without
   needing `.widget-drill-hint`'s `align-self` trick (that trick aligns a
   flex ITEM within its own immediate container's cross axis — right for
   `.widget-drill-hint`'s nested position inside the row-direction
   `.widget-chip-row`, not for this node, which sits OUTSIDE that row,
   directly under the reason sentence). */
.widget-honesty-note {
  font-size: 0.7rem;
  color: var(--ink-soft);
  font-family: var(--font-mono);
  text-align: right;
}

.widget-trend-list,
.widget-leaderboard-list,
.widget-queue-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  font-size: 0.85rem;
}

.widget-trend-point,
.widget-leaderboard-row {
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
}

.widget-leaderboard-rank {
  font-family: var(--font-mono);
  color: var(--midgray);
  width: 1.5rem;
}

.widget-queue-row {
  padding: 0.2rem 0;
  border-bottom: 1px dashed var(--border);
}

.widget-threshold-band-body {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.widget-band {
  align-self: flex-start;
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
}

.widget-band--green {
  background: var(--answered-bg);
  color: var(--answered-ink);
}

.widget-band--amber {
  background: #fff4e0;
  color: #8a5a12;
}

.widget-band--red {
  background: var(--refusal-bg);
  color: var(--refusal-ink);
}

/* TF-144 (C-202a) — benchmark_comparison ---------------------------------- */

.widget-benchmark-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  font-size: 0.85rem;
}

.widget-benchmark-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}

.widget-benchmark-label {
  color: var(--midgray);
  flex: 1 1 auto;
}

.widget-benchmark-self-value {
  font-weight: 600;
}

.widget-benchmark-value {
  font-family: var(--font-mono);
  color: var(--ink-soft);
}

.widget-benchmark-distance {
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
}

.widget-benchmark-distance--ahead {
  background: var(--answered-bg);
  color: var(--answered-ink);
}

.widget-benchmark-distance--behind {
  background: var(--refusal-bg);
  color: var(--refusal-ink);
}

/* TF-144 (C-202a) — milestone_elapsed -------------------------------------- */

.widget-milestone-body {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.widget-milestone-pair {
  font-size: 0.75rem;
  color: var(--midgray);
}

.widget-milestone-target {
  font-size: 0.75rem;
  color: var(--ink-soft);
  font-family: var(--font-mono);
}

.widget-milestone-warning {
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  align-self: flex-start;
  background: #fff4e0;
  color: #8a5a12;
}

/* TF-145 (C-202b) — aging_list, exception_list (single_number reuses
   .widget-kpi-value, no new rule needed) ------------------------------- */

.widget-aging-list,
.widget-exception-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  font-size: 0.85rem;
}

.widget-aging-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}

.widget-aging-label {
  color: var(--midgray);
  flex: 1 1 auto;
}

.widget-aging-value {
  font-family: var(--font-mono);
  color: var(--nearblack);
}

.widget-exception-row {
  padding: 0.2rem 0;
  border-bottom: 1px dashed var(--border);
}

/* TF-103 (C-206) — the drill-down affordance. `[data-drilldown-widget-id]`
   marks every clickable "ask about this" surface dashboard.js renders
   (kpi_scalar/threshold_band/milestone_elapsed's whole body, plus
   aging_list's own red-count control below); the click LISTENER lives in
   foreman_rail.js, this file only has to look clickable. ------------------ */

[data-drilldown-widget-id] {
  cursor: pointer;
}

.widget-aging-body {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.widget-aging-red-count {
  align-self: flex-start;
  background: var(--refusal-bg);
  color: var(--refusal-ink);
  border: none;
  font-family: var(--font-body);
  font-size: 0.8rem;
  font-weight: 600;
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
}

/* ---------------------------------------------------------------------
   C-205 (TF-101) — the Foreman chat rail. Fixed-position, layered above
   .dashboard-main's own scroll region rather than inside it, so opening it
   never depends on — or fights with — that container's height (see the
   comment on #foreman-rail in dashboard.html for the TF-145 lesson this
   sidesteps). Reuses the chat-shell's own turn/disposition-badge/citation
   tokens from the section above so a rail answer and index.html's own
   chat answer look identical, and every widget-tile token from the
   dashboard section above so a reused tile node-spec (AC 3/AC 4's byte-
   identical tile-parity mechanism) renders pixel-identical here too.
   --------------------------------------------------------------------- */

.foreman-rail-toggle {
  position: fixed;
  right: 1.5rem;
  bottom: 1.5rem;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: var(--navy);
  color: var(--lighttext);
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 600;
  padding: 0.65rem 1.1rem;
  border-radius: 999px;
  border: 2px solid var(--amber);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.25);
}

.foreman-rail-toggle:focus-visible {
  outline: 3px solid var(--amber);
  outline-offset: 2px;
}

/* TF-252: while the rail is open, `#foreman-rail-collapse` ("Close", inside
   the rail's own header) is the one control that closes it — the floating
   toggle stays in markup (foreman_rail.js never hides it) but would
   otherwise sit visually on top of the now-full-height dock below. Presentation
   only: no attribute changes, `el.toggle.hidden` in foreman_rail.js is
   untouched. */
body:has(#foreman-rail:not([hidden])) .foreman-rail-toggle {
  display: none;
}

/* TF-252 (V1-1 brief section 7, "Layout"): "Persistent collapsible rail,
   right side, present on every dashboard view... one click to open" — a
   full-height dock, not a floating chat bubble. Converts the rail from a
   bottom-right popover into the wireframe's own right-side panel spanning
   the full viewport height; `.dashboard-main` cedes width to it below via
   `:has()` (pure CSS — no JS change, same technique this file already uses
   for the login/chat shell backdrops above). */
.foreman-rail {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  width: min(380px, 100vw);
  height: 100vh;
  /* TF-1377: on a phone 100vh is taller than the visible screen while the
     browser's address bar shows, which puts the Ask button under it. */
  height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border: none;
  border-left: 1px solid var(--border);
  border-radius: 0;
  box-shadow: -6px 0 24px rgba(0, 0, 0, 0.18);
  overflow: hidden;
}

/* Reserves room for the open rail rather than letting it cover the last
   grid column — mirrors the wireframe's own `.main.rail-open { margin-right:
   var(--rail-w) }`. Applied to `body` (the flex COLUMN container), not to
   `.dashboard-main` itself: `.dashboard-main` centers a `max-width: 1100px`
   box via `margin: 0 auto` inside body's full width, and `#foreman-rail` is
   `position: fixed` (out of normal flow) — a `margin-right` added directly
   to `.dashboard-main` does not shrink that box's own width, so its
   centered content simply keeps its pre-rail width and renders UNDER the
   fixed-position rail rather than reflowing narrower (caught on the real
   running page, not assumed from the CSS alone — the whole reason this
   card's own gate requires a real click-through). Padding the flex
   CONTAINER shrinks the space `.dashboard-main`'s own width/centering is
   computed against, which reflows correctly; `.app-header` (body's other
   flex child, not nested inside `.dashboard-main`) narrows the same way,
   which is the correct read of the wireframe's own layout too — its rail
   sits beside the whole page, header included. Narrow viewports (where the
   rail already goes full-width via the min() above) skip the push,
   matching the wireframe's own sub-1100px breakpoint collapsing the rail to
   a stacked, non-overlaying block instead. */
@media (min-width: 860px) {
  body:has(#foreman-rail:not([hidden])) {
    padding-right: min(380px, 100vw);
  }
}

.foreman-rail-header {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--navy);
  color: var(--lighttext);
  padding: 0.65rem 0.9rem;
  border-bottom: 2px solid var(--amber);
}

.foreman-rail-header h2 {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 1rem;
  color: var(--lighttext);
}

.foreman-rail-header .link-button {
  color: var(--amber);
}

.foreman-rail-conversation {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.foreman-rail-ask-form {
  flex: 0 0 auto;
  display: flex;
  gap: 0.5rem;
  align-items: flex-end;
  background: var(--canvas);
  padding: 0.6rem 0.75rem;
  border-top: 1px solid var(--border);
}

.foreman-rail-ask-form textarea {
  flex: 1;
  resize: vertical;
  padding: 0.5rem 0.65rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  font: inherit;
  font-size: 0.85rem;
}

.rail-metric-tile-wrap {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.rail-metric-tile-wrap .widget-tile {
  border: none;
  border-radius: 0;
  box-shadow: none;
}

.rail-work-item-offer {
  margin-top: 0.4rem;
  font-size: 0.8rem;
  color: var(--ink-soft);
}

.foreman-preview-slot {
  margin-bottom: 1rem;
  padding: 1rem;
  background: var(--cream);
  border: 2px dashed var(--amber);
  border-radius: var(--radius);
}

.foreman-preview-badge {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--navy);
  margin-bottom: 0.5rem;
}

.foreman-preview-error {
  color: var(--refusal-ink);
  font-size: 0.85rem;
}

.foreman-preview-clear {
  margin-top: 0.6rem;
  background: transparent;
  border: 1px solid var(--navy);
  color: var(--navy);
  font-family: var(--font-body);
  font-size: 0.8rem;
  padding: 0.35rem 0.75rem;
  border-radius: 6px;
}

/* TF-103 (C-206) — the Tier 2 picker panel. Reuses the rail's own turn/
   preview-badge tokens above so it reads as part of the same conversation,
   not a second visual language. ------------------------------------------ */

.tier2-panel {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  background: var(--cream);
  border: 1px solid var(--tan);
  border-radius: var(--radius);
  padding: 0.75rem;
}

.tier2-field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.tier2-field-label {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--midgray);
}

.tier2-metric-options,
.tier2-dim-options,
.tier2-window-options {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}

.tier2-option {
  background: var(--paper);
  border: 1px solid var(--border);
  color: var(--nearblack);
  font-family: var(--font-body);
  font-size: 0.75rem;
  padding: 0.25rem 0.65rem;
  border-radius: 999px;
}

.tier2-option--selected {
  background: var(--navy);
  border-color: var(--navy);
  color: var(--lighttext);
}

.tier2-green-max,
.tier2-amber-max,
.tier2-warn-offset {
  width: 6rem;
  padding: 0.3rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  font: inherit;
}

.tier2-preview-slot {
  padding: 0.75rem;
  background: var(--paper);
  border: 2px dashed var(--amber);
  border-radius: var(--radius);
}

.tier2-errors {
  color: var(--refusal-ink);
  font-size: 0.8rem;
}

.tier2-empty-note {
  color: var(--midgray);
  font-size: 0.8rem;
}

.tier2-actions {
  display: flex;
  gap: 0.5rem;
}

.tier2-confirm-button {
  background: var(--navy);
  color: var(--lighttext);
  border: none;
  font-family: var(--font-body);
  font-size: 0.8rem;
  padding: 0.4rem 0.85rem;
  border-radius: 6px;
}

.tier2-preview-button,
.tier2-discard-button {
  background: transparent;
  border: 1px solid var(--navy);
  color: var(--navy);
  font-family: var(--font-body);
  font-size: 0.8rem;
  padding: 0.4rem 0.85rem;
  border-radius: 6px;
}

.tier2-rollback-button {
  margin-top: 0.4rem;
  background: transparent;
  border: 1px solid var(--navy);
  color: var(--navy);
  font-family: var(--font-body);
  font-size: 0.75rem;
  padding: 0.3rem 0.65rem;
  border-radius: 6px;
}

/* TF-312 (C-208c) — the Tier 3 authoring confirm-before-save panel. Mirrors
   .tier2-panel's own tokens exactly (same conversation-turn visual
   language), kept as its own named class rather than reusing "tier2-*" so
   the two unrelated features stay distinguishable in devtools. ---------- */

.authoring-panel {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  background: var(--cream);
  border: 1px solid var(--tan);
  border-radius: var(--radius);
  padding: 0.75rem;
}

.authoring-errors {
  color: var(--refusal-ink);
  font-size: 0.8rem;
}

.authoring-actions {
  display: flex;
  gap: 0.5rem;
}

.authoring-confirm-button {
  background: var(--navy);
  color: var(--lighttext);
  border: none;
  font-family: var(--font-body);
  font-size: 0.8rem;
  padding: 0.4rem 0.85rem;
  border-radius: 6px;
}

.authoring-discard-button {
  background: transparent;
  border: 1px solid var(--navy);
  color: var(--navy);
  font-family: var(--font-body);
  font-size: 0.8rem;
  padding: 0.4rem 0.85rem;
  border-radius: 6px;
}

.tier2-rollback-confirm {
  margin-top: 0.3rem;
  font-size: 0.75rem;
  color: var(--ink-soft);
}

/* ---------------------------------------------------------------------
   TF-170 (V1-3a) — Operator Console shell (TF-26, child 1 of 6). A
   DIFFERENT page from the Owner's Dashboard above (C-201/TF-98): this is
   the design-preview-governed console at docs/loop/design/
   TF-26_operator_console_preview.html, reusing the SAME brand tokens
   (--navy/--amber/--cream/--canvas/--tan/--midgray/--nearblack/--lighttext/
   --font-*) and the app-header/dashboard-auth-gate rules above rather than
   inventing a second brand language. `.chip` / `.askbtn` / `.preview-badge`
   are new — the preview defines them inline; this file gives them the same
   names so a future panel story's own markup can keep using them without
   guessing at new class names.
   --------------------------------------------------------------------- */

.operator-console-body {
  background: var(--canvas);
}

.operator-console-body .app-header {
  flex-wrap: wrap;
  row-gap: 0.4rem;
}

.chip {
  font-size: 0.8rem;
  font-weight: 600;
  padding: 0.25rem 0.65rem;
  border-radius: 999px;
  background: rgba(200, 136, 42, 0.18);
  color: var(--amber);
  border: 1px solid rgba(200, 136, 42, 0.5);
}

.askbtn {
  font-size: 0.85rem;
  font-weight: 700;
  text-decoration: none;
  padding: 0.45rem 1rem;
  border-radius: 6px;
  background: var(--amber);
  color: var(--navy);
  white-space: nowrap;
}

.askbtn:hover {
  filter: brightness(1.1);
}

.preview-badge {
  margin-left: auto;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  padding: 0.3rem 0.65rem;
  border-radius: 4px;
  text-transform: uppercase;
  background: var(--cream);
  color: var(--brass-deep, #7a4e00);
  border: 1px dashed var(--amber);
}

/* The real, live badge — solid rather than dashed, so it never reads as the
   design mockup's own "not real" treatment. */
.preview-badge--live {
  border-style: solid;
  background: rgba(12, 163, 12, 0.14);
  color: #0c5c0c;
  border-color: #0c5c0c;
}

.operator-console-body .asof {
  font-size: 0.75rem;
  color: var(--tan);
  width: 100%;
}

.operator-console-main {
  width: 100%;
  max-width: 1180px;
  margin: 0 auto;
  padding: 1.5rem;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

.operator-console-modules {
  display: flex;
  gap: 0.75rem;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}

.modchip {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--midgray);
  background: var(--paper);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.5rem 0.9rem;
  cursor: pointer;
  user-select: none;
}

.modchip input {
  accent-color: var(--brass-deep, #7a4e00);
}

/* TF-295 (UX-C4/F15): the toggle above persists nothing (it only flips a
   body class for the current view), so this qualifier keeps it from reading
   as a saved client setting. */
.modchip-hint {
  font-size: 0.75rem;
  font-style: italic;
  color: var(--midgray);
}

/* ---- KPI row: container + loading/empty states only. A tile's real VALUE
   is a separate, still-blocked panel story — see operator_console.js's own
   module docstring for why no `.kpi--value` state exists here yet. ---- */

.kpis {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 0.9rem;
  margin: 0 0 1rem;
}

@media (max-width: 900px) {
  .kpis {
    grid-template-columns: repeat(2, 1fr);
  }
}

.kpi {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.kpi-label {
  font-size: 0.75rem;
  color: var(--midgray);
  font-family: var(--font-body);
}

.kpi-value {
  font-size: 1.75rem;
  font-weight: 700;
  font-family: var(--font-mono);
  color: var(--nearblack);
  line-height: 1.05;
}

.kpi--loading .kpi-value {
  color: var(--ink-soft);
}

.kpi-note {
  font-size: 0.75rem;
  color: var(--midgray);
}

/* TF-173 (V1-3d) — a KPI tile patched with a REAL value (patchKpiTileValue,
   operator_console.js), distinct from the generic "empty" placeholder dash
   state above — same "one class per state" discipline the audit strip
   below establishes, rather than reusing .kpi--empty's class for a tile
   that now carries real data. */
.kpi--value .kpi-value {
  color: var(--nearblack);
}

/* Module toggle: the preview's own literal mechanism, reused as-is so any
   future panel's own pg-only/pg-off markup (activity feed rows, the permit
   pipeline panel, ...) is gated by this SAME rule pair rather than a second
   one. */
body.no-genie .pg-only {
  display: none !important;
}

body:not(.no-genie) .pg-off {
  display: none !important;
}

/* ---------------------------------------------------------------------
   TF-173 (V1-3d) — Foreman Q&A analytics (TF-26, child 4 of 6): the 14-day
   "questions your team asked" bar chart and the "how the Foreman answered"
   disposition mix. Renders operator_console.js's own
   renderQaDailyChart()/renderQaDisposition() output. Boxed the same way
   .audit-strip below is (border/background/radius) — there is no shared
   `.card` chrome rule in this file to reuse (see operator_console.js's own
   KPI-tile comment: `class="card kpi"` predates this file having a `.card`
   rule at all, a pre-existing gap, not this card's to fix).
   --------------------------------------------------------------------- */

.operator-console-charts {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.9rem;
  margin-top: 1rem;
}

@media (max-width: 900px) {
  .operator-console-charts {
    grid-template-columns: 1fr;
  }
}

.qa-daily-chart,
.qa-disposition {
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--paper);
  padding: 0.9rem 1.1rem 1.1rem;
}

.qa-daily-chart h2,
.qa-disposition h2 {
  font-size: 0.95rem;
  margin: 0 0 0.7rem;
  color: var(--nearblack);
}

.qa-chart-status {
  font-size: 0.8rem;
  color: var(--midgray);
}

/* A genuinely empty window (window_total === 0) and a route this file could
   not reach are TWO DISTINCT classes/colors — never conflated, same
   discipline as the audit strip's own failed/unavailable pair below. */
.qa-chart-status--unavailable {
  color: #8f1f1f;
}

/* ---- 14-day bar chart ---- */

.qa-bars {
  display: flex;
  align-items: flex-end;
  gap: 0.3rem;
  height: 120px;
}

.qa-bar-col {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  height: 100%;
}

.qa-bar-track {
  flex: 1 1 auto;
  width: 100%;
  display: flex;
  align-items: flex-end;
}

/* height is set inline, per-bar, by operator_console.js's own
   buildQaDailyChartElement — a real CSSOM property assignment (never a
   template string built into markup) driven by a real per-day count. */
.qa-bar {
  width: 100%;
  min-height: 2px;
  background: var(--amber);
  border-radius: 3px 3px 0 0;
}

.qa-bar-label {
  font-size: 0.6rem;
  color: var(--midgray);
  white-space: nowrap;
}

.qa-chart-legend {
  margin-top: 0.6rem;
  font-size: 0.72rem;
  color: var(--midgray);
}

/* ---- disposition stacked bar ---- */

.qa-disposition-bar {
  display: flex;
  height: 26px;
  border-radius: 6px;
  overflow: hidden;
  background: var(--tan);
}

.qa-disposition-segment {
  height: 100%;
}

/* Three distinct brand-consistent tokens, one per bucket — never a shared
   color between "answered" and either refusal reason, and never a shared
   color between the two refusal reasons either, so AC 4's "a refusal is
   counted as a refusal, never folded into answered" is visually true, not
   only true in the underlying counts. */
.qa-disp--answered {
  background: var(--amber);
}

.qa-disp--no-match {
  background: #2a78d6;
}

.qa-disp--unreachable {
  background: #2e7d4f;
}

.qa-disposition-legend {
  list-style: none;
  margin: 0.6rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.qa-disposition-legend-item {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.78rem;
  color: var(--nearblack);
}

.qa-disposition-swatch {
  display: inline-block;
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 2px;
  flex: 0 0 auto;
}

.qa-disposition-note {
  margin: 0.6rem 0 0;
  font-size: 0.72rem;
  color: var(--midgray);
}

/* ---------------------------------------------------------------------
   TF-172 (V1-3b) — the automation activity feed (TF-26, child 3 of 6).
   Renders operator_console.js's own renderAutomationFeed() output. A failed
   outcome always gets its OWN dot color (automation-feed-dot--failure),
   independent of waiting/rejected, so a failure is never visually
   indistinguishable from a clean run that merely took a different gate
   path — same "a broken state must never share a color with a healthy one"
   discipline as the audit strip block below. The KPI tile's own
   .kpi--flagged rule (>= 1 failure today) lives at the end of this block,
   next to the KPI row rules it patches.
   --------------------------------------------------------------------- */

.operator-console-automation-feed {
  margin: 0 0 1rem;
  padding: 0.9rem 1.1rem;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--paper);
}

.operator-console-automation-feed .section-heading {
  font-size: 0.75rem;
  font-weight: 650;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  color: var(--midgray);
  margin: 0 0 0.6rem;
}

.section-heading-live {
  float: right;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  color: var(--midgray);
}

.automation-feed-item {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 0.6rem 0;
  border-top: 1px solid var(--tan);
  font-size: 0.82rem;
}

.automation-feed-item:first-child {
  border-top: none;
}

.automation-feed-time {
  flex: 0 0 58px;
  color: var(--midgray);
  font-variant-numeric: tabular-nums;
  font-size: 0.75rem;
  padding-top: 0.1rem;
}

.automation-feed-dot {
  flex: 0 0 10px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  margin-top: 0.25rem;
}

/* Four distinct colors, one per dot variant — no shared token between
   "good" and "failure" the way a single "status: boolean" flag would risk. */
.automation-feed-dot--good {
  background: #0c5c0c;
}

.automation-feed-dot--waiting {
  background: var(--amber, #c8882a);
}

.automation-feed-dot--failure {
  background: #d03b3b;
}

.automation-feed-dot--rejected {
  background: var(--midgray);
}

.automation-feed-body {
  flex: 1 1 auto;
  min-width: 0;
}

.automation-feed-headline {
  color: var(--nearblack);
}

.automation-feed-note {
  font-size: 0.72rem;
  color: var(--midgray);
  margin-top: 0.15rem;
}

.automation-feed-empty {
  font-size: 0.82rem;
  color: var(--midgray);
  padding: 0.4rem 0;
}

/* AC4's own "not the unavailable case" distinction, made visual: a genuinely
   empty result reads as ordinary muted text; an unreachable/unconfigured
   source reads red, matching the audit strip's own failed/unavailable
   color. */
.automation-feed-empty--unavailable {
  color: #8f1f1f;
}

/* The "Automations run today" KPI tile, patched in place by
   applyAutomationsKpiTile() once GET /engine/automation-feed answers.
   .kpi--flagged is added ONLY when failures_today >= 1 (AC2's own "visibly
   flagged") — a second, independent visual signal beyond the note text's
   own wording, same discipline as the audit strip's border-color shift on a
   failed chain below. */
.kpi--flagged .kpi-value {
  color: #8f1f1f;
}

.kpi--flagged .kpi-note {
  color: #8f1f1f;
  font-weight: 650;
}

/* ---------------------------------------------------------------------
   TF-174 (V1-3e) — the run-log chain-verify audit strip (TF-26, child 5 of
   6). Renders operator_console.js's own renderAuditStrip() output — see
   that file's module comment for why "failed" gets its OWN status class and
   OWN color token here rather than the same ".ok" treatment the design
   preview uses for its one, always-verified mockup state: a broken chain
   (verified: false, a REAL field on a REAL response body) must never read
   as the same green check as a healthy one.
   --------------------------------------------------------------------- */

.operator-console-audit-strip {
  display: block;
  margin-top: 1rem;
}

.audit-strip {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  flex-wrap: wrap;
  padding: 0.9rem 1.1rem;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--paper);
}

.audit-strip-status {
  font-weight: 650;
  font-size: 0.85rem;
  white-space: nowrap;
}

.audit-strip-status--loading {
  color: var(--midgray);
}

/* The ONLY state that reads as a green check, on purpose. */
.audit-strip-status--verified {
  color: #0c5c0c;
}

/* A failed/unavailable check is red text, never green — no shared class,
   no shared color variable with .audit-strip-status--verified above. */
.audit-strip-status--failed,
.audit-strip-status--unavailable {
  color: #8f1f1f;
}

.audit-strip-detail {
  font-size: 0.78rem;
  font-weight: 600;
}

.audit-strip-detail--failed,
.audit-strip-detail--unavailable {
  color: #8f1f1f;
}

.audit-strip-meta {
  font-size: 0.75rem;
  color: var(--midgray);
}

/* The whole strip's own border/background shift red on a failed or
   unreachable check — a second, independent visual signal beyond the status
   text's own color, so the failed state is unmistakable even at a glance. */
.audit-strip--failed,
.audit-strip--unavailable {
  border-color: #d03b3b;
  background: rgba(208, 59, 59, 0.06);
}

.operator-console-footer {
  max-width: 1180px;
  margin: 0 auto;
  padding: 1.1rem 1.5rem 1.75rem;
  font-size: 0.75rem;
  color: var(--midgray);
  border-top: 1px solid var(--tan);
}

/* ---------------------------------------------------------------------
   TF-104 (V1-203) — the Tier 1 owner settings panel. Reuses the SAME
   .dashboard-auth-gate rule (C-201, above) and the Tier 2 panel's own
   input/button tokens (.tier2-* just above) rather than inventing a third
   visual language for what is, structurally, one more form-shaped surface
   on this brand.
   --------------------------------------------------------------------- */

.owner-settings-main {
  width: 100%;
  max-width: 900px;
  margin: 0 auto;
  padding: 1.5rem;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

.owner-settings-intro {
  font-size: 0.85rem;
  color: var(--midgray);
  margin-bottom: 1rem;
}

/* TF-484: the business-name field. */
.owner-settings-profile {
  border: 1px solid var(--hairline, #e0dccf);
  border-radius: 0.5rem;
  padding: 1rem;
  margin-bottom: 1.5rem;
}

.owner-settings-profile-heading {
  font-size: 1rem;
  margin: 0 0 0.35rem;
}

.owner-settings-profile-intro {
  font-size: 0.85rem;
  color: var(--midgray);
  margin: 0 0 0.75rem;
}

.owner-settings-profile-form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.owner-settings-profile-form label {
  font-size: 0.85rem;
  font-weight: 600;
}

#owner-settings-profile-input {
  flex: 1 1 240px;
  min-width: 160px;
  padding: 0.4rem 0.6rem;
  font-size: 0.9rem;
}

.owner-settings-profile-status {
  color: var(--midgray);
  font-size: 0.85rem;
}

.owner-settings-profile-error {
  color: var(--error, #b3261e);
  font-size: 0.85rem;
}

.owner-settings-tiles {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

.owner-settings-tile-card {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  background: var(--paper);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.9rem 1rem;
}

.owner-settings-tile-heading {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}

.owner-settings-tile-title {
  font-weight: 700;
  color: var(--nearblack);
}

.owner-settings-tile-type {
  font-size: 0.7rem;
  color: var(--midgray);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.owner-settings-tile-controls {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}

/* TF-293 (F14): Move up/down at the respective edge of the list. */
.owner-settings-tile-controls button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.owner-settings-band-rows {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.owner-settings-band-row {
  display: flex;
  gap: 0.4rem;
  align-items: center;
}

.owner-settings-band-row input,
.owner-settings-band-row select {
  padding: 0.3rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  font: inherit;
}

.owner-settings-drift {
  font-size: 0.75rem;
  color: var(--brass-deep, #7a4e00);
}

/* TF-260: inline save feedback. Error uses the same refusal tokens the
   widget error banner (.widget-error-banner) already carries; status
   (success) reuses the answered tokens the QA disposition pill uses —
   no new color language invented for what is structurally the same
   confirm/refuse pair as the rest of this file. */
.owner-settings-save-error {
  font-size: 0.8rem;
  padding: 0.5rem 0.7rem;
  border-radius: 6px;
  background: var(--refusal-bg);
  color: var(--refusal-ink);
}

.owner-settings-save-status {
  font-size: 0.8rem;
  padding: 0.5rem 0.7rem;
  border-radius: 6px;
  background: var(--answered-bg);
  color: var(--answered-ink);
}

.owner-settings-rollback {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.3rem;
}

.owner-settings-rollback select {
  padding: 0.3rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  font: inherit;
}

.owner-settings-empty {
  color: var(--midgray);
  font-size: 0.85rem;
}

/* TF-1528: "Messages this box may send" on the owner settings page. One card per kind of message, a state word beside the
   name (never colour alone), and two controls. The danger button is the only one that cannot be undone from this page. */
.owner-settings-send-purposes {
  border: 1px solid var(--hairline, #e0dccf);
  border-radius: 0.5rem;
  padding: 1rem;
  margin-bottom: 1.5rem;
}

.owner-settings-send-purposes[hidden] {
  display: none;
}

.owner-settings-send-purposes-heading {
  font-size: 1rem;
  margin: 0 0 0.35rem;
}

.owner-settings-send-purposes-intro {
  font-size: 0.85rem;
  color: var(--midgray);
  margin: 0 0 0.75rem;
}

.send-purpose-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.75rem;
}

.send-purpose-card {
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 0.75rem 0.9rem;
  background: var(--paper);
}

.send-purpose-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.send-purpose-title {
  font-size: 0.95rem;
  margin: 0;
}

.send-purpose-state {
  font-size: 0.8rem;
  font-weight: 600;
  padding: 0.1rem 0.55rem;
  border-radius: 999px;
  border: 1px solid var(--border);
}

.send-purpose-state-on {
  background: var(--answered-bg);
  color: var(--answered-ink);
}

.send-purpose-state-off,
.send-purpose-state-stopped {
  background: var(--refusal-bg);
  color: var(--refusal-ink);
}

.send-purpose-description,
.send-purpose-help {
  margin: 0.35rem 0 0;
  font-size: 0.85rem;
  color: var(--midgray);
}

.send-purpose-result {
  margin: 0.5rem 0 0;
  font-size: 0.85rem;
}

.send-purpose-result:empty {
  display: none;
}

.send-purpose-result-done {
  color: var(--answered-ink);
}

.send-purpose-result-error {
  color: var(--refusal-ink);
  font-weight: 600;
}

.send-purpose-actions,
.send-purpose-confirm {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.6rem;
}

.send-purpose-confirm-question {
  flex: 1 1 100%;
  margin: 0;
  font-size: 0.9rem;
  font-weight: 600;
}

.send-purpose-button {
  min-height: 44px;
  padding: 0.4rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  background: var(--paper);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}

.send-purpose-button:focus-visible,
.send-purpose-result:focus-visible {
  outline: 3px solid var(--navy);
  outline-offset: 2px;
}

.send-purpose-button:disabled {
  opacity: 0.6;
  cursor: progress;
}

.send-purpose-danger {
  border-color: var(--refusal-ink);
  color: var(--refusal-ink);
}

.send-purposes-failure-message {
  color: var(--refusal-ink);
  margin: 0 0 0.5rem;
}

@media (max-width: 480px) {
  .send-purpose-actions .send-purpose-button,
  .send-purpose-confirm .send-purpose-button {
    flex: 1 1 100%;
  }
}

/* TF-1569: "Stop emailing one customer" on the owner settings page. One address box, two plain-words choices (marketing only
   is the default), and a second step before anything is recorded. The danger button is the one that cannot be undone here. */
.owner-settings-do-not-contact {
  border: 1px solid var(--hairline, #e0dccf);
  border-radius: 0.5rem;
  padding: 1rem;
  margin-bottom: 1.5rem;
}

.owner-settings-do-not-contact[hidden] {
  display: none;
}

.owner-settings-do-not-contact-heading {
  font-size: 1rem;
  margin: 0 0 0.35rem;
}

.owner-settings-do-not-contact-intro {
  font-size: 0.85rem;
  color: var(--midgray);
  margin: 0 0 0.75rem;
}

.dnc-form {
  display: grid;
  gap: 0.9rem;
}

.dnc-field,
.dnc-choices {
  margin: 0;
  padding: 0;
  border: 0;
  min-width: 0;
}

.dnc-label,
.dnc-legend {
  display: block;
  font-size: 0.9rem;
  font-weight: 600;
  margin: 0 0 0.3rem;
  padding: 0;
}

.dnc-input {
  box-sizing: border-box;
  width: 100%;
  max-width: 28rem;
  min-height: 44px;
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  background: var(--paper);
  color: var(--ink);
  font: inherit;
}

.dnc-input:disabled {
  opacity: 0.7;
}

.dnc-hint,
.dnc-choice-detail {
  margin: 0.25rem 0 0;
  font-size: 0.85rem;
  color: var(--midgray);
}

.dnc-choice {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 0.6rem;
  align-items: center;
  padding: 0.45rem 0;
}

.dnc-choice-detail {
  grid-column: 2;
}

.dnc-radio {
  width: 1.25rem;
  height: 1.25rem;
  margin: 0;
}

.dnc-choice-label {
  font-size: 0.95rem;
  min-height: 44px;
  display: flex;
  align-items: center;
}

.dnc-actions,
.dnc-confirm {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.dnc-actions[hidden],
.dnc-confirm[hidden] {
  display: none;
}

.dnc-confirm-question {
  flex: 1 1 100%;
  margin: 0;
  font-size: 0.9rem;
  font-weight: 600;
}

.dnc-button {
  min-height: 44px;
  padding: 0.4rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  background: var(--paper);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}

.dnc-button:focus-visible,
.dnc-input:focus-visible,
.dnc-radio:focus-visible,
.dnc-result:focus-visible {
  outline: 3px solid var(--navy);
  outline-offset: 2px;
}

.dnc-button:disabled {
  opacity: 0.6;
  cursor: progress;
}

.dnc-danger {
  border-color: var(--refusal-ink);
  color: var(--refusal-ink);
}

.dnc-result {
  margin: 0;
  font-size: 0.85rem;
}

.dnc-result:empty {
  display: none;
}

.dnc-result-done {
  color: var(--answered-ink);
}

.dnc-result-error {
  color: var(--refusal-ink);
  font-weight: 600;
}

.dnc-failure {
  color: var(--refusal-ink);
  margin: 0;
}

@media (max-width: 480px) {
  .dnc-actions .dnc-button,
  .dnc-confirm .dnc-button {
    flex: 1 1 100%;
  }
}

/* TF-259 (C-207b): owner retrieval of action audit records — the read-only
   history table mounted inside the owner-settings shell above. */
.owner-audit-history {
  margin-top: 1.5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
}

.owner-audit-history-heading {
  font-size: 1rem;
  margin: 0 0 0.75rem;
}

.owner-audit-history-note {
  margin: 0;
  color: var(--midgray);
  font-size: 0.9rem;
}

.owner-audit-history-filter {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}

.owner-audit-history-filter-label {
  font-size: 0.85rem;
  color: var(--midgray);
}

.owner-audit-history-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
}

.owner-audit-history-table th,
.owner-audit-history-table td {
  text-align: left;
  padding: 0.4rem 0.6rem;
  border-bottom: 1px solid var(--border);
}

.owner-audit-history-escalation {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

/* TF-485: the "What needs me?" wrapper around the approval-queue and
   auto-send-history panels below — a plain block, not a CSS grid (unlike
   .dashboard-grid's own .dashboard-qhead, which this reuses for its
   heading), since this section always holds exactly the two full-width
   panels beneath it, never a row of tiles. */
.dashboard-attention-section {
  margin-top: 1rem;
}

/* TF-1142: "Crew and approvals", the cards that replaced the header's Crew
   suggestions, Invoice previews and Approval devices links. Each card is a
   .pending-items-panel (same cream panel as "What needs me?"): the sentence
   saying where the link goes, then the link itself. */
.dashboard-review-section {
  margin-top: 1rem;
}

.dashboard-review-cards {
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1rem;
}

.dashboard-review-cards > .dashboard-review-card {
  margin-bottom: 0;
}

.dashboard-review-card-text {
  margin: 0 0 0.75rem;
  color: var(--ink);
  line-height: 1.45;
}

.dashboard-review-card-link {
  color: var(--navy);
  font-weight: 700;
}

.dashboard-review-card-link:focus-visible {
  outline: 3px solid var(--navy);
  outline-offset: 2px;
}

/* TF-102 (C-207): the "approval queue" panel + its Foreman-rail compact
   twin. Navigation-only styling — no button here ever submits a decision,
   only Retry (a re-fetch of this same read-only list) and the tap targets
   that hand the owner off to the Approval Desk's own origin. */
.pending-items-panel {
  margin-bottom: 1rem;
  padding: 1rem;
  background: var(--cream);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.pending-items-heading {
  margin: 0 0 0.6rem;
  font-size: 1rem;
  color: var(--navy);
}

/* TF-592: heading + "Updated Ns ago · Refresh" affordance, side by side —
   also reused, unmodified, by owner_settings.html's audit-history section. */
.pending-items-heading-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem;
  flex-wrap: wrap;
}

.pending-items-heading-row .pending-items-heading,
.pending-items-heading-row .owner-audit-history-heading {
  margin: 0;
}

.pending-items-refresh-status {
  display: flex;
  align-items: center;
  gap: 0.2rem;
  font-size: 0.8rem;
  color: var(--midgray);
}

.pending-items-refresh-age {
  white-space: nowrap;
}

.pending-items-refresh-button {
  padding: 0.2rem 0.4rem;
  font-size: 0.8rem;
  color: var(--navy);
  background: none;
  border: none;
  text-decoration: underline;
  cursor: pointer;
}

/* TF-592 (AC4): the rollback notice — an item that did not go through on
   the Desk, surfaced inline exactly once, styled the same as the existing
   origin-mismatch operator note above (an attention-worthy but non-fatal
   inline state, not the "unreachable/error" full escalation banner). */
.pending-items-rollback-error {
  margin-bottom: 0.5rem;
  padding: 0.5rem 0.75rem;
  background: var(--refusal-bg);
  color: var(--refusal-ink);
  border-radius: var(--radius);
  font-size: 0.85rem;
}

.pending-items-note {
  margin: 0;
  color: var(--midgray);
  font-size: 0.9rem;
}

.pending-items-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.pending-items-row {
  display: block;
}

.pending-items-row--broken .pending-items-link {
  opacity: 0.6;
  cursor: not-allowed;
}

/* TF-316: a row whose desk_url failed the real origin-boundary check —
   distinct from a row with no desk_url at all, so the misconfiguration note
   above the list has a visual anchor. */
.pending-items-row--misconfigured .pending-items-link {
  border-left: 3px solid var(--refusal-ink);
}

.pending-items-misconfigured {
  margin-bottom: 0.5rem;
  padding: 0.5rem 0.75rem;
  background: var(--refusal-bg);
  color: var(--refusal-ink);
  border-radius: var(--radius);
  font-size: 0.85rem;
}

/* AC 6 (mobile-usable): every clickable node in this feature carries this
   class, sized to a real touch target regardless of viewport — no
   hover-only reveal anywhere in cockpit/web/pending_items.js. */
.pending-items-tap-target {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 0.5rem 0.75rem;
  font: inherit;
}

.pending-items-link {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--navy);
  text-decoration: none;
}

.pending-items-link:hover,
.pending-items-link:focus-visible {
  border-color: var(--navy);
}

.pending-items-escalation {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.5rem 0.75rem;
  background: var(--refusal-bg);
  color: var(--refusal-ink);
  border-radius: var(--radius);
}

.pending-items-retry {
  border: 1px solid var(--refusal-ink);
  border-radius: var(--radius);
  background: transparent;
  color: var(--refusal-ink);
  cursor: pointer;
}

.pending-items-compact {
  margin-bottom: 0.5rem;
}

.pending-items-compact-link {
  width: 100%;
  justify-content: center;
  text-align: center;
}

/* ---------------------------------------------------------------------
   TF-171 (child 2 of 6 of TF-26) — the Operator Console's approval queue
   panel. Reuses .pending-items-list/-row/-note/-escalation/-tap-target/
   -link (above) directly for spacing, touch-target sizing, and the
   broken/misconfigured-row and escalation treatments — only the two-line
   row layout and the section/subheading chrome below are new.
   --------------------------------------------------------------------- */
.operator-console-approval-queue {
  margin-top: 1rem;
}

.approval-queue-group {
  margin-bottom: 1rem;
}

.approval-queue-subheading {
  margin: 0 0 0.4rem;
  font-size: 0.95rem;
  color: var(--navy);
}

.approval-queue-list-container {
  min-height: 0;
}

.approval-queue-row-main {
  margin: 0;
  font-size: 0.9rem;
  color: var(--navy);
}

.approval-queue-row-sub {
  margin: 0.15rem 0 0.4rem;
  font-size: 0.8rem;
  color: var(--midgray);
}

/* TF-1051: who decided. A word chip per kind (the word carries the meaning; the colour only helps
   tell human, policy and override apart), the record reference in the mono face, and a problem
   line that reads as a problem without relying on colour alone ("Needs attention:" leads it). */
.approver-attribution {
  margin: 0.15rem 0 0.4rem;
  font-size: 0.8rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.6rem;
  align-items: center;
}

.approver-marker {
  display: inline-block;
  padding: 0.05rem 0.5rem;
  border-radius: 999px;
  border: 1px solid currentColor;
  font-weight: 600;
  white-space: nowrap;
}

.approver-marker--person {
  color: #1f4f7a;
}

.approver-marker--policy {
  color: #5b3a8c;
}

.approver-marker--operator_override {
  color: #8a4a00;
}

.approver-marker--slack {
  color: #4f4f4f;
}

.approver-marker--undecided {
  color: #4f4f4f;
  border-style: dashed;
}

.approver-marker--integrity_gap {
  color: #9a2a1a;
  background: #fdf1ee;
}

.approver-record-ref {
  font-family: ui-monospace, Menlo, Consolas, monospace;
  overflow-wrap: anywhere;
}

.approver-problem {
  flex: 1 1 100%;
  margin: 0;
  color: #9a2a1a;
  font-weight: 600;
}

.approver-note {
  flex: 1 1 100%;
  margin: 0;
}

.approver-record-button {
  font: inherit;
  padding: 0.15rem 0.6rem;
  min-height: 32px;
  cursor: pointer;
}

.approver-record-detail {
  flex: 1 1 100%;
  border-left: 3px solid currentColor;
  padding-left: 0.6rem;
}

.approver-record-line {
  margin: 0.1rem 0;
  overflow-wrap: anywhere;
}

/* ---------------------------------------------------------------------
   TF-368 (USR-2) — the Owner-only "Users" page. Reuses the SAME
   .dashboard-auth-gate rule (C-201) and the owner-settings tokens above
   (.owner-settings-save-error/.owner-settings-save-status) rather than
   inventing a third confirm/refuse color language.
   --------------------------------------------------------------------- */

.owner-users-main {
  width: 100%;
  max-width: 1000px;
  margin: 0 auto;
  padding: 1.5rem;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

.owner-users-intro {
  font-size: 0.85rem;
  color: var(--midgray);
  margin-bottom: 1rem;
}

.owner-users-section-heading {
  font-family: var(--font-serif);
  font-size: 1.05rem;
  color: var(--nearblack);
  margin: 1.25rem 0 0.6rem;
}

.owner-users-invite-form {
  background: var(--paper);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.9rem 1rem;
}

.owner-users-invite-fields {
  display: grid;
  grid-template-columns: auto 1fr auto 1fr auto;
  gap: 0.5rem 0.6rem;
  align-items: center;
}

.owner-users-invite-fields input,
.owner-users-invite-fields select {
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  font: inherit;
}

.owner-users-save-error {
  margin-top: 0.6rem;
  font-size: 0.8rem;
  padding: 0.5rem 0.7rem;
  border-radius: 6px;
  background: var(--refusal-bg);
  color: var(--refusal-ink);
}

.owner-users-save-status {
  margin-top: 0.6rem;
  font-size: 0.8rem;
  padding: 0.5rem 0.7rem;
  border-radius: 6px;
  background: var(--answered-bg);
  color: var(--answered-ink);
}

.owner-users-invite-link-result {
  margin-top: 0.6rem;
  font-size: 0.82rem;
  color: var(--midgray);
}

.owner-users-invite-link-list {
  list-style: none;
  margin: 0.4rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

/* TF-505: one copy row — the button, the transient Copied/failure feedback
   next to it, and the raw copyable text underneath as REAL selectable text
   (the no-clipboard fallback made real, not just claimed). */
.owner-users-invite-link-item {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
}

.owner-users-invite-link-feedback {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--answered-ink);
  min-height: 1em;
}

.owner-users-invite-link-feedback[data-copy-state="failed"] {
  color: var(--refusal-ink);
}

.owner-users-invite-link-url {
  flex-basis: 100%;
  display: block;
  margin: 0;
  padding: 0.35rem 0.55rem;
  background: var(--cream);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--nearblack);
  word-break: break-all;
  user-select: all;
}

/* TF-505: an Invited row's honest "nothing has been sent" reminder, under
   the status word itself. */
.owner-users-invited-helper {
  margin: 0.2rem 0 0;
  font-size: 0.72rem;
  color: var(--midgray);
}

/* TF-731: the setup-link state under a user's status word, and the line that follows a request
   (the Approval Desk address to confirm it at). Same weight as the invited helper above. */
.owner-users-setup-link-state {
  margin: 0.2rem 0 0;
  font-size: 0.72rem;
  color: var(--midgray);
}

.owner-users-setup-link-state[data-setup-link-state="refused"],
.owner-users-setup-link-state[data-setup-link-state="unknown"] {
  color: var(--refusal-ink);
}

.owner-users-setup-link-result {
  margin-top: 0.6rem;
  font-size: 0.8rem;
  padding: 0.5rem 0.7rem;
  border-radius: 6px;
  background: var(--answered-bg);
  color: var(--answered-ink);
}

.owner-users-setup-link-result p {
  margin: 0 0 0.3rem;
}

.owner-users-setup-link-desk {
  font-weight: 600;
}

/* TF-1152: the seats line under the invite form. "full" and "over" carry the
   same weight as a warning note, not an error: nothing failed. */
.owner-users-seats {
  margin: 0.6rem 0 0;
  font-size: 0.85rem;
  color: var(--midgray);
}

.owner-users-seats[data-seat-state="full"],
.owner-users-seats[data-seat-state="over"] {
  color: var(--navy);
  font-weight: 600;
}

.owner-users-security-key-hint {
  margin-top: 0.6rem;
  font-size: 0.8rem;
  padding: 0.5rem 0.7rem;
  border-radius: 6px;
  background: var(--cream);
  border: 1px solid var(--border);
  color: var(--nearblack);
}

.owner-users-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 0.5rem;
  font-size: 0.85rem;
}

.owner-users-table th,
.owner-users-table td {
  text-align: left;
  padding: 0.5rem 0.6rem;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}

.owner-users-table select {
  padding: 0.3rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  font: inherit;
}

.owner-users-table button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.owner-users-last-owner-note {
  margin: 0.35rem 0 0;
  font-size: 0.72rem;
  color: var(--brass-deep, #7a4e00);
}

.owner-users-empty {
  color: var(--midgray);
  font-size: 0.85rem;
  margin-top: 0.5rem;
}

/* TF-368 AC3 — an in-page confirm dialog, never the browser's native
   confirm(). A simple fixed overlay + centered panel; owner_users.js hides
   it by default (`hidden`) and traps Tab focus between its own two buttons
   while open. */
.owner-users-confirm-dialog {
  position: fixed;
  inset: 0;
  background: rgba(10, 22, 40, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 50;
}

.owner-users-confirm-panel {
  background: var(--paper);
  border-radius: var(--radius);
  padding: 1.25rem 1.5rem;
  max-width: 420px;
  width: calc(100% - 2rem);
  box-shadow: 0 12px 32px rgba(10, 22, 40, 0.35);
}

.owner-users-confirm-panel h2 {
  margin: 0 0 0.5rem;
  font-family: var(--font-serif);
  font-size: 1.1rem;
  color: var(--nearblack);
}

.owner-users-confirm-panel p {
  margin: 0 0 1rem;
  font-size: 0.88rem;
  color: var(--midgray);
}

.owner-users-confirm-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.6rem;
}

/* TF-559 (ADR-052 Amendment A slice F) — the owner Connectors area: four
   vendor cards on the owner-settings page, and the fixed cross-origin modal
   that opens from a card's own Connect button. */
.owner-settings-connectors {
  margin-top: 2rem;
}

.owner-settings-connectors-heading {
  margin: 0 0 0.35rem;
  font-family: var(--font-serif);
  font-size: 1.1rem;
  color: var(--nearblack);
}

.owner-settings-connectors-intro {
  margin: 0 0 1rem;
  font-size: 0.88rem;
  color: var(--midgray);
}

.connectors-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 0.9rem;
}

.connectors-card {
  border: 1px solid var(--hairline, #d9d2c4);
  border-radius: var(--radius);
  padding: 1rem;
  background: var(--paper);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.connectors-card-title {
  margin: 0;
  font-size: 1rem;
}

.connectors-card-badge {
  display: inline-block;
  font-family: var(--font-serif);
  color: var(--nearblack);
}

.connectors-card-status {
  margin: 0;
  font-size: 0.85rem;
  color: var(--midgray);
}

.connectors-card-note {
  margin: 0;
  font-size: 0.85rem;
  color: var(--midgray);
}

.connectors-card-action {
  align-self: flex-start;
}

/* TF-720: no Intake origin on this box. The button must not look live. */
.connectors-card-action:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.connectors-unavailable-note {
  margin: 0.75rem 0 0;
  font-size: 0.85rem;
  color: var(--midgray);
}

/* The modal itself — same fixed-overlay + centered-panel shape as
   .owner-users-confirm-dialog above, generalized to a variable-height body
   (an iframe, or static provider copy) rather than that dialog's fixed
   two-line message. */
.connectors-modal {
  position: fixed;
  inset: 0;
  background: rgba(10, 22, 40, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 50;
}

.connectors-modal-panel {
  background: var(--paper);
  border-radius: var(--radius);
  padding: 1.25rem 1.5rem;
  max-width: 520px;
  width: calc(100% - 2rem);
  box-shadow: 0 12px 32px rgba(10, 22, 40, 0.35);
}

.connectors-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.75rem;
}

.connectors-modal-header h2 {
  margin: 0;
  font-family: var(--font-serif);
  font-size: 1.1rem;
  color: var(--nearblack);
}

.connectors-modal-close {
  background: none;
  border: none;
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  color: var(--midgray);
}

.connectors-modal-embed {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.connectors-modal-iframe {
  width: 100%;
  min-height: 420px;
  border: 1px solid var(--hairline, #d9d2c4);
  border-radius: var(--radius);
}

.connectors-modal-fallback-link,
.connectors-modal-unavailable {
  font-size: 0.85rem;
  color: var(--midgray);
}

/* TF-980: Intake's connector summary page, framed inline. TF-1059: it is
   the whole Connectors section on a box with an Intake origin, under the
   page's own heading and intro, with no Cockpit cards above it. */
.connectors-summary-host {
  margin-top: 1.25rem;
}

.connectors-summary {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  min-width: 0;
}

/* TF-1059, per TF-1058's layout budget: 720px tall holds four provider
   cards (2x2) with no scrollbar of its own at >= 768px wide; below that
   Intake stacks them in one column, so the frame is 1650px (TF-907: the JobTread
   write card's Connect button and help line made the stack taller than the old 1200px). Block-level,
   full width and borderless, so it never adds a horizontal scroll. */

.connectors-summary-iframe {
  display: block;
  box-sizing: border-box;
  width: 100%;
  max-width: 100%;
  height: 1650px;
  border: 0;
  background: var(--paper);
}

@media (min-width: 768px) {
  .connectors-summary-iframe {
    height: 720px;
  }
}

.connectors-summary-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
}

.connectors-summary-fallback-link,
.connectors-summary-live {
  margin: 0;
  font-size: 0.85rem;
  color: var(--midgray);
}

/* TF-594: support remains reachable outside every role/auth gate. */
.problem-report-footer {
  flex: 0 0 auto;
  padding: 8px 20px;
  border-top: 1px solid var(--border);
  background: var(--paper);
  color: var(--ink);
  font-size: 0.875rem;
}
.problem-report-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 16px; }
.problem-report-actions a, .problem-report-actions button {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--accent);
  text-decoration: underline;
}
.problem-report-footer p { margin: 0; color: var(--ink-soft); }
.problem-report-actions :focus-visible, .problem-report-dialog :focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}
.problem-report-dialog { width: min(620px, calc(100% - 32px)); max-height: 90dvh; overflow: auto; border: 1px solid var(--border); border-radius: var(--radius); color: var(--ink); }
.problem-report-dialog::backdrop { background: rgb(10 22 40 / 60%); }
.problem-report-dialog textarea { display: block; width: 100%; margin: 8px 0 16px; resize: vertical; }

/* TF-1468: the in-product "Report an issue" form (report_issue.js), in the TF-594 dialog's frame. */
.report-issue-dialog h2 { margin: 0 0 8px; }
.report-issue-dialog p { margin: 0 0 12px; color: var(--ink-soft); }
.report-issue-form { display: block; }
.report-issue-fieldset { margin: 0 0 12px; padding: 0; border: 0; }
.report-issue-fieldset legend { padding: 0; font-weight: 600; color: var(--ink); }
.report-issue-choice { display: flex; align-items: center; gap: 8px; min-height: 44px; color: var(--ink); }
.report-issue-field label { font-weight: 600; color: var(--ink); }
.report-issue-field textarea { margin: 4px 0 12px; font: inherit; }
.report-issue-preview { display: grid; grid-template-columns: minmax(0, 1fr); gap: 2px 0; margin: 0 0 16px; }
.report-issue-preview dt { margin-top: 8px; font-weight: 600; color: var(--ink); }
.report-issue-preview dd { margin: 0; color: var(--ink); white-space: pre-wrap; overflow-wrap: anywhere; }
.report-issue-actions { display: flex; flex-wrap: wrap; gap: 8px 12px; margin: 0 0 8px; }
.report-issue-button { min-height: 44px; padding: 0 16px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--paper); color: var(--ink); font: inherit; cursor: pointer; }
.report-issue-primary { border-color: var(--accent); background: var(--accent); color: var(--accent-ink); }
.report-issue-button:disabled { opacity: 0.6; cursor: default; }
.report-issue-status { min-height: 1.4em; margin: 0; color: var(--ink); }
.report-issue-note { color: var(--ink); }

/* TF-1346: the read-only "Your requests" list (open_requests.js), in the TF-594 dialog's frame and the report form's buttons. */
.your-requests-dialog h2 { margin: 0 0 8px; }
.your-requests-dialog p { margin: 0 0 12px; color: var(--ink-soft); }
.your-requests-list { list-style: none; margin: 0 0 12px; padding: 0; }
.your-requests-item { margin: 0 0 12px; padding: 12px 0 0; border-top: 1px solid var(--border); }
.your-requests-item:first-child { border-top: 0; padding-top: 0; }
.your-requests-dialog .your-requests-summary { margin: 0 0 4px; color: var(--ink); font-weight: 600; white-space: pre-wrap; overflow-wrap: anywhere; }
.your-requests-facts { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 2px 12px; margin: 0; }
.your-requests-facts dt { color: var(--ink-soft); }
.your-requests-facts dd { margin: 0; color: var(--ink); overflow-wrap: anywhere; }
.your-requests-dialog .report-issue-note { color: var(--ink); }
.your-requests-asof { color: var(--ink-soft); }
.your-requests-status { min-height: 1.4em; margin: 0; color: var(--ink); }

/* TF-1731: the request detail view inside the same dialog (open_requests.js). Replies are drawn as text, so line breaks come from
   pre-wrap and a long unbroken string wraps instead of widening the dialog. */
.your-requests-dialog .your-requests-detail-title { margin: 0 0 4px; font-size: 1.1rem; color: var(--ink); overflow-wrap: anywhere; }
.your-requests-dialog .your-requests-thread-title { margin: 16px 0 8px; font-size: 1rem; color: var(--ink); }
.your-requests-thread { list-style: none; margin: 0 0 12px; padding: 0; }
.your-requests-comment { margin: 0 0 8px; padding: 8px 12px; border: 1px solid var(--border); border-left-width: 4px; border-radius: var(--radius); }
.your-requests-comment-you { border-left-color: var(--accent); }
.your-requests-dialog .your-requests-comment-head { margin: 0 0 4px; color: var(--ink-soft); font-size: 0.9rem; }
.your-requests-dialog .your-requests-comment-text { margin: 0; color: var(--ink); white-space: pre-wrap; overflow-wrap: anywhere; }
.your-requests-item .report-issue-actions { margin: 8px 0 0; }
.your-requests-sticky { position: sticky; bottom: 0; padding: 8px 0; background: var(--paper); }
@media (max-width: 480px) {
  .your-requests-facts { grid-template-columns: minmax(0, 1fr); }
  .your-requests-facts dd { margin-bottom: 6px; }
}

/* ---------------------------------------------------------------------
   TF-984: the owner data-access notice screen (cockpit/web/owner_ack.js),
   shown after sign-in and before the chat or the dashboard. Same card
   family as .dashboard-auth-gate (cream, amber rule). The notice keeps the
   server's own line breaks (pre-wrap) because it is rendered as text, and
   the section scrolls on its own so a long notice and its "I accept"
   button stay reachable at phone width.
   --------------------------------------------------------------------- */
body:has(#owner-ack-section:not([hidden])) {
  background: var(--canvas);
}

.owner-ack {
  width: 100%;
  max-width: 640px;
  margin: 1.5rem auto;
  padding: 1.5rem;
  box-sizing: border-box;
  background: var(--cream);
  border-bottom: 3px solid var(--amber);
  border-radius: var(--radius);
  font-family: var(--font-body);
  color: var(--nearblack);
  overflow-y: auto;
  min-height: 0;
}

.owner-ack-heading {
  font-family: var(--font-serif);
  font-size: 1.3rem;
  margin: 0 0 0.75rem;
  color: var(--navy);
}

.owner-ack-heading:focus {
  outline: none;
}

.owner-ack-heading:focus-visible {
  outline: 3px solid var(--amber);
  outline-offset: 2px;
}

.owner-ack-intro,
.owner-ack-waiting {
  margin: 0 0 1rem;
  line-height: 1.5;
}

.owner-ack-text {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  line-height: 1.55;
  background: var(--paper);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 1rem;
  margin: 0 0 1rem;
}

.owner-ack-status {
  min-height: 1.4em;
  margin: 0 0 0.75rem;
}

.owner-ack-error {
  color: var(--refusal-ink);
  font-weight: 600;
}

.owner-ack-button {
  background: var(--navy);
  color: var(--lighttext);
  font-weight: 600;
  min-height: 44px;
  min-width: 44px;
}

.owner-ack-button:focus-visible,
.owner-ack-reopen:focus-visible {
  outline: 3px solid var(--amber);
  outline-offset: 2px;
}

.owner-ack-reopen {
  margin-top: 0.5rem;
  background: var(--navy);
  color: var(--lighttext);
}

@media (max-width: 480px) {
  .owner-ack {
    margin: 0.75rem auto;
    padding: 1rem;
  }
  .owner-ack-button {
    width: 100%;
  }
}

/* TF-944: failed-send rows in "What needs me?". The refusal palette marks
   each row as something that went wrong; the text itself says so too, so
   colour is never the only signal. */
.send-failed-list {
  list-style: none;
  margin: 0.6rem 0 0;
  padding: 0;
}

.send-failed-item {
  margin: 0 0 0.4rem;
  padding: 0.5rem 0.75rem;
  background: var(--refusal-bg);
  color: var(--refusal-ink);
  border-left: 3px solid var(--refusal-ink);
  border-radius: 4px;
}

.send-failed-unavailable {
  margin: 0.6rem 0 0;
  color: var(--ink-soft);
}

/* TF-1018: the readiness-rule editor, retirement preview and the "Add a
   readiness rule" toolbar in Settings. Text carries every state; colour is
   only a hint. Controls stack and fill the width at phone size. */
.business-rule-toolbar {
  margin: 0 0 0.75rem;
}

.business-rule-editor {
  margin: 0.75rem 0;
  padding: 0.75rem 1rem;
  background: var(--paper);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.business-rule-editor select,
.business-rule-editor label {
  display: block;
  max-width: 100%;
  margin-top: 0.6rem;
}

.business-rule-editor select {
  min-height: 2.5rem;
  width: 100%;
}

.business-rule-editor .business-rule-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

.business-rule-editor h4:focus,
.business-rule-card:focus {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.business-rule-editor .business-rule-error {
  color: var(--refusal-ink);
  background: var(--refusal-bg);
  padding: 0.4rem 0.6rem;
  border-radius: 4px;
}

.business-rule-editor .business-rule-status {
  color: var(--answered-ink);
  background: var(--answered-bg);
  padding: 0.4rem 0.6rem;
  border-radius: 4px;
}

@media (max-width: 480px) {
  .business-rule-editor .business-rule-actions button,
  .business-rule-toolbar button {
    width: 100%;
    min-height: 2.75rem;
  }
}

.business-rule-field-label {
  font-weight: 600;
  margin-right: 0.5rem;
}

.business-rule-pending {
  margin: 0.5rem 0;
  padding: 0.5rem 0.75rem;
  border-left: 3px solid var(--accent);
}

/* TF-1018 (A4.2): the Crew suggestions page and the dashboard's count line.
   Text carries every state; colour is only a hint. Controls stack and fill the
   width at phone size, and nothing relies on hover. */
.crew-suggestions-body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.crew-suggestions-main {
  width: 100%;
  max-width: 1000px;
  margin: 0 auto;
  padding: 1.5rem 1rem;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

.crew-suggestions-intro,
.crew-suggestions-weekly,
.crew-suggestions-continuity,
.crew-suggestions-checked {
  color: var(--ink-soft);
}

.crew-suggestions-live {
  min-height: 1.4rem;
  margin: 0.5rem 0;
  color: var(--answered-ink);
}

.crew-suggestions-alert,
.crew-suggestion-error {
  color: var(--refusal-ink);
  background: var(--refusal-bg);
  padding: 0.5rem 0.75rem;
  border-radius: 4px;
}

.crew-suggestion-status {
  color: var(--answered-ink);
  background: var(--answered-bg);
  padding: 0.5rem 0.75rem;
  border-radius: 4px;
}

.crew-suggestions-state {
  margin: 1rem 0;
  padding: 0.75rem 1rem;
  background: var(--paper);
  border: 1px solid var(--border);
  border-left: 4px solid var(--accent);
  border-radius: var(--radius);
}

.crew-suggestions-section-heading {
  margin: 1.5rem 0 0.5rem;
}

.crew-suggestions-list {
  display: grid;
  gap: 1rem;
}

.crew-suggestion-card {
  padding: 1rem 1.1rem;
  background: var(--paper);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.crew-suggestion-heading {
  margin: 0 0 0.4rem;
  font-family: var(--font-serif);
}

.crew-suggestion-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0 0 0.6rem;
}

.crew-suggestion-badge {
  padding: 0.1rem 0.55rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 0.85rem;
}

.crew-suggestion-section {
  margin: 0.8rem 0;
  padding-left: 0.75rem;
  border-left: 3px solid var(--border);
}

.crew-suggestion-section-heading {
  margin: 0 0 0.3rem;
  font-size: 0.95rem;
}

.crew-suggestion-field {
  margin: 0.15rem 0;
}

.crew-suggestion-field-label {
  font-weight: 600;
  margin-right: 0.5rem;
}

.crew-suggestion-text,
.crew-suggestion-limits,
.crew-suggestion-before,
.crew-suggestion-after,
.crew-suggestion-example {
  overflow-wrap: anywhere;
}

.crew-suggestion-note,
.crew-suggestion-preview,
.crew-suggestion-readonly {
  color: var(--ink-soft);
}

.crew-suggestion-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.8rem;
}

.crew-suggestion-card:focus,
.crew-suggestions-main h2:focus,
.crew-suggestions-dialog-panel h2:focus {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.crew-suggestions-disclosure {
  margin: 1.25rem 0;
}

.crew-suggestions-disclosure > summary {
  cursor: pointer;
  min-height: 2.5rem;
}

.crew-suggestions-dialog {
  position: fixed;
  inset: 0;
  background: rgba(10, 22, 40, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 50;
}

.crew-suggestions-dialog[hidden] {
  display: none;
}

.crew-suggestions-dialog-panel {
  background: var(--paper);
  border-radius: var(--radius);
  padding: 1.25rem 1.5rem;
  max-width: 560px;
  width: calc(100% - 2rem);
  max-height: 85vh;
  overflow-y: auto;
  box-shadow: 0 12px 32px rgba(10, 22, 40, 0.35);
}

.crew-suggestions-source-record dt {
  font-weight: 600;
  margin-top: 0.4rem;
}

.crew-suggestions-source-record dd {
  margin: 0;
  overflow-wrap: anywhere;
}

.crew-suggestions-brief a {
  margin-left: 0.25rem;
}

/* TF-1323: one row per cited source, the open control and the flag control side by side. The flagged
   line is words, not colour. A flagged control stays in place (aria-disabled, never disabled) so focus
   is not lost; its text and its accessible name both say "Flagged as wrong". It shows a default cursor
   because it does nothing. */
.crew-suggestion-sources {
  list-style: none;
  margin: 0;
  padding: 0;
}

.crew-suggestion-sources li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0.4rem 0;
}

.crew-suggestion-sources button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.crew-suggestion-sources button[aria-disabled="true"] {
  cursor: default;
}

.crew-suggestion-flagged {
  color: var(--ink-soft);
  overflow-wrap: anywhere;
}

@media (max-width: 480px) {
  .crew-suggestion-actions button,
  .crew-suggestion-actions select,
  .crew-suggestion-sources button,
  .crew-suggestions-state button,
  .crew-suggestions-prefs-form button {
    width: 100%;
    min-height: 2.75rem;
  }
}

/* TF-642: customer responses in "What needs me?". Every state is written out
   in words; colour and the left border only hint. Controls wrap and fill the
   width at phone size. */
.aftercare-coverage {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem;
  margin: 0.4rem 0 0.6rem;
  font-size: 0.85rem;
  color: var(--ink-soft);
}

.aftercare-coverage p {
  margin: 0;
}

.aftercare-alert {
  margin: 0 0 0.6rem;
  padding: 0.5rem 0.75rem;
  background: var(--refusal-bg);
  color: var(--refusal-ink);
  border-left: 3px solid var(--refusal-ink);
  border-radius: 4px;
}

.aftercare-summary,
.aftercare-empty,
.aftercare-group-note {
  margin: 0 0 0.6rem;
  color: var(--ink-soft);
}

.aftercare-group-heading {
  margin: 0.8rem 0 0.4rem;
  font-size: 0.95rem;
  color: var(--navy);
}

.aftercare-task-list,
.aftercare-task-states,
.aftercare-history-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.aftercare-task {
  margin: 0 0 0.6rem;
  padding: 0.6rem 0.75rem;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 4px;
}

.aftercare-task-mine {
  border-left: 3px solid var(--navy);
}

.aftercare-task-title {
  margin: 0 0 0.2rem;
  font-size: 0.95rem;
}

.aftercare-task-note {
  margin: 0 0 0.4rem;
  font-size: 0.85rem;
  color: var(--ink-soft);
}

.aftercare-task-facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.1rem 0.6rem;
  margin: 0 0 0.4rem;
  font-size: 0.85rem;
}

.aftercare-task-facts dd {
  margin: 0;
  overflow-wrap: anywhere;
}

.aftercare-task-states {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin-bottom: 0.5rem;
}

.aftercare-state {
  padding: 0.1rem 0.45rem;
  font-size: 0.8rem;
  border: 1px solid var(--border);
  border-radius: 999px;
}

.aftercare-state-overdue,
.aftercare-state-unassigned,
.aftercare-state-no_due_time,
.aftercare-state-stop_requested {
  background: var(--refusal-bg);
  color: var(--refusal-ink);
}

.aftercare-task-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.4rem;
}

.aftercare-field {
  display: inline-flex;
  flex-direction: column;
  font-size: 0.8rem;
}

.aftercare-readonly,
.aftercare-task-error,
.aftercare-history-error {
  margin: 0.4rem 0 0;
  font-size: 0.85rem;
}

.aftercare-task-error {
  color: var(--refusal-ink);
}

.aftercare-history {
  margin-top: 0.5rem;
  font-size: 0.85rem;
}

/* TF-1362: the classification note and the classifications listed in the history. */
.aftercare-classify-note {
  flex-basis: 100%;
  margin: 0;
  font-size: 0.8rem;
}

.aftercare-history-heading {
  margin: 0.6rem 0 0.2rem;
  font-weight: 600;
}

.aftercare-classification-list {
  margin: 0;
  padding-left: 1.2rem;
}

/* TF-1230 (TF-1194 R6): the assistant's suggested reading, beside the person's own choice. It is a suggestion in a
   dashed box with its own heading; a likely request not to be contacted also carries a sentence and the refusal tone,
   so no state is colour alone. */
.aftercare-suggestion {
  margin: 0 0 0.5rem;
  padding: 0.4rem 0.6rem;
  border: 1px dashed var(--border);
  border-radius: 6px;
  font-size: 0.85rem;
}

.aftercare-suggestion p {
  margin: 0.1rem 0;
  overflow-wrap: anywhere;
}

.aftercare-suggestion-heading {
  font-weight: 600;
}

.aftercare-suggestion-note {
  font-size: 0.8rem;
}

.aftercare-suggestion-stop {
  border-style: solid;
  background: var(--refusal-bg);
  color: var(--refusal-ink);
}

.aftercare-suggestion-warning {
  font-weight: 600;
}

.aftercare-suggestion-list {
  margin: 0;
  padding-left: 1.2rem;
}

@media (max-width: 600px) {
  .aftercare-task-actions > *,
  .aftercare-field input,
  .aftercare-field select {
    width: 100%;
  }
}

/* TF-1121 (TF-709b, OB-8): "Set up your crew" on the Settings page.
   Every text colour below is a pair this card's contrast test measures
   (tests/test_tf1121_crew_setup_assets.py): each meets WCAG AA 4.5:1 on its
   own background. State is never colour alone: each status chip carries its
   word ("Done", "Blocked", ...), and the current step also has
   aria-current="step", a bold label and a thick left border. */
.owner-settings-crew-setup {
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 1rem;
  margin-bottom: 1.5rem;
  background: var(--paper);
  color: var(--ink);
}

.owner-settings-crew-setup-heading {
  font-size: 1rem;
  margin: 0 0 0.35rem;
}

.crew-setup-intro,
.crew-setup-hint {
  font-size: 0.85rem;
  color: var(--ink-soft);
  background: var(--paper);
}

.crew-setup-steps {
  list-style: none;
  margin: 0.75rem 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.crew-setup-step-button {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  text-align: left;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--border);
  border-left: 4px solid var(--border);
  border-radius: 0.35rem;
  background: var(--paper);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}

.crew-setup-step-button[aria-current="step"] {
  border-left-color: var(--navy);
  font-weight: 600;
}

.crew-setup-step-button:focus-visible,
.owner-settings-crew-setup button:focus-visible,
.owner-settings-crew-setup a:focus-visible,
.owner-settings-crew-setup input:focus-visible,
.owner-settings-crew-setup select:focus-visible,
.owner-settings-crew-setup textarea:focus-visible,
.owner-settings-crew-setup [tabindex="-1"]:focus {
  outline: 3px solid var(--navy);
  outline-offset: 2px;
}

.crew-setup-status {
  font-size: 0.8rem;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  color: var(--ink);
  background: var(--bg);
  white-space: nowrap;
}

.crew-setup-status-complete {
  color: var(--answered-ink);
  background: var(--answered-bg);
}

.crew-setup-status-blocked {
  color: var(--refusal-ink);
  background: var(--refusal-bg);
}

.crew-setup-status-in_progress {
  color: var(--lighttext);
  background: var(--navy);
}

.crew-setup-reasons {
  margin: 0.35rem 0 0 1rem;
  padding-left: 1rem;
  font-size: 0.85rem;
  color: var(--refusal-ink);
  background: var(--paper);
}

.crew-setup-next {
  font-size: 0.85rem;
  color: var(--ink);
  background: var(--paper);
  margin: 0.25rem 0 0 1rem;
}

.crew-setup-detail {
  border-top: 1px solid var(--border);
  margin-top: 0.75rem;
  padding-top: 0.75rem;
}

.crew-setup-detail h3 {
  font-size: 0.95rem;
  margin: 0 0 0.35rem;
}

.crew-setup-detail h4 {
  font-size: 0.9rem;
  margin: 0.9rem 0 0.35rem;
}

.crew-setup-detail button,
.crew-setup-handoff button {
  margin: 0.35rem 0.5rem;
}

.crew-setup-detail form button,
.crew-setup-detail > button {
  margin-left: 0;
}

.crew-setup-ok {
  color: var(--answered-ink);
  background: var(--paper);
}

.crew-setup-missing {
  color: var(--refusal-ink);
  background: var(--paper);
}

.crew-setup-error {
  color: var(--refusal-ink);
  background: var(--refusal-bg);
  padding: 0.35rem 0.5rem;
  border-radius: 0.35rem;
}

.crew-setup-saved {
  color: var(--answered-ink);
  background: var(--answered-bg);
  padding: 0.35rem 0.5rem;
  border-radius: 0.35rem;
}

.crew-setup-form,
.crew-setup-question {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  margin: 0.5rem 0 0.9rem;
  max-width: 36rem;
}

.crew-setup-form label,
.crew-setup-question label,
.crew-setup-question legend {
  font-size: 0.85rem;
  font-weight: 600;
}

.crew-setup-field-label {
  display: block;
  margin-bottom: 0.25rem;
}

.crew-setup-form button,
.crew-setup-question button {
  align-self: flex-start;
}

.crew-setup-question textarea,
.crew-setup-question input[type="text"],
.crew-setup-question input[type="number"],
.crew-setup-question select {
  box-sizing: border-box;
  width: 100%;
}

.crew-setup-question fieldset {
  border: 0;
  padding: 0;
  margin: 0;
}

.crew-setup-sample {
  font-size: 0.85rem;
  color: var(--ink);
  background: var(--bg);
  border-radius: 0.35rem;
  padding: 0.5rem 0.75rem;
}

.crew-setup-sample dl {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.15rem 0.75rem;
  margin: 0.25rem 0;
}

.crew-setup-sample dd {
  margin: 0;
}

/* TF-1122: the "Final approval" step. Layout only; every colour it shows is
   one of the pairs above, so the contrast test already covers it. */
.crew-setup-approval-version code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.95em;
  letter-spacing: 0.04em;
}

.crew-setup-approval-summary ul {
  margin: 0.25rem 0 0.5rem 1rem;
  padding-left: 1rem;
}

.crew-setup-approval-state {
  margin: 0.75rem 0;
}

.crew-setup-desk-link {
  font-weight: 600;
}

/* TF-1353: step 1 (category pickers) and step 2 (one card per chosen system). Layout only; every colour is one of the
   pairs above, so the contrast test already covers it. */
.crew-setup-category fieldset,
.crew-setup-official fieldset {
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 0.6rem 0.9rem 0.75rem;
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
}

.crew-setup-category legend,
.crew-setup-official legend {
  padding: 0 0.35rem;
}

.crew-setup-pick-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0.2rem 0;
}

.crew-setup-pick-row input[type="text"] {
  flex: 1 1 14rem;
  min-width: 0;
}

.crew-setup-pick-row label {
  font-weight: 600;
}

.crew-setup-system-cards {
  list-style: none;
  margin: 0.5rem 0;
  padding: 0;
}

.crew-setup-system-card {
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 0.6rem 0.9rem;
  margin: 0.5rem 0;
}

.crew-setup-system-card p {
  margin: 0.25rem 0 0;
}

/* TF-1354: the owner's steps 3 to 5 (team, agents, numbers and settings). Layout only: every colour is one of the pairs
   above, so the contrast test already covers it. A step saved for later or needing another look carries a dashed outline as
   well as its words, so the pill never relies on colour alone. */
.crew-setup-status-deferred,
.crew-setup-status-stale {
  border: 1px dashed var(--ink);
}

.crew-setup-team,
.crew-setup-agents,
.crew-setup-deferred {
  list-style: none;
  margin: 0.5rem 0;
  padding: 0;
}

.crew-setup-team li,
.crew-setup-deferred li {
  margin: 0.3rem 0;
}

.crew-setup-agent {
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 0.6rem 0.9rem;
  margin: 0.5rem 0;
}

.crew-setup-agent-unavailable {
  border-style: dashed;
}

.crew-setup-agent p {
  margin: 0.25rem 0 0;
}

.crew-setup-agent fieldset {
  border: 0;
  padding: 0;
  margin: 0.4rem 0 0;
  min-width: 0;
}

.crew-setup-prefill {
  border-left: 3px solid var(--border);
  padding-left: 0.75rem;
  margin: 0.5rem 0;
}

.crew-setup-marks {
  margin-top: 1rem;
  border-top: 1px dashed var(--border);
  padding-top: 0.6rem;
}

.crew-setup-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.crew-setup-lms {
  margin-top: 1rem;
  font-size: 0.85rem;
  color: var(--ink-soft);
  background: var(--paper);
  border-top: 1px dashed var(--border);
  padding-top: 0.5rem;
}

@media (max-width: 600px) {
  .crew-setup-step-button {
    flex-direction: column;
    align-items: flex-start;
  }
}

/* TF-1026 (ADR-059 B2.2): shared exceptions in "What needs me?". Every state is written in words; colour and the
   left border only hint. A hold is always labelled "observed, not authoritative" and the page has no control that
   releases one. Controls wrap and fill the width at phone size. */
.shared-causes-intro,
.shared-causes-note,
.shared-causes-empty {
  margin: 0 0 0.6rem;
  color: var(--ink-soft);
  font-size: 0.85rem;
}

.shared-causes-warning {
  color: var(--refusal-ink);
}

.shared-causes-coverage {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem;
  margin: 0.4rem 0 0.6rem;
  font-size: 0.85rem;
  color: var(--ink-soft);
}

.shared-causes-coverage p {
  margin: 0;
}

.shared-causes-subheading {
  margin: 0.8rem 0 0.4rem;
  font-size: 0.95rem;
  color: var(--navy);
}

.shared-causes-summary {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 0.6rem;
  margin: 0 0 0.6rem;
}

.shared-causes-block {
  padding: 0.5rem 0.75rem;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 4px;
}

.shared-causes-facts,
.shared-causes-burden,
.shared-causes-list,
.shared-causes-states,
.shared-causes-context,
.shared-causes-freshness,
.shared-causes-hold-reasons,
.shared-causes-delegated,
.shared-causes-history-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.shared-causes-facts li,
.shared-causes-burden-item,
.shared-causes-context-item,
.shared-causes-freshness li,
.shared-causes-history-item {
  margin: 0 0 0.25rem;
  font-size: 0.85rem;
  overflow-wrap: anywhere;
}

.shared-causes-row,
.shared-causes-detail {
  margin: 0 0 0.6rem;
  padding: 0.6rem 0.75rem;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 4px;
}

.shared-causes-detail {
  border-left: 3px solid var(--navy);
}

.shared-causes-row-title,
.shared-causes-detail-heading {
  margin: 0 0 0.2rem;
  font-size: 0.95rem;
}

.shared-causes-row-subject {
  margin: 0 0 0.4rem;
  font-size: 0.85rem;
  overflow-wrap: anywhere;
}

.shared-causes-states {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin-bottom: 0.5rem;
}

.shared-causes-state {
  padding: 0.1rem 0.45rem;
  font-size: 0.8rem;
  border: 1px solid var(--border);
  border-radius: 999px;
}

.shared-causes-state-needs_check,
.shared-causes-state-unverified {
  background: var(--refusal-bg);
  color: var(--refusal-ink);
}

.shared-causes-hold {
  margin: 0 0 0.5rem;
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--border);
  border-left-width: 3px;
  border-radius: 4px;
  font-size: 0.85rem;
}

.shared-causes-hold-held {
  background: var(--refusal-bg);
  color: var(--refusal-ink);
  border-left-color: var(--refusal-ink);
}

.shared-causes-hold-none {
  border-left-color: var(--navy);
}

.shared-causes-hold p {
  margin: 0.25rem 0 0;
}

.shared-causes-hold-held p {
  color: var(--refusal-ink);
}

.shared-causes-actions,
.shared-causes-decision {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.4rem;
  margin: 0 0 0.5rem;
}

.shared-causes-field {
  display: inline-flex;
  flex-direction: column;
  font-size: 0.8rem;
}

.shared-causes-readonly,
.shared-causes-error {
  margin: 0.4rem 0 0;
  font-size: 0.85rem;
}

.shared-causes-error {
  color: var(--refusal-ink);
}

.shared-causes-ref {
  font-family: var(--font-mono);
  overflow-wrap: anywhere;
}

.shared-causes-scope {
  color: var(--ink-soft);
}

.shared-causes-history,
.shared-causes-resolved {
  margin: 0.5rem 0 0;
  font-size: 0.85rem;
}

.shared-causes-button:focus-visible,
.shared-causes-link:focus-visible,
.shared-causes-signin:focus-visible,
.shared-causes-field select:focus-visible,
.shared-causes-detail-heading:focus-visible,
.shared-causes-panel .pending-items-heading:focus-visible {
  outline: 3px solid var(--navy);
  outline-offset: 2px;
}

/* TF-1139 (TF-712e, OB-11): "What's running now" on the Settings page.
   Every text colour below is one of the pairs the "Set up your crew" section
   already measures (tests/test_tf1121_crew_setup_assets.py): ink, ink-soft,
   answered and refusal on their own backgrounds. This card's contrast test
   (tests/test_tf1139_live_setup_assets.py) measures them again for these
   selectors. State is never colour alone: the running version says "Running"
   in words, a pending change names its state, and every control has a word. */
.owner-settings-live-setup {
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 1rem;
  margin-bottom: 1.5rem;
  background: var(--paper);
  color: var(--ink);
}

.owner-settings-live-setup-heading {
  font-size: 1rem;
  margin: 0 0 0.35rem;
}

.live-setup-body h3 {
  font-size: 0.95rem;
  margin: 0 0 0.35rem;
}

.live-setup-body h4 {
  font-size: 0.9rem;
  margin: 0.9rem 0 0.35rem;
}

.live-setup-confirm h5 {
  font-size: 0.9rem;
  margin: 0 0 0.35rem;
}

.live-setup-hint {
  font-size: 0.85rem;
  color: var(--ink-soft);
  background: var(--paper);
}

.live-setup-facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.15rem 0.75rem;
  margin: 0.25rem 0 0.5rem;
  font-size: 0.9rem;
  color: var(--ink);
  background: var(--paper);
}

.live-setup-facts dd {
  margin: 0;
}

.live-setup-evidence-list,
.live-setup-sections {
  margin: 0.25rem 0 0.5rem 1rem;
  padding-left: 1rem;
  font-size: 0.9rem;
  color: var(--ink);
  background: var(--paper);
}

.live-setup-responsibility-list {
  list-style: none;
  margin: 0.25rem 0 0.5rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.live-setup-responsibility {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--border);
  border-left: 4px solid var(--border);
  border-radius: 0.35rem;
  color: var(--ink);
  background: var(--paper);
}

.live-setup-responsibility-name {
  font-weight: 600;
}

.live-setup-status {
  font-size: 0.8rem;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  color: var(--ink);
  background: var(--bg);
  white-space: nowrap;
}

.live-setup-status-active {
  color: var(--answered-ink);
  background: var(--answered-bg);
}

.live-setup-confirm {
  flex-basis: 100%;
  margin: 0.5rem 0;
  padding: 0.75rem;
  border: 1px solid var(--border);
  border-left: 4px solid var(--navy);
  border-radius: 0.35rem;
  color: var(--ink);
  background: var(--bg);
}

.live-setup-confirm .live-setup-sections,
.live-setup-confirm .live-setup-hint {
  background: var(--bg);
}

.live-setup-pending {
  border-top: 1px solid var(--border);
  margin-top: 0.75rem;
  padding-top: 0.25rem;
  color: var(--ink);
  background: var(--paper);
}

.live-setup-error {
  color: var(--refusal-ink);
  background: var(--refusal-bg);
  padding: 0.35rem 0.5rem;
  border-radius: 0.35rem;
}

.live-setup-saved {
  color: var(--answered-ink);
  background: var(--answered-bg);
  padding: 0.35rem 0.5rem;
  border-radius: 0.35rem;
}

.owner-settings-live-setup button {
  margin: 0.35rem 0.5rem 0.35rem 0;
}

.owner-settings-live-setup button:focus-visible,
.owner-settings-live-setup a:focus-visible,
.owner-settings-live-setup [tabindex="-1"]:focus {
  outline: 3px solid var(--navy);
  outline-offset: 2px;
}

@media (max-width: 600px) {
  .shared-causes-actions > *,
  .shared-causes-decision > *,
  .shared-causes-field select {
    width: 100%;
  }
  .live-setup-facts {
    grid-template-columns: 1fr;
  }
}

/* --- TF-1264: the "time and money saved" tiles (value_ledger_tiles.js) -----------------------------------------
   Drawn in their own panel, in the dashboard grid's own 12-column grid with its own tile styling, so they read as
   the same family as every other tile (TF-252). Below 900px the grid's own breakpoint already makes each tile
   full width; the rules here only keep the controls tappable and let long figures and dates wrap at phone width. */
.value-ledger-panel {
  margin-top: 1rem;
  margin-bottom: 0.9rem;
}

/* TF-1636 (D1 of TF-1620): the headings of sections C (Business performance) and D (Value delivered), and the
   History section. They are the same .dashboard-qhead row the in-grid headings use, outside the grid, so a
   heading that the page hides must stay hidden despite the flex display above. */
.dashboard-section-head {
  margin: 1rem 0 0.5rem;
}

.dashboard-section-head[hidden] {
  display: none;
}

.value-ledger-panel .dashboard-section-head {
  margin-top: 0;
}

/* The five page-level headings read as the top level: larger than the panel headings inside the sections
   (UX review of TF-1636). The in-grid group headings keep the small caption style. */
#dashboard-attention-section > .dashboard-qhead .dashboard-qhead-title,
#crew-today-section > .dashboard-qhead .dashboard-qhead-title,
#dashboard-performance-head .dashboard-qhead-title,
#value-ledger-panel .dashboard-qhead-title,
#dashboard-history-section > .dashboard-qhead .dashboard-qhead-title {
  font-size: 1.05rem;
  letter-spacing: 0.3px;
}

.dashboard-history-section {
  margin-top: 1rem;
}

/* TF-1637 (D2 of TF-1620): the "Needs your decision" count line and its ordered guide, the plain
   approval rows, the compact shared-exception panel and the second-look slot. Words carry every state;
   colour only hints. */
.attention-summary {
  margin: 0.3rem 0 0.2rem;
  font-weight: 650;
  color: var(--navy);
}

.attention-summary[hidden] {
  display: none;
}

.attention-summary[data-state="partial"],
.attention-summary[data-state="unreadable"] {
  color: var(--refusal-ink);
}

.attention-guide {
  margin: 0 0 0.6rem;
  font-size: 0.85rem;
  color: var(--ink-soft);
}

.attention-guide[hidden] {
  display: none;
}

.attention-guide > summary,
.shared-causes-more > summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--navy);
}

.attention-guide-list {
  margin: 0.4rem 0 0;
  padding-left: 1.3rem;
}

.attention-guide-list li {
  margin: 0.2rem 0;
}

.pending-items-age {
  display: block;
  margin: 0.15rem 0 0 0.75rem;
  font-size: 0.8rem;
  color: var(--ink-soft);
}

.shared-causes-headline {
  margin: 0.3rem 0 0.4rem;
  font-size: 0.9rem;
  color: var(--ink);
}

.shared-causes-headline:empty {
  display: none;
}

.shared-causes-more {
  margin-top: 0.4rem;
}

.shared-causes-more[open] > summary {
  margin-bottom: 0.5rem;
}

.second-look-slot {
  padding: 0.75rem;
}

.second-look-slot[hidden] {
  display: none;
}

.second-look-condition {
  margin: 0 0 0.6rem;
  font-size: 0.85rem;
  color: var(--ink-soft);
}

.crew-suggestions-brief a {
  text-underline-offset: 0.15em;
}

.value-ledger-body {
  margin: 0;
}

.value-ledger-period {
  font-size: 0.85rem;
  font-weight: 650;
  color: var(--midgray);
  margin: 0;
}

.value-ledger-month-nav {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.value-ledger-figures {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.75rem;
  align-items: flex-start;
}

.value-ledger-figure {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.value-ledger-number {
  overflow-wrap: anywhere;
}

.value-ledger-figure-label,
.value-ledger-count,
.value-ledger-note {
  font-size: 0.8rem;
  color: var(--midgray);
  margin: 0;
}

.value-ledger-withheld {
  font-size: 0.85rem;
  color: var(--ink-soft);
  margin: 0;
  max-width: 28rem;
}

/* The tile's buttons are quiet text controls, like .widget-drill-hint, with a target big enough for a thumb. */
.value-ledger-button {
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--navy);
  font-family: var(--font-body);
  font-size: 0.8rem;
  min-height: 2.5rem;
  padding: 0.35rem 0.75rem;
  cursor: pointer;
}

.value-ledger-button:hover,
.value-ledger-button:focus-visible {
  border-color: var(--navy);
}

.value-ledger-button:focus-visible {
  outline: 3px solid var(--amber);
  outline-offset: 2px;
}

.value-ledger-button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.value-ledger-step {
  min-width: 2.5rem;
  font-size: 1.1rem;
  line-height: 1;
}

.value-ledger-disclosure {
  align-self: flex-start;
  text-align: left;
}

/* TF-1641 (D6 of TF-1620): the month is the one prominent card; the since-go-live tile sits in an expandable history
   under it. The basis lines say what the figures rest on and stay quiet; the evidence trail is grouped by outcome. */
.value-ledger-tile--primary .value-ledger-number {
  font-size: 2rem;
}

.value-ledger-basis {
  font-size: 0.8rem;
  color: var(--ink-soft);
  max-width: 44rem;
}

.value-ledger-basis p {
  margin: 0 0 0.2rem;
}

.value-ledger-history-button {
  min-height: 2.75rem;
  border-color: var(--navy);
  font-weight: 650;
}

.value-ledger-history-button::before {
  content: "\25B8";
  display: inline-block;
  margin-right: 0.45rem;
  transition: transform 0.15s;
}

.value-ledger-history-button[aria-expanded="true"]::before {
  transform: rotate(90deg);
}

.value-ledger-tile--primary [data-vl="dollars"] {
  font-size: 1.5rem;
}

.value-ledger-basis p:last-child {
  color: var(--ink);
}

.value-ledger-history {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.75rem;
}

.value-ledger-history[hidden],
.value-ledger-history-region[hidden] {
  display: none;
}

.value-ledger-history-region {
  flex: 1 1 100%;
  margin: 0;
}

.value-ledger-evidence-group + .value-ledger-evidence-group {
  margin-top: 0.5rem;
}

.value-ledger-evidence-heading {
  font-size: 0.85rem;
  font-weight: 650;
  color: var(--navy);
  margin: 0 0 0.15rem;
}

.value-ledger-how,
.value-ledger-evidence {
  border-left: 3px solid var(--tan);
  padding: 0.1rem 0 0.1rem 0.75rem;
  font-size: 0.85rem;
  color: var(--ink);
}

.value-ledger-how p {
  margin: 0 0 0.4rem;
}

.value-ledger-evidence-title {
  font-weight: 650;
  margin: 0 0 0.35rem;
}

.value-ledger-evidence-list {
  list-style: none;
  margin: 0 0 0.5rem;
  padding: 0;
  max-height: 20rem;
  overflow-y: auto;
}

.value-ledger-evidence-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.15rem 0.75rem;
  padding: 0.35rem 0;
  border-bottom: 1px solid var(--border);
}

.value-ledger-evidence-when {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--ink-soft);
}

@media (max-width: 480px) {
  .value-ledger-figures {
    flex-direction: column;
    gap: 0.6rem;
  }

  .value-ledger-disclosure {
    align-self: stretch;
  }
}

/* TF-1173: the role card's secondary lines read as secondary. */
.crew-setup-responsibility > p {
  margin: 0.25rem 0 0;
}

.crew-setup-responsibility > p:first-child {
  margin-top: 0;
}

.crew-setup-responsibility {
  margin-bottom: 1rem;
}

.crew-setup-role-earlier,
.crew-setup-responsibility > p[data-limits-note] {
  font-size: 0.85rem;
  color: var(--ink-soft);
  background: var(--paper);
}

.aftercare-task-source {
  margin: 0 0 0.4rem;
  font-size: 0.85rem;
  color: var(--ink-soft);
}

/* TF-1409 (item 1): "What your crew did today", the owner's short view of the
   day's crew activity on the dashboard (cockpit/web/crew_today.js). The card
   is a .pending-items-panel; each row is a time, a coloured marker and one
   sentence. The marker is never the only signal: the sentence says the same
   thing in words. */
.dashboard-crew-today-section {
  margin-top: 1rem;
}

.crew-today-summary {
  margin: 0 0 0.6rem;
  font-weight: 600;
  color: var(--ink);
}

.crew-today-note {
  margin: 0.4rem 0 0;
  color: var(--midgray);
}

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

.crew-today-item {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  padding: 0.35rem 0;
  border-top: 1px solid var(--border);
}

.crew-today-item:first-child {
  border-top: 0;
}

.crew-today-item::before {
  content: "";
  flex: 0 0 auto;
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
  background: #0c5c0c;
  align-self: center;
}

.crew-today-item--waiting::before {
  background: var(--amber, #c8882a);
}

.crew-today-item--failed::before {
  background: #d03b3b;
}

.crew-today-item--declined::before {
  background: var(--midgray);
}

/* TF-1642: a routine check is a neutral hollow marker, so it never reads as a result; the sentence says the same in words. */
.crew-today-item--routine::before {
  background: transparent;
  border: 2px solid var(--midgray);
}

.crew-today-earlier {
  margin-top: 0.6rem;
}

.crew-today-earlier-summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--ink);
  min-height: 2.75rem;
  display: flex;
  align-items: center;
}

.shared-causes-history-panel .shared-causes-intro {
  margin: 0 0 0.6rem;
}

.crew-today-time {
  flex: 0 0 auto;
  min-width: 4.5rem;
  color: var(--midgray);
  font-variant-numeric: tabular-nums;
}

.crew-today-text {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* TF-1409 (item 2): "Your approval devices" on the Settings page, the same
   bordered box as the business-name section below it. */
.owner-settings-approval-devices {
  border: 1px solid var(--hairline, #e0dccf);
  border-radius: 0.5rem;
  padding: 1rem;
  margin-bottom: 1.5rem;
}

.owner-settings-approval-devices-heading {
  font-size: 1rem;
  margin: 0 0 0.4rem;
}

.owner-settings-approval-devices-text {
  margin: 0 0 0.75rem;
  line-height: 1.45;
}

.owner-settings-approval-devices-link {
  color: var(--navy);
  font-weight: 700;
}

.owner-settings-approval-devices-link:focus-visible {
  outline: 3px solid var(--navy);
  outline-offset: 2px;
}

/* TF-1409 (item 3): the Settings page never scrolls sideways, at any width.
   .owner-settings-main scrolls its own content (overflow-y: auto, which makes
   overflow-x auto too), so anything wider than the column put a horizontal
   scrollbar at the bottom of it. Two things were wider: a long unbroken value
   (an evidence reference, a job-class key, an id, a timestamp) and the
   nine-column Action audit history table, which a table lays out at its
   widest word. Long values now break anywhere, form controls and frames
   never exceed the column, and below 960px each audit row is shown as a
   short list of "Column: value" lines instead of a nine-column row. */
.owner-settings-main {
  overflow-wrap: anywhere;
}

.owner-settings-main select,
.owner-settings-main input,
.owner-settings-main textarea,
.owner-settings-main table,
.owner-settings-main iframe,
.owner-settings-main img,
.owner-settings-main pre {
  max-width: 100%;
}

.owner-settings-main pre {
  white-space: pre-wrap;
}

/* In the audit table, only the columns that hold long unbroken values (ids,
   action names, timestamps, the approver and its record reference) may break
   inside a word; headings and plain words ("Approved", "Outcome") wrap only
   between words, so the table never reads "Sta te". */
.owner-audit-history-table th {
  overflow-wrap: normal;
}

.owner-audit-history-table td {
  overflow-wrap: break-word;
}

.owner-audit-history-table td[data-history-cell="correlation_id"],
.owner-audit-history-table td[data-history-cell="action"],
.owner-audit-history-table td[data-history-cell="proposed_at"],
.owner-audit-history-table td[data-history-cell="decided_at"],
.owner-audit-history-table td[data-history-cell="executed_at"],
.owner-audit-history-table td[data-history-cell="approver"] {
  overflow-wrap: anywhere;
}

/* Below this width the nine columns cannot fit the Settings column without
   breaking words, so each row becomes a short list instead. */
@media (max-width: 960px) {
  .owner-audit-history-table,
  .owner-audit-history-table tbody,
  .owner-audit-history-table tr,
  .owner-audit-history-table td {
    display: block;
    width: 100%;
  }

  /* The header row stays for a screen reader but is taken out of view; the
     table is its containing block so it adds no height to the page. */
  .owner-audit-history-table {
    position: relative;
  }

  .owner-audit-history-table thead {
    top: 0;
    left: 0;
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .owner-audit-history-table tr {
    padding: 0.4rem 0;
    border-bottom: 1px solid var(--border);
  }

  .owner-audit-history-table td {
    border-bottom: 0;
    padding: 0.15rem 0;
  }

  .owner-audit-history-table td[data-label]::before {
    content: attr(data-label) ": ";
    font-weight: 600;
  }
}

/* TF-1512 (TF-1424): the demo box's "Reset demo" and "Check demo" controls on
   the dashboard (cockpit/web/demo_control.js). The section is hidden unless the
   person is an owner on a demo box. The result line is words first: the colour
   only backs up what the sentence already says. */
.demo-control {
  margin: 0 0 1rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--paper);
}

.demo-control-heading {
  margin: 0 0 0.5rem;
  font-size: 1rem;
}

.demo-control-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.demo-control-button {
  min-height: 44px;
  padding: 0.4rem 1rem;
  border: 1px solid var(--navy);
  border-radius: 8px;
  background: var(--navy);
  color: var(--cream);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

.demo-control-button:disabled {
  cursor: default;
  opacity: 0.6;
}

.demo-control-button:focus-visible {
  outline: 3px solid var(--amber);
  outline-offset: 2px;
}

.demo-control-status {
  margin: 0.6rem 0 0;
  color: var(--ink-soft);
  overflow-wrap: anywhere;
}

.demo-control-status[data-demo-control-tone="ok"] {
  color: var(--answered-ink);
  font-weight: 600;
}

.demo-control-status[data-demo-control-tone="bad"] {
  color: var(--refusal-ink);
  font-weight: 600;
}

/* TF-1556 (TF-1393a): "Metrics you asked for" (cockpit/web/metric_retire.js). Hidden unless the person is an owner
   with at least one tile bound to a metric they asked for. The sentence carries the meaning; colour only backs it up. */
.metric-retire {
  margin: 0 0 1rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--paper);
}

.metric-retire-heading {
  margin: 0 0 0.25rem;
  font-size: 1rem;
}

.metric-retire-sub {
  margin: 0 0 0.5rem;
  color: var(--ink-soft);
}

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

.metric-retire-item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 0.75rem;
  padding: 0.5rem 0;
  border-top: 1px solid var(--border);
}

.metric-retire-item:first-child {
  border-top: 0;
}

.metric-retire-title {
  flex: 1 1 12rem;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.metric-retire-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.metric-retire-button {
  min-height: 44px;
  padding: 0.4rem 1rem;
  border: 1px solid var(--navy);
  border-radius: 8px;
  background: var(--paper);
  color: var(--navy);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

.metric-retire-button--confirm {
  background: var(--navy);
  color: var(--cream);
}

.metric-retire-button:disabled {
  cursor: default;
  opacity: 0.6;
}

.metric-retire-button:focus-visible {
  outline: 3px solid var(--amber);
  outline-offset: 2px;
}

.metric-retire-message {
  flex: 1 1 100%;
  margin: 0;
  color: var(--ink-soft);
  overflow-wrap: anywhere;
}

.metric-retire-message:empty {
  display: none;
}

.metric-retire-message[data-metric-retire-tone="ok"] {
  color: var(--answered-ink);
  font-weight: 600;
}

.metric-retire-message[data-metric-retire-tone="bad"] {
  color: var(--refusal-ink);
  font-weight: 600;
}

/* TF-1447 (B8 of TF-1427): "First owner invite" (cockpit/web/first_owner_invite.js). Hidden unless the server calls the person an
   onboarding operator. The sentences carry the meaning; colour only backs it up. */
.first-owner-invite {
  margin: 0 0 1rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--paper);
}

.first-owner-invite-heading {
  margin: 0 0 0.25rem;
  font-size: 1rem;
}

.first-owner-invite-sub {
  margin: 0 0 0.5rem;
  color: var(--ink-soft);
}

.first-owner-invite-line {
  margin: 0 0 0.25rem;
  overflow-wrap: anywhere;
}

.first-owner-invite-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.5rem;
}

.first-owner-invite-actions:empty {
  display: none;
}

.first-owner-invite-button {
  min-height: 44px;
  padding: 0.4rem 1rem;
  border: 1px solid var(--navy);
  border-radius: 8px;
  background: var(--paper);
  color: var(--navy);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

.first-owner-invite-button--confirm {
  background: var(--navy);
  color: var(--cream);
}

.first-owner-invite-button--quiet {
  border-color: var(--border);
  font-weight: 400;
}

.first-owner-invite-button:disabled {
  cursor: default;
  opacity: 0.6;
}

.first-owner-invite-button:focus-visible {
  outline: 3px solid var(--amber);
  outline-offset: 2px;
}

.first-owner-invite-message {
  margin: 0.5rem 0 0;
  color: var(--ink-soft);
  overflow-wrap: anywhere;
}

.first-owner-invite-message:empty {
  display: none;
}

.first-owner-invite-message[data-first-owner-invite-tone="ok"] {
  color: var(--answered-ink);
  font-weight: 600;
}

.first-owner-invite-message[data-first-owner-invite-tone="bad"] {
  color: var(--refusal-ink);
  font-weight: 600;
}

.first-owner-invite-message[data-first-owner-invite-tone="warn"] {
  color: var(--ink);
  font-weight: 600;
}

/* TF-1393: the Foreman rail's "track ..." panel (cockpit/web/metric_track.js). One card for one metric, two for a question
   ("Which did you mean?"). The words carry the meaning; colour only backs it up. */
.metric-track-body {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.metric-track-heading {
  margin: 0;
  font-weight: 600;
}

.metric-track-card {
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--paper);
}

.metric-track-caption {
  margin: 0 0 0.25rem;
  color: var(--ink-soft);
  font-size: 0.875rem;
}

.metric-track-label {
  margin: 0;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.metric-track-rule,
.metric-track-meta,
.metric-track-card-refusal {
  margin: 0.25rem 0 0;
  overflow-wrap: anywhere;
}

.metric-track-meta {
  color: var(--ink-soft);
  font-size: 0.875rem;
}

.metric-track-card-refusal {
  color: var(--refusal-ink);
  font-weight: 600;
}

.metric-track-value {
  margin: 0.25rem 0 0;
}

.metric-track-value-text {
  font-size: 1.25rem;
  font-weight: 700;
}

.metric-track-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.5rem;
}

.metric-track-button {
  min-height: 44px;
  padding: 0.4rem 1rem;
  border: 1px solid var(--navy);
  border-radius: 8px;
  background: var(--paper);
  color: var(--navy);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

.metric-track-button--confirm {
  background: var(--navy);
  color: var(--cream);
}

.metric-track-button:disabled {
  cursor: default;
  opacity: 0.6;
}

.metric-track-button:focus-visible {
  outline: 3px solid var(--amber);
  outline-offset: 2px;
}

.metric-track-message {
  margin: 0;
  color: var(--ink-soft);
  overflow-wrap: anywhere;
}

.metric-track-message:empty {
  display: none;
}

.metric-track-message[data-metric-track-tone="ok"] {
  color: var(--answered-ink);
  font-weight: 600;
}

.metric-track-message[data-metric-track-tone="bad"] {
  color: var(--refusal-ink);
  font-weight: 600;
}

/* ---------------------------------------------------------------------
   TF-1356 (ADR-058 Amendment A section 4, INV-9): the owner-only "finish
   setup" banner. #onboarding-banner is a live region every owner page ships
   empty; onboarding_banner.js fills it, and :empty keeps it out of the layout
   (no height, no border) for staff, before activation is known, and after
   activation. Navy on cream, the masthead's own pair. The link is a real <a>
   so it is focusable, and it draws the same 3px focus outline as the
   controls around it.
   --------------------------------------------------------------------- */
.onboarding-banner {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
  padding: 0.6rem 1.5rem;
  background: var(--cream);
  color: var(--navy);
  border-bottom: 1px solid var(--tan);
  font-size: 0.95rem;
}

.onboarding-banner:empty {
  display: none;
}

.onboarding-banner-link {
  color: var(--navy);
  font-weight: 600;
  text-decoration: underline;
}

.onboarding-banner-link:focus-visible {
  outline: 3px solid var(--navy);
  outline-offset: 2px;
}

@media (max-width: 600px) {
  .onboarding-banner {
    padding: 0.6rem 1rem;
  }

  .onboarding-banner-link {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

/* TF-1707 (TF-1631 revision 2): Owner promotion on the Users page and the dashboard. One state line in plain ink at a readable size
   (the setup-link state above is deliberately small; this one is the thing the person is waiting on), details a step lighter,
   the confirm link sized like a button, the dashboard list on the same cards the Approvals section uses. Phone sizes are in the
   TF-1378 block below, which stays last. */
.owner-users-promotion {
  margin: 0.4rem 0 0;
  font-size: 0.85rem;
  color: var(--ink);
}

.owner-users-promotion:focus-visible {
  outline: 3px solid var(--navy);
  outline-offset: 2px;
}

.owner-users-promotion-line {
  margin: 0 0 0.25rem;
  line-height: 1.4;
}

.owner-users-promotion-detail {
  color: var(--midgray);
  font-size: 0.8rem;
}

.owner-users-promotion-link {
  display: inline-block;
  margin: 0 0.4rem 0.2rem 0;
  font-weight: 700;
  color: var(--navy);
}

.owner-users-promotion-link:focus-visible,
.owner-promotion-refresh:focus-visible {
  outline: 3px solid var(--navy);
  outline-offset: 2px;
}

.owner-users-promotion-status,
.owner-promotion-status {
  margin: 0.5rem 0;
  font-size: 0.85rem;
  color: var(--midgray);
}

.owner-promotion-requests .owner-promotion-item {
  margin-bottom: 0;
}

.owner-promotion-refresh {
  margin-top: 0.5rem;
}

/* ---------------------------------------------------------------------
   TF-1523 (ADR-055 B-8): the Owner's "Email forwarding" setup screen
   (inbound_mail_setup.js). Not mounted on any page yet; these rules wait with it.
   Every control is 44 px at any width, so the phone block below needs nothing
   from this screen. Keep this block above the TF-1378 block, which stays last.
   --------------------------------------------------------------------- */
.inbound-mail-setup {
  max-width: 44rem;
  color: var(--ink);
}

.inbound-mail-setup:focus {
  outline: 3px solid var(--navy);
  outline-offset: 2px;
}

.inbound-mail-setup h3 {
  margin: 1.5rem 0 0.4rem;
}

.inbound-mail-setup ol,
.inbound-mail-setup ul {
  margin: 0.4rem 0 0.8rem;
  padding-left: 1.4rem;
}

.inbound-mail-setup li {
  margin: 0.35rem 0;
}

.inbound-mail-setup pre {
  margin: 0.5rem 0;
  padding: 0.6rem 0.8rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  font-family: var(--font-mono);
  font-size: 0.85rem;
}

.inbound-mail-setup [data-inbound-mail-address] {
  font-family: var(--font-mono);
  font-size: 1.05rem;
  overflow-wrap: anywhere;
  user-select: all;
}

.inbound-mail-setup [data-inbound-mail-notice] {
  color: var(--refusal-ink);
  font-weight: 600;
}

.inbound-mail-setup dl {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.25rem 1rem;
  margin: 0.4rem 0;
}

.inbound-mail-setup dd {
  margin: 0;
}

.inbound-mail-button {
  min-height: 44px;
  min-width: 44px;
  margin: 0.25rem 0.5rem 0.25rem 0;
  padding: 0.4rem 1rem;
  border: 1px solid var(--accent);
  border-radius: 0.4rem;
  background: var(--paper);
  color: var(--accent);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

.inbound-mail-button-primary {
  background: var(--accent);
  color: var(--accent-ink);
}

.inbound-mail-button:focus-visible,
.inbound-mail-setup a:focus-visible {
  outline: 3px solid var(--navy);
  outline-offset: 2px;
}

.inbound-mail-setup a[data-control] {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin: 0.25rem 0;
}

/* ---------------------------------------------------------------------
   TF-1378 (ADR-062 §9 S2): touch targets and field sizes at phone width.

   Everything below sits inside one phone breakpoint, so a screen wider than
   600 px keeps exactly the sizes it had. Three rules, each from the TF-1267
   phone audit (docs/designs/tf1267-phone-access/AUDIT.md):

   1. A control a thumb has to hit is at least 44 px in both directions:
      Configure, the tile drill-down link, the dashboard's red-count and
      review links, the header's nav buttons, and every button, select and
      field on Settings and Users. A checkbox's hit area is its label, which
      grows to 44 px, since a 44 px tick box would look wrong.
   2. Type in a field or select is at least 16 px, because iOS Safari zooms
      the page in on focus below that and does not zoom back out.
   3. The Users invite form stacks, and the user table becomes one card per
      person, instead of running off the right edge (the table used to scroll
      sideways inside the page, which hid its Actions column). The Settings
      band rows wrap for the same reason.

   Not here, on purpose: the header's own width (the right-hand group that
   makes the dashboard 675 px wide) belongs to TF-1377 (S1). These rules only
   size the header's buttons; they do not wrap, hide or move the group.
   --------------------------------------------------------------------- */
@media (max-width: 600px) {
  /* 1. Header nav buttons, on every page that has the header. */
  .app-header .header-nav-link,
  .app-header .link-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    min-width: 44px;
  }

  /* 1. Dashboard tiles. The title's reserved space grows with the button. */
  .widget-configure-button {
    min-height: 44px;
    min-width: 44px;
    top: 0.25rem;
    right: 0.4rem;
    padding: 0.25rem 0.9rem;
    font-size: 0.8rem;
  }

  .widget-tile:has(.widget-configure-button) .widget-title {
    padding-right: 6rem;
  }

  .widget-drill-hint {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 0.25rem;
    font-size: 0.8rem;
  }

  .widget-aging-red-count {
    min-height: 44px;
    padding: 0.3rem 1rem;
  }

  .dashboard-review-card-link {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  /* TF-1637: the guide and the shared-exception list open from a summary line, and the crew-suggestion
     entry is a link: each is a control a thumb has to hit. */
  .attention-guide > summary,
  .shared-causes-more > summary {
    display: flex;
    align-items: center;
    min-height: 44px;
  }

  .crew-suggestions-brief a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  /* 2. Type size in every field and select, on every page. */
  input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):not([type="range"]),
  select,
  textarea,
  #owner-settings-profile-input,
  .foreman-rail-ask-form textarea,
  .owner-users-invite-fields input,
  .owner-users-invite-fields select,
  .owner-users-table select,
  .owner-settings-band-row input,
  .owner-settings-band-row select,
  .owner-settings-rollback select {
    font-size: 16px;
  }

  /* 1. Settings and Users: every button, select and field is a full-size target. */
  .owner-settings-body button,
  .owner-users-body button,
  .owner-settings-body select,
  .owner-users-body select,
  .owner-settings-body input:not([type="checkbox"]):not([type="radio"]),
  .owner-users-body input:not([type="checkbox"]):not([type="radio"]) {
    min-height: 44px;
    min-width: 44px;
  }

  .owner-settings-body label:has(> input[type="checkbox"]),
  .owner-users-body label:has(> input[type="checkbox"]) {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 44px;
    min-width: 44px;
  }

  .owner-settings-body input[type="checkbox"],
  .owner-users-body input[type="checkbox"] {
    width: 1.4rem;
    height: 1.4rem;
    margin: 0;
  }

  /* 3. Settings: a threshold row wraps instead of running past the screen. */
  .owner-settings-band-row {
    flex-wrap: wrap;
  }

  .owner-settings-band-row input {
    flex: 1 1 8rem;
    min-width: 0;
  }

  .owner-settings-band-row select,
  .owner-settings-band-row button {
    flex: 0 1 auto;
  }

  /* 3. Users: the invite form stacks, one label above its field. */
  .owner-users-invite-fields {
    grid-template-columns: minmax(0, 1fr);
  }

  .owner-users-invite-fields input,
  .owner-users-invite-fields select,
  .owner-users-invite-fields button {
    width: 100%;
    box-sizing: border-box;
  }

  .owner-users-invite-link-list {
    flex-wrap: wrap;
  }

  /* 3. Users: one card per person. The labels come from the header row, in
     its order (owner_users.html), and the row's six cells never change. */
  .owner-users-table,
  .owner-users-table tbody,
  .owner-users-table tr,
  .owner-users-table td {
    display: block;
    width: 100%;
    box-sizing: border-box;
  }

  .owner-users-table thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  .owner-users-table tr {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--paper);
    margin-bottom: 0.75rem;
    padding: 0.25rem 0.75rem;
  }

  .owner-users-table td {
    display: grid;
    grid-template-columns: 6.5rem minmax(0, 1fr);
    gap: 0.5rem;
    align-items: center;
    border-bottom: 1px solid var(--border);
    padding: 0.5rem 0;
    overflow-wrap: anywhere;
  }

  .owner-users-table td:last-child {
    border-bottom: none;
  }

  .owner-users-table td::before {
    font-weight: 600;
    color: var(--midgray);
    font-size: 0.8rem;
  }

  .owner-users-table td:nth-child(1)::before { content: "Email"; }
  .owner-users-table td:nth-child(2)::before { content: "Role"; }
  .owner-users-table td:nth-child(3)::before { content: "Status"; }
  .owner-users-table td:nth-child(4)::before { content: "Invited by"; }
  .owner-users-table td:nth-child(5)::before { content: "Date"; }
  .owner-users-table td:nth-child(6)::before { content: "Actions"; }

  .owner-users-table td:nth-child(2) select {
    width: 100%;
  }

  /* TF-1707: the Owner promotion links and Refresh status are thumb targets too; the lines wrap instead of running off the card. */
  .owner-users-promotion-link,
  .owner-promotion-refresh,
  .owner-promotion-item .dashboard-review-card-link {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  .owner-users-promotion,
  .owner-users-promotion-line {
    overflow-wrap: anywhere;
  }
}
