/* ==================================================================
   Threshold - error-feedback family (th-fb / th-ref / th-fb-load / th-fatal)
   ------------------------------------------------------------------
   Canon source: Claude Design "Threshold error-feedback design"
   (Threshold Error Feedback.dc.html + Error Feedback Canon.dc.html).
   Consumes --th-* brand tokens only (threshold-tokens.css). Loads after
   threshold-chrome.css and before destination CSS (appdef.css etc).

   Locked-accent inventory (ADR-0035 Hard Rule 4): this file adds ZERO
   references to --th-marigold-deep / --th-xr-bind. Feedback registers
   use the status tokens (--th-risk / --th-warn / --th-ok / --th-advant)
   and neutral ink; copper is an authoring accent and is not a severity.

   Supersedes:
     - #blazor-error-ui (app.css) - the stock lightyellow bar. Replaced by
       #blazor-error-ui.th-fatal below (same id so Blazor's runtime toggle
       still targets it; color-scheme:light only is removed).
     - .cc-toast as a failure surface. Toasts remain for success/info and
       for the off-screen pointer variant (.cc-toast__action) only.
     - .apd-prefix-foot.is-invalid role=alert error line
       (ConfirmationPrefixRow) - migrate to .th-fb.is-compact.is-rejected.
   Extends:
     - .th-grid-state.is-error (th-grid.css) - the section-load state now
       carries .th-fb-load's action row + reference footer.
   ================================================================== */

/* ------------------------------------------------------------------
   Register tokens. Each variant sets three locals; everything else
   reads them. --fb-ink is the on-soft text color (AA on --fb-soft).
   ------------------------------------------------------------------ */
.th-fb,
.th-fb-load {
  --fb-edge: var(--th-risk);
  --fb-soft: var(--th-risk-soft);
  --fb-ink:  var(--th-risk-ink);
}
.th-fb.is-rejected,  .th-fb.is-failed,  .th-fb-load           { --fb-edge: var(--th-risk); --fb-soft: var(--th-risk-soft); --fb-ink: var(--th-risk-ink); }
.th-fb.is-unknown,   .th-fb.is-conflict, .th-fb.is-partial     { --fb-edge: var(--th-warn); --fb-soft: var(--th-warn-soft); --fb-ink: var(--th-warn-ink); }
.th-fb.is-denied                                              { --fb-edge: var(--th-ink-3); --fb-soft: var(--th-paper-2);  --fb-ink: var(--th-ink-2); }
.th-fb.is-blocked                                             { --fb-edge: var(--th-warn); --fb-soft: var(--th-warn-soft); --fb-ink: var(--th-warn-ink); }
.th-fb.is-retrying                                            { --fb-edge: var(--th-advant); --fb-soft: var(--th-advant-soft); --fb-ink: var(--th-advant-deep); }
.th-fb.is-resolved                                            { --fb-edge: var(--th-ok); --fb-soft: var(--th-ok-soft); --fb-ink: var(--th-ok-ink); }

/* ------------------------------------------------------------------
   .th-fb - the failed-action notice. Attaches to the operation owner
   (row, field, drawer foot, modal body). Persistent until cleared.
   ------------------------------------------------------------------ */
.th-fb {
  position: relative;
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr);
  grid-template-areas:
    "glyph label"
    "glyph body"
    "glyph ledger"
    "glyph actions"
    "glyph ref";
  column-gap: 10px;
  row-gap: 4px;
  align-items: start;
  padding: 10px 14px 10px 12px;
  border: 1px solid var(--th-line-2);
  border-left: 3px solid var(--fb-edge);
  border-radius: var(--th-radius-3);
  background: var(--th-paper-sunk);
  color: var(--th-ink);
  font-family: var(--th-f-sans);
  font-size: var(--th-size-13);
  line-height: 1.45;
  min-width: 0;
}
.th-fb .glyph {
  grid-area: glyph;
  width: 20px; height: 20px;
  border-radius: 50%;
  display: inline-grid; place-items: center;
  background: var(--fb-soft);
  color: var(--fb-ink);
  border: 1px solid color-mix(in oklch, var(--fb-edge) 35%, transparent);
  margin-top: 1px;
}
.th-fb .glyph svg { width: 11px; height: 11px; }
.th-fb.is-unknown .glyph { border-style: dashed; }
.th-fb .label {
  grid-area: label;
  font-family: var(--th-f-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--fb-ink);
  line-height: 20px;
}
.th-fb .body { grid-area: body; color: var(--th-ink); min-width: 0; overflow-wrap: anywhere; }
.th-fb .body strong { font-weight: 600; }
.th-fb .body .known { color: var(--th-ink-2); }
.th-fb .body .next  { color: var(--th-ink-2); }
.th-fb .body code {
  font-family: var(--th-f-mono); font-size: 11.5px;
  background: var(--th-paper-2); border: 1px solid var(--th-line);
  padding: 0 4px; border-radius: var(--th-radius-2); color: var(--th-ink-2);
}

/* Ledger - partial-completion read-back. One line per leg, each with its
   own outcome word so "partly saved" is never inferred from color. */
.th-fb .ledger {
  grid-area: ledger;
  list-style: none; margin: 2px 0 0; padding: 0;
  display: grid; gap: 3px;
  font-family: var(--th-f-mono); font-size: 11.5px; color: var(--th-ink-2);
}
.th-fb .ledger li { display: grid; grid-template-columns: 14px 1fr auto; gap: 8px; align-items: center; }
.th-fb .ledger li svg { width: 11px; height: 11px; }
.th-fb .ledger li.is-ok   { color: var(--th-ink-2); }
.th-fb .ledger li.is-ok svg { color: var(--th-ok); }
.th-fb .ledger li.is-bad  { color: var(--th-ink); }
.th-fb .ledger li.is-bad svg { color: var(--th-risk); }
.th-fb .ledger li .st { font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--th-ink-3); }
.th-fb .ledger li.is-bad .st { color: var(--th-risk-ink); }
.th-fb .ledger li.is-unknown { color: var(--th-ink); }
.th-fb .ledger li.is-unknown svg { color: var(--th-warn); }
.th-fb .ledger li.is-unknown .st { color: var(--th-warn-ink); }

/* Actions - recovery verbs. Primary is the safe next step; ghost is
   Dismiss / Keep editing. Never more than one primary. */
.th-fb .actions { grid-area: actions; display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.th-fb-btn {
  display: inline-flex; align-items: center; gap: 6px;
  text-decoration: none;
  padding: 4px 10px;
  border-radius: var(--th-radius-3);
  font-family: var(--th-f-sans); font-size: 12px; font-weight: 500; line-height: 1.4;
  border: 1px solid var(--th-line-2);
  background: var(--th-paper-sunk); color: var(--th-ink);
  cursor: pointer; white-space: nowrap;
}
.th-fb-btn:hover { background: var(--th-paper-2); border-color: var(--th-ink-4); }
.th-fb-btn.is-primary { background: var(--th-advant-deep); border-color: var(--th-advant-deep); color: var(--th-advant-fg); }
.th-fb-btn.is-primary:hover { background: oklch(32% 0.09 230); }
[data-theme="dark"] .th-fb-btn.is-primary { background: var(--th-advant); border-color: var(--th-advant); }
[data-theme="dark"] .th-fb-btn.is-primary:hover { background: oklch(74% 0.13 245); }
.th-fb-btn.is-ghost { background: transparent; border-color: transparent; color: var(--th-ink-2); }
.th-fb-btn.is-ghost:hover { background: var(--th-paper-2); color: var(--th-ink); }
.th-fb-btn:disabled, .th-fb-btn[aria-disabled="true"] { opacity: 0.5; cursor: default; pointer-events: none; }
.th-fb-btn:focus-visible, .th-copy:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--th-advant-soft); border-color: var(--th-advant); }
.th-fb-btn svg { width: 11px; height: 11px; }

/* Reference slot inside a notice - dashed rule above, quiet. */
.th-fb .ref { grid-area: ref; margin-top: 6px; padding-top: 6px; border-top: 1px dashed var(--th-line); min-width: 0; }

/* Compact - lives under a row / inline field. Label and body share a
   line; actions inline; reference still present (screenshots stay
   lookup-able) but on one quiet line. */
.th-fb.is-compact {
  grid-template-columns: 16px minmax(0, 1fr);
  padding: 7px 10px 7px 9px;
  row-gap: 2px;
  font-size: 12px;
}
.th-fb.is-compact .glyph { width: 16px; height: 16px; margin-top: 0; }
.th-fb.is-compact .glyph svg { width: 9px; height: 9px; }
.th-fb.is-compact .label { line-height: 16px; font-size: 9.5px; }
.th-fb.is-compact .actions { margin-top: 2px; }
.th-fb.is-compact .th-fb-btn { padding: 2px 8px; font-size: 11.5px; }
.th-fb.is-compact .ref { margin-top: 4px; padding-top: 4px; }

/* Retrying - controls freeze, spinner replaces glyph. aria-busy on the
   owner region; the notice text says what is being retried. */
.th-fb.is-retrying .glyph svg { display: none; }
.th-fb.is-retrying .glyph::after {
  content: ""; width: 10px; height: 10px; border-radius: 50%;
  border: 1.5px solid color-mix(in oklch, var(--fb-edge) 30%, transparent);
  border-top-color: var(--fb-edge);
  animation: th-fb-spin 0.9s linear infinite;
}
@keyframes th-fb-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .th-fb.is-retrying .glyph::after { animation: none; border-top-color: var(--fb-edge); } }

/* Resolved - transient (3 s) then removed. */
.th-fb.is-resolved { border-color: color-mix(in oklch, var(--th-ok) 30%, var(--th-line-2)); }

/* Owner marker - the element the notice belongs to gets a 2px edge on
   the same tone so the association survives distance/wrapping. */
.th-fb-owner { box-shadow: inset 2px 0 0 var(--fb-edge, var(--th-risk)); }
.th-fb-owner.is-unknown { --fb-edge: var(--th-warn); }
.th-fb-owner.is-retrying { --fb-edge: var(--th-advant); }

/* Unconfirmed chip - for values whose write outcome is unknown. Sits in
   the row-state cluster next to the authoritative value. */
.th-fb-chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 1px 8px; border-radius: var(--th-radius-pill);
  font-family: var(--th-f-mono); font-size: 10px; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase; white-space: nowrap;
  border: 1px dashed color-mix(in oklch, var(--th-warn) 55%, transparent);
  background: var(--th-warn-soft); color: var(--th-warn-ink);
}
.th-fb-chip.is-risk { border-style: solid; border-color: color-mix(in oklch, var(--th-risk) 28%, transparent); background: var(--th-risk-soft); color: var(--th-risk-ink); }
.th-fb-chip svg { width: 9px; height: 9px; }

/* ------------------------------------------------------------------
   .th-ref - Reference + Copy diagnostic details. Subordinate: mono,
   ink-3, wraps at any character so the full lookup value is visible.
   ------------------------------------------------------------------ */
.th-ref {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "k copy" "v copy" "manual manual";
  column-gap: 10px; row-gap: 1px;
  align-items: start;
  font-family: var(--th-f-mono);
  font-size: 11px;
  /* FbRef renders on canon's normal line boxes, not the host's Bootstrap reboot. */
  line-height: normal;
  color: var(--th-ink-3);
  min-width: 0;
}
.th-ref .k {
  grid-area: k;
  font-size: 9.5px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--th-ink-4);
}
.th-ref .v {
  grid-area: v;
  color: var(--th-ink-2);
  overflow-wrap: anywhere;
  user-select: all;
  font-variant-numeric: tabular-nums;
  line-height: 1.45;
}
.th-ref .v .kind { color: var(--th-ink-4); user-select: none; }
/* The Reference IS the 32-hex trace id (App Insights operation_Id). Any span /
   request id is a separate, quieter line - never concatenated into the reference. */
.th-ref .v .span { display: block; color: var(--th-ink-3); font-size: 10.5px; margin-top: 1px; }
/* Delivery status is an engineering result, not an inference from having an id. */
.th-ref .k .status { text-transform: none; letter-spacing: 0.02em; font-size: 10px; color: var(--th-ink-4); }
.th-ref .k .status.is-delivered { color: var(--th-ok-ink); }
.th-ref .k .status.is-queued { color: var(--th-warn-ink); }
.th-ref .v.is-unavailable {
  font-family: var(--th-f-serif); font-style: italic; font-size: 12px;
  color: var(--th-ink-3); user-select: text;
}
.th-ref .copy { grid-area: copy; align-self: end; }
.th-copy {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 8px;
  border-radius: var(--th-radius-3);
  border: 1px solid var(--th-line-2);
  background: var(--th-paper-sunk); color: var(--th-ink-2);
  font-family: var(--th-f-sans); font-size: 11.5px; font-weight: 500; line-height: 1.4;
  cursor: pointer; white-space: nowrap;
}
.th-copy:hover { background: var(--th-paper-2); border-color: var(--th-ink-4); color: var(--th-ink); }
.th-copy svg { width: 11px; height: 11px; }
.th-copy[hidden], .th-ref .manual[hidden] { display: none; }
.th-copy.is-copied { color: var(--th-ok-ink); border-color: color-mix(in oklch, var(--th-ok) 45%, transparent); background: var(--th-ok-soft); }
.th-copy.is-failed { color: var(--th-risk-ink); border-color: color-mix(in oklch, var(--th-risk) 40%, transparent); }
/* Inside a compact notice the value owns the full width; the copy
   button drops beneath it so a 55-char trace never breaks per character. */
.th-fb.is-compact .th-ref { grid-template-columns: minmax(0, 1fr); grid-template-areas: "k" "v" "copy" "manual"; }
.th-fb.is-compact .th-ref .copy { justify-self: start; margin-top: 4px; }
/* Manual fallback - clipboard refused. The payload is exposed read-only
   and pre-selected so Ctrl/Cmd+C works. */
.th-ref .manual { grid-area: manual; margin-top: 6px; display: grid; gap: 4px; }
.th-ref .manual textarea {
  width: 100%; min-height: 96px; resize: vertical;
  font-family: var(--th-f-mono); font-size: 11px; line-height: 1.45;
  color: var(--th-ink); background: var(--th-paper);
  border: 1px solid var(--th-line-2); border-radius: var(--th-radius-3);
  padding: 6px 8px;
}
.th-ref .manual textarea:focus { outline: none; border-color: var(--th-advant); box-shadow: 0 0 0 3px var(--th-advant-soft); }
.th-ref .manual .hint { font-family: var(--th-f-sans); font-size: 11.5px; color: var(--th-ink-3); }

/* ------------------------------------------------------------------
   .th-fb-load - failed section load. Sibling of .th-grid-state.is-error
   (same glyph/title/body metrics) plus an action row and reference.
   Distinguishes "unavailable" from "empty" in words, never by emptiness.
   ------------------------------------------------------------------ */
.th-fb-load {
  display: flex; flex-direction: column; align-items: center;
  gap: 8px; text-align: center;
  padding: 40px 24px 32px;
  color: var(--th-ink-3);
  font-family: var(--th-f-sans);
  /* Preserve canon's unset line boxes; body and controls keep their explicit metrics. */
  line-height: normal;
}
.th-fb-load .glyph {
  width: 40px; height: 40px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--fb-soft);
  border: 1px solid color-mix(in oklab, var(--fb-edge) 30%, transparent);
  color: var(--fb-edge);
  margin-bottom: 2px;
}
.th-fb-load .glyph svg { width: 16px; height: 16px; }
.th-fb-load .label {
  font-family: var(--th-f-mono); font-size: 10px; letter-spacing: 0.12em;
  text-transform: uppercase; font-weight: 600; color: var(--fb-ink);
}
.th-fb-load .title { font-size: var(--th-size-15); font-weight: 600; color: var(--th-ink); }
.th-fb-load .body  { font-size: var(--th-size-13); max-width: 420px; color: var(--th-ink-3); line-height: 1.45; }
.th-fb-load .body strong { color: var(--th-ink-2); font-weight: 600; }
.th-fb-load .actions { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; margin-top: 4px; }
.th-fb-load .ref { margin-top: 10px; width: 100%; max-width: 460px; text-align: left; }
.th-fb-load.is-compact { padding: 22px 16px 18px; gap: 6px; }
.th-fb-load.is-compact .glyph { width: 28px; height: 28px; }
.th-fb-load.is-compact .glyph svg { width: 12px; height: 12px; }
.th-fb-load.is-compact .title { font-size: var(--th-size-13); }
.th-fb-load.is-compact .body { font-size: 12px; }
.th-fb-load.is-compact .ref { margin-top: 6px; }
.th-fb-load.is-retrying .glyph svg { display: none; }
.th-fb-load.is-retrying .glyph::after {
  content: ""; width: 14px; height: 14px; border-radius: 50%;
  border: 1.5px solid color-mix(in oklch, var(--th-advant) 30%, transparent);
  border-top-color: var(--th-advant);
  animation: th-fb-spin 0.9s linear infinite;
}
.th-fb-load.is-retrying { --fb-edge: var(--th-advant); --fb-soft: var(--th-advant-soft); --fb-ink: var(--th-advant-deep); }

/* ------------------------------------------------------------------
   Toast pointer variant - only when the owning notice is off-screen.
   Extends .cc-toast (threshold-chrome.css). Carries NO reference and
   NO retry; its single action scrolls to / focuses the owner.
   ------------------------------------------------------------------ */
.cc-toast__action {
  flex: 0 0 auto;
  background: transparent; border: 1px solid var(--th-line-2); border-radius: var(--th-radius-3);
  padding: 2px 8px; margin: -1px 0 0 4px;
  font-family: var(--th-f-sans); font-size: 12px; font-weight: 500; color: var(--th-advant-deep);
  cursor: pointer; white-space: nowrap;
}
.cc-toast__action:hover { background: var(--th-paper-2); border-color: var(--th-ink-4); }
.cc-toast__eyebrow {
  display: block;
  font-family: var(--th-f-mono); font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; font-weight: 600;
  color: var(--th-risk-ink); margin-bottom: 1px;
}

/* ------------------------------------------------------------------
   #blazor-error-ui.th-fatal - application-level fallback.
   Full-viewport takeover on the cover substrate. Plain HTML/CSS/JS:
   lives in index.html outside #app, styled by tokens already applied
   pre-paint (data-theme on <html>), populated by threshold-fatal.js.
   Blazor's runtime sets style="display:block" on #blazor-error-ui;
   the .th-fatal rules turn that into the takeover.
   ------------------------------------------------------------------ */
#blazor-error-ui.th-fatal {
  color-scheme: normal;
  background: var(--th-paper);
  box-shadow: none;
  padding: 0;
  position: fixed; inset: 0; width: auto;
  z-index: 3000;
  display: none;
  color: var(--th-ink);
  font-family: var(--th-f-sans);
  overflow: auto;
}
/* Blazor's runtime sets style="display:block"; threshold-fatal.js clears that
   inline value and adds .is-shown, so the grid below is what renders. The
   attribute selector is only a no-JS safety net. */
#blazor-error-ui.th-fatal.is-shown,
#blazor-error-ui.th-fatal[style*="display: block"] { display: grid !important; grid-template-rows: 1fr auto; }
/* Background isolation while the takeover is up: everything else is inert. */
body.th-fatal-open > :not(#blazor-error-ui) { pointer-events: none; user-select: none; }
.th-fatal .th-cover-watermark { color: var(--th-paper-2); }
/* overflow: clip, not the handoff's hidden (owner-approved 2026-09-28): hidden made the stage a
   scroll container, whose grid minimum is zero, so on a short viewport the 1fr row shrank and
   clipped the card instead of letting the takeover scroll. clip still trims the watermark. */
.th-fatal-stage {
  position: relative; display: grid; place-items: center; padding: 48px 24px; overflow: clip;
}
.th-fatal-card {
  position: relative; z-index: 1;
  width: min(560px, 100%);
  display: grid; gap: 14px;
}
.th-fatal-hero { display: flex; align-items: center; gap: 12px; margin-bottom: 6px; }
.th-fatal-hero .th-hero-wordmark { font-size: 22px; }
.th-fatal-hero .th-hero-product { font-size: 18px; }
.th-fatal-hero .th-hero-rule { width: 1px; height: 18px; }
.th-fatal-label {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--th-f-mono); font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; font-weight: 600;
  color: var(--th-risk-ink);
}
.th-fatal-label .glyph {
  width: 20px; height: 20px; border-radius: 50%; display: inline-grid; place-items: center;
  background: var(--th-risk-soft); color: var(--th-risk-ink);
  border: 1px solid color-mix(in oklch, var(--th-risk) 35%, transparent);
}
.th-fatal-label .glyph svg { width: 11px; height: 11px; }
.th-fatal-title {
  margin: 0;
  font-family: var(--th-f-serif); font-weight: 500; font-size: var(--th-size-24);
  letter-spacing: -0.012em; line-height: 1.15; color: var(--th-ink);
}
.th-fatal-body { margin: 0; font-size: var(--th-size-14); line-height: 1.5; color: var(--th-ink-2); max-width: 52ch; }
.th-fatal-body strong { color: var(--th-ink); font-weight: 600; }
.th-fatal-panel {
  background: var(--th-paper-sunk); border: 1px solid var(--th-line); border-radius: var(--th-radius-5);
  padding: 12px 14px;
}
.th-fatal-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.th-fatal-actions .th-fb-btn { padding: 6px 14px; font-size: var(--th-size-13); }
.th-fatal-note { font-size: 12px; color: var(--th-ink-3); line-height: 1.45; }
.th-fatal-note strong { color: var(--th-ink-2); font-weight: 600; }
.th-fatal-foot { position: relative; z-index: 1; }
/* Fallback when tokens failed to load too (CSS 404) - system colors,
   still legible, still copyable. */
#blazor-error-ui.th-fatal { background: var(--th-paper, #fbfbfc); color: var(--th-ink, #1d2129); }
/* Threshold's host loads Bootstrap, whose reboot gives body a 1.5 line-height; the canon renders
   the takeover on a host without it, so every line box the takeover leaves unset (the Reference
   label, the manual-copy hint) inherits canon's normal instead. */
#blazor-error-ui.th-fatal { line-height: normal; }

/* ------------------------------------------------------------------
   Narrow widths (<= 720px design floor). Notices stack actions; the
   reference copy button drops beneath the value; the fatal card fills.
   ------------------------------------------------------------------ */
@media (max-width: 720px) {
  .th-ref { grid-template-columns: minmax(0, 1fr); grid-template-areas: "k" "v" "copy" "manual"; }
  .th-ref .copy { justify-self: start; margin-top: 4px; }
  .th-fb .actions .th-fb-btn { flex: 1 1 auto; justify-content: center; }
  .th-fatal-stage { padding: 32px 18px; align-items: start; }
  .th-fatal-actions .th-fb-btn { flex: 1 1 auto; justify-content: center; }
}

/* Same rules as a container query, for hosts that size the surface by
   container rather than viewport (specimen artboards, embedded panels). */
@container th-surface (max-width: 720px) {
  .th-ref { grid-template-columns: minmax(0, 1fr); grid-template-areas: "k" "v" "copy" "manual"; }
  .th-ref .copy { justify-self: start; margin-top: 4px; }
  .th-fb .actions .th-fb-btn { flex: 1 1 auto; justify-content: center; }
  .th-fatal-stage { padding: 32px 18px; align-items: start; }
  .th-fatal-actions .th-fb-btn { flex: 1 1 auto; justify-content: center; }
}
