/* ============================================================================
   ORA SUBJECT PANEL · styles for the ONE detail panel every page opens.
   ----------------------------------------------------------------------------
   Token-driven only. Not one hex value, not one brand colour: colour and type
   come from tokens.css, which is what makes this panel repaint correctly under
   a client's brand lock. The prototype the operator drew is the SPECIFICATION
   for block order, hierarchy, disclosure, geometry and tone; its hex values are
   not, and none of them appear here.

   RULE 5 · GEOMETRY AND SCROLL, and why each line of it is here:

     * FIXED 550px wide, FULL VIEWPORT HEIGHT. Not a clamp, not a vw fraction -
       the operator moves between seven pages and the panel must be the same
       object each time, at the same size, or the re-orientation this component
       exists to end simply moves from "which panel" to "which shape".
     * FULL HEIGHT EVEN WHEN EVERY SECTION IS COLLAPSED. The panel is a flex
       column; .sp-body is flex:1 and ENDS WITH A FLEXIBLE FILLER that absorbs
       the remaining space; the footer is margin-top:auto. Without the filler a
       fully-collapsed panel shrink-wraps its ten triggers and the footer floats
       up the middle of the screen.
     * EXACTLY TWO KINDS OF SCROLL CONTAINER EXIST: .sp-body, and .sp-scroll
       (an open section whose content exceeds 450px). Nothing else scrolls -
       there is a guard in scripts/check-subject-panel.mjs that fails CI if a
       third overflow rule appears in this file.
     * NEVER THE PAGE, NEVER HORIZONTALLY. overflow-x:hidden on the panel plus
       min-width:0 on every flex child, and long values truncate with ellipsis
       or wrap. A horizontal scrollbar at any width is a defect.
     * NO THIRD SCROLL. A record expanded inside a scrolling section does NOT
       get its own scrollbar; the section scrolls. .sp-rec carries no overflow.
     * A section that is scrolling SHOWS it: .sp-scroll gets a fade at the cut
       edge, and its trigger summary carries the count (that half is in
       lib/subject-panel-model.js, which is the only place a summary is made).

   ICONS (rule 7): one set, CSS-drawn. There is not a single text glyph in this
   file or in subject-panel.js - the previous build drew its chevrons with
   characters and they rendered as stray upright lines in some fonts.
   ========================================================================== */

/* ── SHELL ───────────────────────────────────────────────────────────────── */

.sp-backdrop{
  position:fixed;inset:0;background:var(--surface-modal-overlay,rgba(0,0,0,.5));
  opacity:0;pointer-events:none;transition:opacity var(--duration-medium,.18s) var(--ease);
  z-index:var(--z-modal,1000)
}
.sp-backdrop.open{opacity:1;pointer-events:auto;backdrop-filter:blur(1.5px)}

.sp-panel{
  /* FIXED width. The one max-width is the small-screen escape hatch: below
     550px there is no 550px to give, and a panel wider than the viewport would
     scroll the page horizontally, which rule 5 forbids outright. */
  --sp-w:550px;
  --sp-section-max:450px;
  /* THE PROTOTYPE'S RHYTHM, as local steps. The repo's scale is 8/12/16/24
     (--space-xs/sm/md/lg); the prototype leans on 18px and 14px, which fall
     between those stops. Rather than round them to the nearest token and call
     it parity, the two in-between steps are named here, once, and derived from
     the scale they sit between so a future scale change still moves them. */
  --sp-lg-18:calc(var(--space-md) + 2px);
  --sp-md-14:calc(var(--space-sm) + 2px);
  position:fixed;top:0;right:0;bottom:0;
  width:var(--sp-w);max-width:100vw;
  background:var(--surface);border-left:1px solid var(--border);
  box-shadow:var(--shadow-lg);
  transform:translateX(100%);
  transition:transform var(--duration-slow,.26s) var(--ease),right var(--motion-slow,.24s) ease;
  z-index:calc(var(--z-modal,1000) + 1);
  /* FULL HEIGHT, ALWAYS: a flex column that fills top-to-bottom whatever its
     content does. height comes from top:0;bottom:0, never from the content. */
  display:flex;flex-direction:column;
  overflow-x:hidden;      /* rule 5: never horizontally, at any width */
  font-family:var(--font-body)
}
.sp-panel.open{transform:translateX(0)}

/* ── RULE 6 · JARVIS COEXISTENCE ─────────────────────────────────────────
   When the assistant opens, the panel TRANSLATES to make room. It does not
   unmount and it is not re-rendered, so scroll position and which sections are
   open survive by construction - there is nothing to restore because nothing
   was ever torn down. The dock stamps body.orbi-open; this is the panel's half
   of the same push layout the shell already uses, so both live in one flex row
   and nothing is ever covered.
   Below 1200px there is no room to push and the dock keeps its overlay, which
   is why this is inside the media query rather than unconditional. */
@media (min-width:1200px){
  body.orbi-open .sp-panel{
    right:var(--orbi-panel-w,min(420px,36vw));
    border-right:1px solid var(--border)
  }
}

/* ── HEAD REGION · identity, address, summary strip ──────────────────────── */
/* These three are the fixed head blocks. They are never collapsed and carry no
   disclosure trigger, which is why they are markup rather than accordions. */

/* THE HEAD'S RHYTHM IS THE PROTOTYPE'S, NOT AN APPROXIMATION (parity pass
   2026-08-30): 24px sides and top, 18px bottom. --space-lg is 1.5rem = 24px, so
   the sides and top are already exact; the bottom was --space-sm (12px) and is
   now the 18px the prototype sets, which is what gives the map band room to sit
   under the identity rather than crowding it. */
.sp-head{
  flex:0 0 auto;padding:var(--space-lg) var(--space-lg) var(--sp-lg-18);
  border-bottom:1px solid var(--border);background:var(--surface)
}
.sp-head-row{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--space-md)}
.sp-head-id{min-width:0;flex:1 1 auto}
/* RULE 4: the TOP of the panel has NO action. It is identity only.
   .sp-head-actions carries panel chrome (close) and nothing that acts on the
   subject - "Resolve <person>" is meaningless, because a person is not
   resolvable. The contextual verb lives in the footer, once. */
.sp-head-actions{display:flex;gap:var(--space-xs);flex:0 0 auto}

.sp-eyebrow{
  font-family:var(--font-mono);font-size:var(--text-2xs,.65rem);letter-spacing:var(--ls-wider,.08em);
  text-transform:uppercase;color:var(--text-tertiary);margin-bottom:2px
}
/* 24px, and the tight tracking that goes with it. The shipped size was
   --fs-lg (18px) at --ls-tight (-.01em), which read as a section heading rather
   than as the subject's name. --fs-xl is 1.75rem/28px and --fs-lg 1.125rem/18px,
   so neither is 24px: the prototype's size is set here against the panel's own
   scale, with the -.03em tracking it needs to stop 24px reading loose. */
.sp-panel h1{
  font-family:var(--font-heading);font-size:1.5rem;line-height:1.15;
  font-weight:var(--fw-semibold,600);
  letter-spacing:-.03em;margin:2px 0 0;
  /* rule 5: content truncates, it never widens the panel */
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap
}
.sp-meta{
  font-size:var(--text-xs,.75rem);color:var(--text-secondary);margin-top:3px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap
}
.sp-badge{
  display:inline-flex;align-items:center;gap:5px;margin-top:6px;
  font-size:var(--text-2xs,.65rem);font-weight:var(--fw-semibold,600);
  letter-spacing:var(--ls-wide,.04em);text-transform:uppercase;
  padding:3px 9px;border-radius:var(--radius-pill,999px);
  /* A badge is a tint and a word; it carries NO stroke (design system, and
     components/badge.css is the one badge source outside this panel). The
     tint comes from the tone rules below; a tone this file does not name
     falls back to the soft surface. */
  border:0;background:var(--surface-soft)
}

/* CSS-drawn icon button. No glyph: the cross is two rotated rules. */
.sp-icon-btn{
  width:28px;height:28px;flex:0 0 auto;border:1px solid var(--border);
  border-radius:var(--radius-md,8px);background:transparent;color:var(--text-secondary);
  cursor:pointer;position:relative;padding:0
}
.sp-icon-btn:hover{background:var(--surface-raised);color:var(--text)}
.sp-icon-btn:focus-visible{outline:var(--focus-ring);outline-offset:var(--focus-ring-offset,2px)}
.sp-icon-btn.sp-close::before,.sp-icon-btn.sp-close::after{
  content:"";position:absolute;left:50%;top:50%;width:11px;height:1.5px;
  background:currentColor;border-radius:1px
}
.sp-icon-btn.sp-close::before{transform:translate(-50%,-50%) rotate(45deg)}
.sp-icon-btn.sp-close::after{transform:translate(-50%,-50%) rotate(-45deg)}

/* address block */
/* ── THE MAP BAND, AND WHAT STANDS WHEN THERE IS NO MAP ──────────────────
   The prototype's band, ported: 152px minimum, the STRONGER border weight, a
   10px radius, and 18px of air above it.

   The frame IS the map. It is drawn ONLY when there are real tiles to put in
   it (`.has-map`). With no map the band does not appear at all — an empty
   bordered rectangle announcing "map not available" is a hole announcing
   itself, not an honest empty. What remains is the address alone, in the same
   treatment the prototype gives its address caption: a bordered mono plate.
   Over a map that plate is positioned bottom-left inside the band, exactly as
   the prototype draws it; with no map it simply sits in normal flow. */
.sp-addr{position:relative;margin-top:var(--sp-lg-18)}
.sp-addr.has-map{
  border:1px solid var(--border-strong);
  border-radius:10px;overflow:hidden;background:var(--surface-raised);
  min-height:152px
}
/* THE ADDRESS CAPTION PLATE — the prototype's treatment, used whether or not
   there is a map behind it. */
.sp-addr-line{
  border:1px solid var(--border-strong);border-radius:6px;
  padding:7px 9px;background:var(--surface-raised);
  color:var(--text);font-family:var(--font-mono);
  font-size:var(--text-2xs,.62rem);line-height:1.35;
  /* long addresses WRAP; they never scroll sideways */
  overflow-wrap:anywhere
}
/* Over a map the same plate floats bottom-left, translucent over the tiles. */
.sp-addr.has-map .sp-addr-line{
  position:absolute;right:8px;bottom:8px;left:8px;
  background:color-mix(in srgb,var(--surface) 92%,transparent)
}
/* THE MAP IS THE ORIGINAL'S TREATMENT (operator correction, 2026-08-29): a
   substantial band, not v2's thin strip. v2 is the STRUCTURAL reference and is
   deliberately plainer; where the two differ visually the original wins. */
.sp-addr-map{display:block;width:100%;height:150px;border:0;background:var(--surface-raised)}

/* summary strip · three cells, each with its own honest state */
.sp-strip{
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  border-bottom:1px solid var(--border);flex:0 0 auto;background:var(--surface)
}
.sp-strip>div{padding:11px var(--space-md);border-right:1px solid var(--border);min-width:0}
.sp-strip>div:last-child{border-right:0}
.sp-strip .k{
  display:block;font-family:var(--font-mono);font-size:var(--text-2xs,.65rem);
  letter-spacing:var(--ls-wide,.06em);text-transform:uppercase;color:var(--text-tertiary);margin-bottom:2px
}
.sp-strip .v{
  display:block;font-size:var(--text-sm,.82rem);font-weight:var(--fw-semibold,600);color:var(--text);
  letter-spacing:var(--ls-tight,-.01em);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap
}

/* ── BODY · SCROLL CONTAINER ONE OF TWO ──────────────────────────────────── */

.sp-body{
  flex:1 1 auto;min-height:0;
  overflow-y:auto;overflow-x:hidden;
  display:flex;flex-direction:column;
  scrollbar-color:var(--scrollbar-thumb) var(--scrollbar-track)
}
/* THE FILLER. This one line is what keeps the panel full height with every
   section collapsed: it absorbs whatever vertical space the sections do not,
   so the footer's margin-top:auto has something to be pushed against. */
.sp-filler{flex:1 0 0;min-height:0}

/* ── SECTIONS · DISCLOSURE DEPTH ONE OF TWO ──────────────────────────────── */

/* EVERY SECTION BOUNDARY IS DRAWN, at the prototype's --border (operator
   correction, 2026-08-30). One border-bottom per section left the FIRST
   boundary - between the summary strip and the first section - carried only by
   the strip, and left the stack reading as one continuous sheet whenever two
   adjacent sections shared a ground. A boundary above AND below every section,
   collapsed into one hairline by the negative offset, is what makes the stack
   legible as separate, openable sections. */
.sp-sec{
  border-top:1px solid var(--border);border-bottom:1px solid var(--border);
  margin-top:-1px;flex:0 0 auto
}
/* AN OPEN SECTION SITS ON A DIFFERENT GROUND, and its trigger a step above
   that again. This is most of what makes the prototype read as depth rather
   than as a stack of hairlines, and it was absent entirely. */
.sp-sec[data-open="1"]{background:color-mix(in srgb,var(--surface-raised) 38%,var(--surface))}
.sp-sec[data-open="1"] .sp-trigger{background:color-mix(in srgb,var(--surface-raised) 58%,var(--surface))}
/* THE PROTOTYPE'S TRIGGER ROW: 48px tall, 18px gutter. It was --space-sm top
   and bottom against a --space-lg gutter, which made a short row in a wide
   channel - the dead gap the parity pass was called for. */
.sp-trigger{
  width:100%;min-height:48px;display:flex;align-items:center;gap:10px;
  padding:0 var(--sp-lg-18);
  background:transparent;border:0;cursor:pointer;color:var(--text);
  font:inherit;text-align:left;min-width:0
}
/* THE WHOLE ROW IS THE TARGET (operator correction, 2026-08-30). The hover
   ground is the prototype's --surface-soft, on the trigger - which fills the
   row edge to edge - so the thing that lights up is the thing that is
   clickable. --surface-hover was near-identical to the open-section ground and
   read as no feedback at all. */
.sp-trigger:hover{background:var(--surface-soft)}
.sp-sec>h2,.sp-trigger>*{cursor:pointer}
.sp-trigger:focus-visible{outline:var(--focus-ring);outline-offset:-2px}
/* 13px/650 title against a 10px meta, as the prototype sets them. The meta was
   --text-xs at --text-secondary, which competed with the title instead of
   sitting under it. */
.sp-title{font-size:.8125rem;font-weight:var(--fw-semibold,600);flex:0 0 auto}
/* AND THE LABEL NEVER SHRINKS OR TRUNCATES (2026-08-30). flex:0 0 auto above is
   the whole rule: when the row runs out of room the SUMMARY gives way, because
   a half-written section name is worse than no summary at all. */
.sp-title{white-space:nowrap}

/* ── THE COUNT BADGE · urgency visible without opening ────────────────────
   ON THE LEFT, immediately after the label - not on the right with the
   summary, because the summary is detail and this is the thing that decides
   whether the operator opens the section at all. It carries a NUMBER so one
   and seven are distinguishable, and it NEVER hides: at the narrowest width
   the summary goes and the badge stays, because urgency outranks detail.

   Two levels only. The tones are resolved in lib/subject-panel-model.js from
   the same palette the tables paint a delinquent row with, so a red here and a
   red in the list behind it can never be two different reds. */
.sp-count{
  flex:0 0 auto;margin-left:7px;
  min-width:18px;height:18px;padding:0 5px;
  display:inline-flex;align-items:center;justify-content:center;
  border-radius:var(--radius-pill,999px);
  font-family:var(--font-mono);font-size:var(--text-2xs,.62rem);
  font-weight:var(--fw-semibold,600);font-variant-numeric:tabular-nums;
  line-height:1;letter-spacing:0
}
.sp-count[data-level="urgent"]{
  background:var(--status-overdue-bg);
  color:var(--status-overdue)
}
.sp-count[data-level="attention"]{
  background:var(--warn-bg,color-mix(in srgb,var(--warn) 15%,transparent));
  color:var(--warn)
}
/* An urgent badge on a COLLAPSED section is the one thing on the row that may
   ask for the eye. Once the section is open the operator is already looking at
   it, so the pulse stops rather than nagging. */
@media (prefers-reduced-motion:no-preference){
  .sp-sec[data-open="0"] .sp-count[data-level="urgent"]{animation:sp-pulse 2.4s ease-in-out infinite}
}
@keyframes sp-pulse{
  0%,100%{box-shadow:0 0 0 0 color-mix(in srgb,var(--status-overdue) 40%,transparent)}
  50%{box-shadow:0 0 0 3px color-mix(in srgb,var(--status-overdue) 0%,transparent)}
}
/* RULE 7: the collapsed row carries a ONE-LINE SUMMARY on the right, never a
   bare title, so the operator decides whether to open without opening. The
   text itself is DERIVED - see summaryOf() in lib/subject-panel-model.js.

   THE SUMMARY MUST NEVER CROWD THE LABEL.
     * right-aligned, one line, ellipsis - it never wraps and never pushes the
       label, because flex:1 1 auto + min-width:0 means IT is what gives way.
     * a hard 24px of clear space between the end of the label (and its badge)
       and the start of the summary; margin-left is on the summary rather than
       padding on the label so the gap survives however long the label gets.
     * the character cap and the cut-at-a-separator are the text half, in
       clampSummary(); the full string goes on title and aria-label, so a hover
       and a screen reader both get all of it. */
.sp-sum{
  /* 24px of clear space after the label AND its badge, so the summary can
     never crowd either. On the summary rather than the label so the gap
     survives however long the label gets. */
  margin-left:24px;
  flex:1 1 auto;min-width:0;font-size:var(--text-2xs,.625rem);color:var(--text-tertiary);
  text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap
}
/* CSS-drawn chevron: two borders on a rotated square. Never a character. */
/* IT POINTS DOWN, AND IT TURNS 180 DEGREES (operator correction, 2026-08-30).
   A right-pointing chevron is the web's word for "navigate away"; this control
   opens something in place, so it carries the prototype's DOWN chevron and
   rotates a full half-turn to point up when the section is open. It is drawn
   in the LABEL's colour: at --text-tertiary it was very nearly invisible, and
   an affordance nobody can see is not an affordance. */
.sp-chev{
  flex:0 0 auto;width:7px;height:7px;margin-left:2px;
  border-right:1.5px solid var(--text);border-bottom:1.5px solid var(--text);
  /* The 2px nudge keeps the V optically centred in the row in BOTH states -
     the ink sits below the box centre pointing down and above it pointing up. */
  transform:translateY(-2px) rotate(45deg);
  transition:transform var(--duration-fast,.18s) var(--ease)
}
.sp-sec[data-open="1"] .sp-chev{transform:translateY(2px) rotate(225deg)}
.sp-panel-body{display:none}
.sp-sec[data-open="1"] .sp-panel-body{display:block}

/* SCROLL CONTAINER TWO OF TWO. An open section taller than 450px scrolls
   INSIDE ITSELF, and shows the cut with a fade at the bottom edge so the
   operator can see there is more. Nothing inside it scrolls again. */
.sp-scroll{
  max-height:var(--sp-section-max);overflow-y:auto;overflow-x:hidden;
  /* 24 / 18 / 18, the prototype's own. Was 2px top, which left the first card
     touching the trigger it opened from. */
  padding:var(--space-lg) var(--sp-lg-18) var(--sp-lg-18);
  scrollbar-color:var(--scrollbar-thumb) var(--scrollbar-track)
}
.sp-sec[data-scrolling="1"] .sp-scroll{
  -webkit-mask-image:linear-gradient(to bottom,#000 calc(100% - 22px),transparent 100%);
  mask-image:linear-gradient(to bottom,#000 calc(100% - 22px),transparent 100%)
}
.sp-sec[data-scrolling="1"]{border-bottom-color:var(--border-subtle)}
.sp-cut{
  display:none;height:1px;margin:0 var(--space-lg);
  background:linear-gradient(to right,transparent,var(--border-strong,var(--border)),transparent)
}
.sp-sec[data-scrolling="1"] .sp-cut{display:block}

/* ── RECORDS · DISCLOSURE DEPTH TWO OF TWO ───────────────────────────────
   A record inside a section. It expands in place and CARRIES NO OVERFLOW RULE
   of its own - rule 5's "no third scroll". There is no third level below this:
   a nested accordion here is the defect the Discovery evidence modal shipped. */
.sp-rec{
  border:1px solid var(--border);border-radius:var(--radius-md,8px);
  margin-bottom:6px;background:var(--surface-raised)
}
.sp-rec-row{
  width:100%;display:flex;align-items:center;gap:var(--space-xs);
  padding:9px 10px;background:transparent;border:0;cursor:pointer;
  color:var(--text);font:inherit;text-align:left;min-width:0
}
.sp-rec-row:hover{background:var(--surface-card-bg-hover,var(--surface-hover))}
.sp-rec-row:focus-visible{outline:var(--focus-ring);outline-offset:-2px}
.sp-rec-name{
  flex:1 1 auto;min-width:0;font-size:var(--text-xs,.75rem);font-weight:var(--fw-medium,500);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap
}
.sp-rec-when{font-family:var(--font-mono);font-size:var(--text-2xs,.65rem);color:var(--text-tertiary);flex:0 0 auto}
.sp-rec-detail{display:none;padding:0 10px 10px;border-top:1px solid var(--border)}
.sp-rec[data-open="1"] .sp-rec-detail{display:block}
.sp-rec-detail p{font-size:var(--text-xs,.75rem);color:var(--text-secondary);padding:var(--space-sm) 0;line-height:var(--lh-normal,1.5)}
/* RULE 4: REPLY BELONGS TO A TICKET, NOT THE PANEL. It lives here, next to the
   thing being replied to, and never in the footer. */
.sp-rec-actions{display:flex;gap:6px;flex-wrap:wrap}
/* The AI draft, inside the ticket record it was read from. */
.sp-draft{margin-bottom:8px}

/* THE CONVERSATION · disclosure depth two, and it carries NO overflow of its
   own. A long thread makes the SECTION scroll, never itself: that is rule 5's
   "no third scroll", and it is the whole reason the turns are laid out as flat
   rows rather than as a bounded chat pane. */
/* THE CONVERSATION. An ordered list, because it IS one: the turns have an
   order and a screen reader is owed it. Each turn is a bounded card with its
   own header, so the boundary between two messages is a border and not a
   guessed-at gap - two replies in a row from the same side used to read as one
   long message. */
.sp-thread{
  display:flex;flex-direction:column;gap:8px;margin:0 0 8px;padding:0;
  list-style:none;counter-reset:none
}
/* NO LEFT RULE (2026-08-31 addendum 2, item 2). The 3px spine read as
   visual noise across a long thread; the side that spoke is now carried by
   a tinted surface on the bounded card itself - customer at --surface-soft,
   support at the existing accent tint - plus the name written on every
   turn's own header, so colour is still never the only carrier. */
.sp-msg{
  display:flex;flex-direction:column;gap:4px;padding:8px 10px;min-width:0;
  border:1px solid var(--border);border-radius:var(--radius-md,8px);
  background:var(--surface-soft)
}
/* THE EDITABLE AI DRAFT (2026-09-01). Deliberately the same box the read-only
   .sp-msg above draws - same border, same radius, same soft surface - because
   it occupies the same place in the record and only its editability changed.
   Every value here is an existing token; no new visual token is introduced.
   The focus ring is the platform's --accent, matching every other focusable
   control in this panel. */
.sp-draft-edit{
  display:block;width:100%;box-sizing:border-box;min-height:96px;resize:vertical;
  padding:8px 10px;margin:0 0 6px;
  border:1px solid var(--border);border-radius:var(--radius-md,8px);
  background:var(--surface-soft);color:var(--text);
  font-family:var(--font-body);font-size:var(--text-sm);line-height:var(--lh-normal)
}
.sp-draft-edit:focus-visible{
  outline:none;border-color:var(--accent);
  box-shadow:0 0 0 3px var(--color-accent-soft,var(--accent-bg))
}
.sp-msg.us{
  background:var(--accent-subtle,var(--surface-raised));
  border-color:color-mix(in srgb,var(--accent) 24%,var(--border))
}
.sp-msg-head{
  display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;
  font-family:var(--font-mono);font-size:var(--text-2xs,.6rem);letter-spacing:var(--ls-wide,.06em);
  text-transform:uppercase;color:var(--text-tertiary)
}
.sp-msg-who{color:var(--text-secondary);font-weight:var(--fw-semibold,600)}
.sp-msg.us .sp-msg-who{color:var(--accent)}
/* The time sits at the far end of the turn's header, so the eye reads name then
   clock on every card at the same place. */
.sp-msg-when{margin-left:auto;font-variant-numeric:tabular-nums;white-space:nowrap}
.sp-msg-when.is-undated{font-style:italic;text-transform:none;letter-spacing:0}
/* pre-line, so a form submission keeps the shape of its fields. A ticket body
   that arrives as a list and is drawn as one paragraph is unreadable. */
.sp-msg-text{
  font-size:var(--text-xs,.74rem);color:var(--text-secondary);line-height:var(--lh-normal,1.5);
  overflow-wrap:anywhere;white-space:pre-line
}
/* A message shown in part says so on the message, not in a footnote. */
.sp-msg-part{
  font-size:var(--text-2xs,.62rem);color:var(--text-tertiary);font-style:italic;line-height:1.45
}
/* Personal detail removed at ingest. The chip names WHAT was removed; it is
   never the marker, and never the value. */
.sp-redact{
  display:inline-block;padding:0 6px;border-radius:var(--radius-pill,999px);
  background:var(--surface-hover,var(--surface-raised));border:0;
  color:var(--text-tertiary);font-size:var(--text-2xs,.62rem);vertical-align:baseline
}

/* A TAG IS A LABEL, NOT A BUTTON (2026-08-31 addendum 2, item 1). The
   outlined pill read as clickable - status, priority and category are none
   of them actions. No stroke; the state reads from a tinted background
   instead, off the same five-kind palette every other surface in this file
   uses (the `.sp-tag.sp-tone-*` rules below, alongside `.sp-badge`'s own). */
.sp-tag{
  font-family:var(--font-mono);
  font-size:var(--text-2xs,.62rem);padding:3px 7px;border-radius:var(--radius-pill,999px);
  border:none;background:var(--surface-soft);color:var(--text-secondary);
  flex:0 0 auto;white-space:nowrap
}
/* The original's record row carries a run of small tags - the state, the
   priority, whether it was resolved - then the date. The quiet variant is for
   the ones that are context rather than status. */
.sp-tag.quiet{color:var(--text-tertiary)}
.sp-tags{display:flex;align-items:center;gap:6px;flex:0 0 auto;min-width:0}
@media (max-width:560px){.sp-tags .sp-tag.quiet{display:none}}

/* ── ONE STATE PALETTE ACROSS EVERY SURFACE ──────────────────────────────
   The five named kinds, bound to the semantic status tokens the tables already
   use, so a customer cannot be amber in a list and red in the panel that opens
   from it. `no-record` and `no-feed` are deliberately NEUTRAL: absence is not
   delinquency, and a thing nobody has told us is not a thing that went wrong. */
.sp-tone-down{color:var(--status-down)}
.sp-tone-delinquent{color:var(--status-overdue)}
.sp-tone-churned{color:var(--status-churned)}
.sp-tone-no-feed{color:var(--status-nofeed)}
.sp-tone-no-record{color:var(--text-tertiary)}
.sp-tone-ok{color:var(--status-active)}
.sp-tone-neutral{color:var(--text-secondary)}
.sp-badge.sp-tone-down{background:var(--status-down-bg)}
.sp-badge.sp-tone-delinquent{background:var(--status-overdue-bg)}
.sp-badge.sp-tone-churned{background:var(--status-churned-bg)}
.sp-badge.sp-tone-no-feed{background:var(--status-nofeed-bg)}
.sp-badge.sp-tone-ok{background:var(--status-active-bg)}
/* The ticket record row's status tag (unresolved/resolved) carries a tone
   too, so it gets the same tinted background instead of the plain
   --surface-soft every other .sp-tag falls back to. */
.sp-tag.sp-tone-down{background:var(--status-down-bg)}
.sp-tag.sp-tone-delinquent{background:var(--status-overdue-bg)}
.sp-tag.sp-tone-churned{background:var(--status-churned-bg)}
.sp-tag.sp-tone-no-feed{background:var(--status-nofeed-bg)}
.sp-tag.sp-tone-ok{background:var(--status-active-bg)}
/* THE HOLE IN THE PALETTE (UX audit 2026-09, finding 1). lib/account-axes.js
   emits the tones warn, danger and muted and nothing styled them, so an
   Overdue, Delinquent or Cancelled pill rendered uncoloured. The three now
   carry the same state colours every other surface uses. */
.sp-tone-warn{color:var(--warn)}
.sp-tone-danger{color:var(--danger)}
.sp-tone-muted{color:var(--text-tertiary)}
.sp-badge.sp-tone-warn,.sp-tag.sp-tone-warn{background:var(--warn-bg)}
.sp-badge.sp-tone-danger,.sp-tag.sp-tone-danger{background:var(--danger-bg)}
.sp-badge.sp-tone-muted,.sp-tag.sp-tone-muted{background:var(--glass-bg)}
.sp-badge.sp-tone-neutral{background:var(--surface-soft)}

/* ── THE CONNECTION BLOCK (2026-09-03) · components/connection-block.js ────
   Rendered in both the subject panel and the ticket panel. Layout only; the
   words are lib/connection-diagnostics.js's and the colours are the tones
   above. */
.cx{display:flex;flex-direction:column;gap:8px}
.cx-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.cx-checked{font-size:var(--text-2xs,.7rem);color:var(--text-tertiary)}
.cx-reason{font-size:var(--text-sm,.82rem);color:var(--text);line-height:var(--lh-normal,1.5);margin:0}
.cx-empty-title{font-weight:var(--fw-semibold,600);font-size:var(--text-sm,.82rem);margin:0;color:var(--text)}
.cx-at{font-style:normal;color:var(--text-tertiary);font-size:var(--text-2xs,.62rem);margin-left:4px}
.cx-actions{margin-top:4px}
.cx-gate{margin:0}
.cx [data-cx-action="reboot"][disabled]{opacity:.55;cursor:not-allowed}
.cx .skel{display:inline-block;border-radius:4px;background:var(--surface-hover,var(--surface-raised))}

/* ── BLOCK BODY PRIMITIVES ───────────────────────────────────────────────── */

.sp-grid2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--space-sm)}
@media (max-width:560px){.sp-grid2{grid-template-columns:minmax(0,1fr)}}
/* CARD WEIGHT. The original carries a TINTED surface for a card whose state is
   good, and a heavier border with it - that tint is most of what makes the panel
   read as rich rather than as a list of boxes. It is bound to the semantic
   status tokens, so a client's brand lock repaints it, and to the same five-kind
   palette the tables use, so a card cannot be green here and amber in the list
   it opened from. */
.sp-tile{
  background:var(--surface-raised);border:1px solid var(--border);
  border-radius:var(--radius-md,8px);padding:var(--space-md) 14px;min-width:0
}
/* THE TINT IS FLAT, AS THE PROTOTYPE DRAWS IT (parity pass 2026-08-30).
   These were a gradient fading to transparent at 68%, so the bottom two-thirds
   of every card fell back to plain --surface-raised and the tint read as a
   smudge at the top edge rather than as a coloured surface. The prototype uses
   one flat tone per state (--success-surface, --danger-surface); this is that,
   mixed against the card's own surface so it still repaints under a brand lock
   and still sits at the same lightness in both themes.
   The card's HEADLINE takes the state colour too - a tinted card with a plain
   white headline reads as decoration rather than as a status. */
.sp-tile.is-good{
  background:color-mix(in srgb,var(--status-active) 12%,var(--surface-raised));
  border-color:color-mix(in srgb,var(--status-active) 34%,var(--border))
}
.sp-tile.is-good>strong{color:var(--status-active)}
.sp-tile.is-risk{
  background:color-mix(in srgb,var(--status-overdue) 12%,var(--surface-raised));
  border-color:color-mix(in srgb,var(--status-overdue) 30%,var(--border))
}
.sp-tile.is-risk>strong{color:var(--status-overdue)}
.sp-tile.is-quiet{
  background:color-mix(in srgb,var(--status-nofeed) 10%,var(--surface-raised));
  border-color:color-mix(in srgb,var(--status-nofeed) 26%,var(--border))
}
/* The card's own label. Every card in the original carries one; it is what lets
   a two-up grid be read without a heading above it. */
.sp-tile>.sp-eyebrow{margin-bottom:5px}
.sp-tile>strong{font-size:var(--fs-md,.95rem);line-height:1.2}
.sp-tile strong{display:block;font-size:var(--text-sm,.8rem);font-weight:var(--fw-semibold,600);margin-bottom:3px;letter-spacing:var(--ls-tight,-.01em)}
.sp-tile p{font-size:var(--text-2xs,.7rem);color:var(--text-secondary);line-height:var(--lh-normal,1.5);overflow-wrap:anywhere}
.sp-tile small{display:block;margin-top:6px;font-size:var(--text-2xs,.65rem);color:var(--text-tertiary)}

.sp-dl{margin-top:var(--space-sm);font-size:var(--text-xs,.75rem)}
.sp-dl>div{display:flex;justify-content:space-between;gap:var(--space-md);padding:4px 0;border-top:1px solid var(--border)}
.sp-dl dt{color:var(--text-tertiary);flex:0 0 auto}
.sp-dl dd{font-weight:var(--fw-medium,500);margin:0;text-align:right;min-width:0;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}

.sp-note{font-size:var(--text-2xs,.7rem);color:var(--text-secondary);line-height:var(--lh-normal,1.5)}
.sp-sub{
  font-family:var(--font-mono);font-size:var(--text-2xs,.65rem);letter-spacing:var(--ls-wide,.08em);
  text-transform:uppercase;color:var(--text-tertiary);margin:var(--space-md) 0 var(--space-sm)
}
.sp-sub:first-child{margin-top:0}

.sp-list{list-style:none;display:flex;flex-direction:column;gap:4px;margin:0;padding:0}
.sp-list li{
  padding:8px 10px;background:var(--surface-raised);border:1px solid var(--border);
  border-radius:var(--radius-md,8px);font-size:var(--text-xs,.75rem);min-width:0
}
.sp-list time{display:block;font-family:var(--font-mono);font-size:var(--text-2xs,.65rem);color:var(--text-tertiary);margin-bottom:3px}
.sp-list p{color:var(--text-secondary);line-height:var(--lh-normal,1.5);overflow-wrap:anywhere}

.sp-kv{display:flex;flex-direction:column;gap:4px}
.sp-kv>div{
  display:flex;justify-content:space-between;gap:var(--space-md);padding:7px 10px;
  font-size:var(--text-xs,.75rem);background:var(--surface-raised);
  border:1px solid var(--border);border-radius:var(--radius-md,8px);min-width:0
}
.sp-kv span{color:var(--text-secondary);flex:0 0 auto}
.sp-kv strong{font-weight:var(--fw-medium,500);text-align:right;min-width:0;overflow-wrap:anywhere}

/* THE QUIET SUMMARY · the counts row above a record list. Bold figures against
   muted words, ruled off from the list below it, exactly as the original draws
   it - the size of the thing before you open it, stated as numbers. */
.sp-counts{
  display:flex;flex-wrap:wrap;gap:var(--space-md);
  font-size:var(--text-2xs,.7rem);color:var(--text-secondary);
  padding:var(--space-sm) 0 10px;margin-bottom:10px;
  border-bottom:1px solid var(--border)
}
.sp-counts strong{color:var(--text);font-weight:var(--fw-semibold,600);font-variant-numeric:tabular-nums}

/* THE DECISION SUMMARY · the block that says what needs attention, with its own
   eyebrow, headline and body. Heavier than a note and quieter than an alarm. */
.sp-decision{
  display:flex;gap:11px;padding:var(--space-md) 13px;margin-bottom:10px;min-width:0;
  border-radius:var(--radius-md,8px);border:1px solid var(--border);
  background:var(--surface-raised)
}
/* NEEDS ATTENTION IS AMBER, NOT RED, and it matches the briefing card that
   states the same decision two blocks down. The prototype draws it amber for a
   reason worth keeping: this card says "look at this", not "this has failed",
   and the panel already spends red on states that genuinely have. */
.sp-decision.is-risk{
  border-color:color-mix(in srgb,var(--warn) 32%,var(--border));
  background:color-mix(in srgb,var(--warn) 10%,var(--surface-raised))
}
.sp-decision .sp-eyebrow{margin-bottom:3px}
.sp-decision strong{display:block;font-size:var(--text-sm,.82rem);font-weight:var(--fw-semibold,600);margin-bottom:3px}
.sp-decision p{font-size:var(--text-2xs,.72rem);color:var(--text-secondary);line-height:var(--lh-normal,1.5)}
/* CSS-drawn attention mark: a disc with a bar and a dot. No text glyph. */
/* The same 26px bordered tile the briefing cards carry, so the two cards that
   state one decision look like one thing. */
.sp-alert{
  flex:0 0 auto;width:26px;height:26px;margin-top:1px;position:relative;
  border:1px solid var(--border-strong);border-radius:var(--radius-md,8px);
  background:transparent;color:var(--text-tertiary)
}
.sp-decision.is-risk .sp-alert{
  border-color:color-mix(in srgb,var(--warn) 50%,var(--border));color:var(--warn)
}
.sp-alert::before,.sp-alert::after{
  content:"";position:absolute;left:50%;transform:translateX(-50%);
  background:currentColor;border-radius:1px
}
.sp-alert::before{top:6px;width:2px;height:8px}
.sp-alert::after{top:17px;width:2px;height:2px;border-radius:50%}

/* lifecycle stepper */
/* THE STEPPER carries a DOT on the rail, as the original draws it - the rail
   alone reads as a progress bar, and the dots are what make it read as named
   steps with a position in them. Both are CSS; there is no glyph. */
.sp-stepper{display:flex;gap:0;list-style:none;margin:0 0 var(--space-md);padding:0}
.sp-stepper li{
  flex:1 1 0;min-width:0;text-align:center;font-size:var(--text-2xs,.6rem);
  color:var(--text-tertiary);padding:0 2px;position:relative;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap
}
.sp-stepper li::before{
  content:"";display:block;height:2px;background:var(--border-strong,var(--border));
  margin-bottom:11px;border-radius:1px
}
.sp-stepper li::after{
  content:"";position:absolute;top:-2px;left:50%;transform:translateX(-50%);
  width:7px;height:7px;border-radius:50%;
  background:var(--surface);border:2px solid var(--border-strong,var(--border))
}
.sp-stepper li.done::before,.sp-stepper li.now::before{background:var(--accent)}
.sp-stepper li.done::after{background:var(--accent);border-color:var(--accent)}
/* THE CURRENT NODE IS RINGED AMBER, and deliberately not the brand accent.
   Done steps are the accent; if "where this account has got to" were also the
   accent the two would be one colour and the stepper would read as finished.
   Amber is the warn token, so it repaints with the brand like everything else
   and means the same thing here as it does in every table. */
.sp-stepper li.now::after{
  background:var(--surface);border-color:var(--warn);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--warn) 20%,transparent)
}
.sp-stepper li.now{color:var(--text);font-weight:var(--fw-semibold,600)}
/* THE THIRD STATE (2026-08-31, lib/lifecycle-stage-model.js). A step before
   the account's current stage that carries no onboarding_checklist entry of
   its own - completed before this platform tracked the account, not "done
   here" (no accent, no claim of credit for someone else's work) and not
   "outstanding" (not the plain unlit dot, which would read as six steps of
   real work left to do). --text-tertiary at reduced opacity: visibly filled
   (unlike the hollow unlit dot) and visibly muted (unlike the accent-filled
   done dot) - the same 55% figure the shared lifecycle-timeline.js ribbon
   already uses for its own "not tracked" state, so the two read as one
   visual language rather than two competing mutes. */
.sp-stepper li.inherited::before{background:var(--text-tertiary);opacity:.55}
.sp-stepper li.inherited::after{
  background:var(--text-tertiary);border-color:var(--text-tertiary);opacity:.55
}
.sp-stepper li.inherited{color:var(--text-tertiary)}

/* checklist */
.sp-check{list-style:none;display:flex;flex-direction:column;gap:4px;margin:0;padding:0}
.sp-check li{
  display:flex;align-items:center;gap:8px;min-height:48px;padding:8px;
  background:var(--surface-raised);
  border:1px solid color-mix(in srgb,var(--border) 75%,transparent);
  border-radius:var(--radius-md,8px);min-width:0
}
.sp-box{
  width:15px;height:15px;flex:0 0 auto;border:1.5px solid var(--border-strong,var(--border));
  border-radius:3px;background:transparent;position:relative;padding:0
}
.sp-box[aria-checked="true"]{background:var(--accent);border-color:var(--accent)}
/* the tick is CSS, not a character */
.sp-box[aria-checked="true"]::after{
  content:"";position:absolute;left:4px;top:1px;width:4px;height:8px;
  border-right:2px solid var(--text-on-accent);border-bottom:2px solid var(--text-on-accent);
  transform:rotate(42deg)
}
.sp-check div{flex:1 1 auto;min-width:0}
.sp-check strong{display:block;font-size:var(--text-xs,.72rem);font-weight:var(--fw-medium,500)}
.sp-check span{font-size:var(--text-2xs,.68rem);color:var(--text-secondary)}
.sp-check small{font-family:var(--font-mono);font-size:var(--text-2xs,.6rem);color:var(--text-tertiary);flex:0 0 auto}

/* reliability ring · the ONE money visual, and it lives in the billing block */
.sp-reli{display:flex;align-items:center;gap:var(--space-sm);min-width:0}
.sp-reli>p{flex:1 1 auto;min-width:0}
.sp-ring{
  --p:0;width:64px;height:64px;border-radius:50%;flex:0 0 auto;
  background:conic-gradient(var(--accent) calc(var(--p) * 1%),var(--surface-hover,var(--border)) 0);
  display:grid;place-items:center
}
.sp-ring>div{
  width:50px;height:50px;border-radius:50%;background:var(--surface-raised);
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  text-align:center;line-height:1.05;padding:0 2px
}
/* "12/12 paid" must fit inside the disc at every zoom, so the figure is set in
   tabular numerals at a fixed small size rather than inheriting a scale that can
   overflow the ring it sits in. */
.sp-ring strong{font-size:10.5px;font-weight:var(--fw-semibold,600);font-variant-numeric:tabular-nums;letter-spacing:-.03em}
.sp-ring span{font-size:9px;color:var(--text-secondary)}
/* THE TRAILING-TWELVE-MONTH STRIP. All twelve months are DRAWN, including the
   ones with no record: a strip that renders only the paid months is a bar chart
   with its denominator hidden, and the whole point of this figure is that the
   denominator is the months this account was actually billed. The empty track
   has to be visible for that to be true, which is why it is a mixed border
   rather than a surface colour that can resolve to the card behind it. */
.sp-bars{display:flex;gap:3px;margin:var(--space-sm) 0}
.sp-bars span{
  flex:1 1 0;min-width:0;height:22px;border-radius:2px;
  background:color-mix(in srgb,var(--border-strong,var(--border)) 70%,transparent)
}
.sp-bars span.on{background:var(--accent)}
.sp-bars span.late{background:var(--warn)}

/* THE BRIEFING CARDS · the original's three-card briefing, each with its own
   eyebrow naming what KIND of thing it is (the next step, how to talk to this
   person, what to check about the service), then the headline, then the reason.
   The eyebrow is not decoration: three cards that all say "strong then prose"
   read as one repeated thing, and the labels are what separate them. */
.sp-briefs{display:flex;flex-direction:column;gap:7px;margin-bottom:var(--space-md)}
.sp-brief{
  display:flex;gap:11px;padding:var(--sp-md-14);background:var(--surface-raised);
  border:1px solid var(--border);border-radius:10px;min-width:0
}
.sp-brief.risk{
  border-color:color-mix(in srgb,var(--warn) 32%,var(--border));
  background:color-mix(in srgb,var(--warn) 10%,var(--surface-raised))
}
.sp-brief .sp-eyebrow{margin-bottom:3px}
.sp-brief strong{display:block;font-size:var(--text-sm,.82rem);font-weight:var(--fw-semibold,600);margin-bottom:3px}
.sp-brief p{font-size:var(--text-2xs,.72rem);color:var(--text-secondary);line-height:var(--lh-normal,1.5)}
.sp-brief>div{min-width:0}
/* A lead line above the briefing, as the original has. */
.sp-intro{font-size:var(--text-2xs,.72rem);color:var(--text-secondary);margin-bottom:10px;line-height:var(--lh-normal,1.5)}
/* CSS-drawn marks, so there is no character to render wrong. */
/* THE ICON TILE, as the prototype draws it: a 26px bordered rounded square,
   not a 14px dot. The dot read as a bullet, which is why three briefing cards
   read as a list; the tile is what gives each card a head of its own weight.
   Still CSS-drawn - there is no glyph in this file (rule 7). */
.sp-mark{
  flex:0 0 auto;width:26px;height:26px;position:relative;
  border:1px solid var(--border-strong);border-radius:var(--radius-md,8px);
  background:transparent;color:var(--text-tertiary)
}
.sp-brief.risk .sp-mark{
  border-color:color-mix(in srgb,var(--warn) 50%,var(--border));color:var(--warn)
}
/* the mark inside the tile: a bar over a dot, in currentColor */
.sp-mark::before,.sp-mark::after{
  content:"";position:absolute;left:50%;transform:translateX(-50%);
  background:currentColor;border-radius:1px
}
.sp-mark::before{top:6px;width:2px;height:8px}
.sp-mark::after{top:17px;width:2px;height:2px;border-radius:50%}

/* ── RULE 3 · AXIS B · ONE SHARED COMPONENT PER STATE ────────────────────
   Each of these renders identically in every block. A block NEVER disappears
   for any of them, and none of them draws a zero. */

.sp-state{
  padding:10px 12px;border:1px solid var(--border);border-radius:var(--radius-md,8px);
  background:var(--surface-raised);min-width:0
}
.sp-state .h{font-size:var(--text-xs,.75rem);font-weight:var(--fw-medium,500);margin-bottom:3px;color:var(--text)}
.sp-state .s{font-size:var(--text-2xs,.7rem);color:var(--text-secondary);line-height:var(--lh-normal,1.5);overflow-wrap:anywhere}
.sp-state.is-error{border-color:color-mix(in srgb,var(--danger) 30%,var(--border))}
.sp-state.is-not-connected .h,.sp-state.is-no-record .h{color:var(--text-secondary)}
.sp-state-actions{margin-top:var(--space-sm);display:flex;gap:6px;flex-wrap:wrap}
.sp-caveat{
  display:block;margin-top:6px;font-family:var(--font-mono);font-size:var(--text-2xs,.62rem);
  color:var(--text-tertiary)
}

/* LOADING · SHAPE-MATCHED SKELETON, never a spinner and never a zero. The
   heights below mirror the real block layouts so nothing jumps when the data
   lands: two tiles, a key/value run, a ring row, a record list. */
.sp-skel{display:flex;flex-direction:column;gap:var(--space-sm)}
.sp-skel i{
  display:block;background:var(--skeleton-bg);border-radius:var(--skeleton-radius,4px);
  position:relative;overflow:hidden
}
.sp-skel i::after{
  content:"";position:absolute;inset:0;
  background:linear-gradient(90deg,transparent,var(--skeleton-highlight),transparent);
  transform:translateX(-100%);animation:sp-shimmer var(--skeleton-shimmer-dur,1.4s) infinite
}
@keyframes sp-shimmer{to{transform:translateX(100%)}}
@media (prefers-reduced-motion:reduce){.sp-skel i::after{animation:none}}
.sp-skel .row{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--space-sm)}
.sp-skel .tile{height:74px}
.sp-skel .line{height:var(--skeleton-line-h,12px)}
.sp-skel .line.short{width:52%}
.sp-skel .line.mid{width:74%}
/* The skeleton's parts are sized from the REAL components above, so a shape
   change there has to be reflected here or the panel jumps again. */
.sp-skel .rec{height:46px}          /* .sp-rec-row */
.sp-skel .tile.tall{height:150px}   /* a .sp-tile carrying a .sp-dl run */
.sp-skel .bars{height:22px}         /* .sp-bars */
.sp-skel .step{height:20px}         /* .sp-stepper */
.sp-skel .check{height:48px}        /* .sp-check li */
.sp-skel .decision{height:76px}     /* .sp-decision */
.sp-skel .brief{height:64px}        /* .sp-brief */
.sp-skel .ring{height:64px;width:64px;border-radius:50%}
.sp-skel .ringrow{display:flex;gap:var(--space-md);align-items:center}
.sp-skel .ringrow .col{flex:1 1 auto;display:flex;flex-direction:column;gap:6px}

/* ── FOOTER · EXACTLY TWO BUTTONS, ALWAYS ───────────────────────────────
   margin-top:auto pins it to the bottom whatever the body does. The primary is
   ONE contextual verb with an object, and it is the same decision the memories
   next-step card made - both read nextStep() in lib/subject-panel-model.js.
   The second is always the assistant. There is never a third. */
/* A footer button the model withholds is withheld: the attribute wins over the display rule. */
.sp-btn[hidden]{display:none}
.sp-foot{
  flex:0 0 auto;margin-top:auto;display:flex;gap:var(--space-sm);
  padding:var(--space-sm) var(--space-lg);border-top:1px solid var(--border);
  background:var(--surface)
}
.sp-btn{
  flex:1 1 0;min-width:0;font:inherit;font-size:var(--text-sm,.8rem);font-weight:var(--fw-medium,500);
  padding:9px 12px;border-radius:var(--radius-md,8px);cursor:pointer;
  border:1px solid var(--border-strong,var(--border));background:transparent;color:var(--text);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap
}
.sp-btn:hover{background:var(--surface-hover,var(--surface-raised))}
.sp-btn:focus-visible{outline:var(--focus-ring);outline-offset:2px}
.sp-btn-primary{background:var(--accent);color:var(--text-on-accent);border-color:var(--accent)}
.sp-btn-primary:hover{background:var(--accent);filter:brightness(1.08)}
/* An `unknown` subject has no primary. Rule 4: an action that is not wired is
   ABSENT - so the button is not rendered at all, and the assistant button takes
   the full width rather than sitting beside a grey shrug. */
.sp-foot[data-primary="none"] .sp-btn{flex:1 1 100%}

.sp-btn-sm{
  font:inherit;font-size:var(--text-2xs,.7rem);padding:5px 10px;border-radius:var(--radius-md,8px);
  border:1px solid var(--border-strong,var(--border));background:transparent;color:var(--text);cursor:pointer
}
.sp-btn-sm:hover{background:var(--surface-hover,var(--surface-raised))}
.sp-btn-sm:focus-visible{outline:var(--focus-ring);outline-offset:2px}
a.sp-btn-sm{text-decoration:none;display:inline-block}

/* ── NARROW VIEWPORTS ────────────────────────────────────────────────────
   Below the panel's own width there is no 550px to hold. It goes full-bleed
   rather than overflowing the viewport, because a panel wider than the screen
   would scroll the PAGE horizontally, and rule 5 forbids that at any width. */
/* AT THE NARROWEST SUPPORTED WIDTH THE SUMMARY HIDES ENTIRELY rather than
   compressing the label. The badge never hides: urgency outranks detail, and a
   number the operator can still see is the whole point of putting it on the
   left. The full summary is still on the trigger's title and aria-label, so
   nothing is lost to a screen reader or to a long press. */
@media (max-width:420px){
  .sp-sum{display:none}
}

@media (max-width:560px){
  .sp-panel{width:100vw}
  .sp-strip{grid-template-columns:minmax(0,1fr);}
  .sp-strip>div{border-right:0;border-bottom:1px solid var(--border)}
  .sp-strip>div:last-child{border-bottom:0}
}
