/* GoboPhone — app UI.
 *
 * Matches the Gobo Studios *app* family (KaraokeFlow, SceneRig), not the
 * marketing site: dark navy ground, violet accent, system fonts. The
 * marketing DESIGN.md olive/sage system is for gobostudios.com and does not
 * apply here.
 *
 * Palette is KaraokeFlow's, so an account moving between products feels like
 * one suite. SceneRig agrees on the ground and the violet.
 */

:root {
  --bg:           #1a1a2e;
  --bg-elevated:  #202038;
  --card:         #252540;
  --border:       #33334d;
  --text:         #e0e0e0;
  --dim:          #9a9ab0;
  --primary:      #6c5ce7;
  --primary-dark: #5a4bd1;
  --success:      #00b894;
  --danger:       #e74c3c;
  --warning:      #fdcb6e;
  --radius:       8px;
  --radius-sm:    6px;
  --shadow:       0 2px 8px rgba(0,0,0,.3);
  --font: 'Helvetica Neue', Helvetica, Arial, -apple-system, BlinkMacSystemFont,
          'Segoe UI', Roboto, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.6 var(--font);
}

/* ---- chrome ---------------------------------------------------------- */

.topbar {
  background: rgba(26,26,46,.92);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 10;
}
.topbar-inner {
  max-width: 1100px; margin: 0 auto; padding: .85rem 1.25rem;
  display: flex; align-items: center; gap: 1.5rem;
}
.wordmark { font-weight: 700; font-size: 1.05rem; letter-spacing: -.01em; }
.wordmark span { color: var(--primary); }
.topnav { display: flex; gap: .25rem; margin-left: auto; flex-wrap: wrap; }
.topnav a {
  color: var(--dim); text-decoration: none; font-size: .9rem;
  padding: .4rem .7rem; border-radius: var(--radius-sm);
}
.topnav a:hover { color: var(--text); background: var(--bg-elevated); }
.topnav a.on { color: var(--text); background: var(--card); }

/* The Configuration menu. A <details> so it works with no JavaScript and the
   browser handles open/closed and keyboard focus itself. */
.topnav .menu { position: relative; }
.topnav .menu > summary {
  list-style: none; cursor: pointer; color: var(--dim); font-size: .9rem;
  padding: .4rem .7rem; border-radius: var(--radius-sm); white-space: nowrap;
}
.topnav .menu > summary::-webkit-details-marker { display: none; }
.topnav .menu > summary::after { content: ' ▾'; opacity: .6; }
.topnav .menu > summary:hover { color: var(--text); background: var(--bg-elevated); }
/* Lit while you are on any page inside it, so the menu says where you are
   rather than only where you can go. */
.topnav .menu.on > summary { color: var(--text); background: var(--card); }

.topnav .menu-items {
  position: absolute; right: 0; top: calc(100% + .35rem); z-index: 40;
  display: flex; flex-direction: column; min-width: 12rem;
  background: var(--card, #1b1b28); border: 1px solid var(--line, #33334d);
  border-radius: var(--radius-sm); padding: .3rem;
  box-shadow: 0 10px 30px rgba(0, 0, 0, .45);
}
.topnav .menu-items a { padding: .5rem .7rem; }

/* On a narrow screen a floating panel is worse than a list: it can reach off
   the edge, and there is room below anyway. */
@media (max-width: 700px) {
  .topnav .menu { width: 100%; }
  .topnav .menu-items { position: static; box-shadow: none; border: 0; padding: 0 0 0 .7rem; }
}

main { max-width: 1100px; margin: 0 auto; padding: 2rem 1.25rem 4rem; }

h1 { font-size: 1.6rem; font-weight: 700; margin: 0 0 .25rem; letter-spacing: -.01em; }
h2 { font-size: 1.1rem; font-weight: 700; margin: 2rem 0 .85rem; }
h3 { font-size: 1rem; font-weight: 700; margin: 0 0 .3rem; }
.sub { color: var(--dim); margin: 0 0 2rem; }
.hint { color: var(--dim); font-size: .85rem; margin: .35rem 0 0; }

/* ---- stat tiles ------------------------------------------------------- */

.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: .85rem; }
.stat {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 1rem 1.1rem;
}
.stat .value { font-size: 1.7rem; font-weight: 700; line-height: 1.1; letter-spacing: -.02em; }
.stat .label { color: var(--dim); font-size: .8rem; text-transform: uppercase; letter-spacing: .05em; margin-top: .3rem; }
.stat.warn .value { color: var(--warning); }
.stat.bad  .value { color: var(--danger); }

/* ---- cards ------------------------------------------------------------ */

.card {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 1.1rem 1.25rem; margin-bottom: .85rem;
}
.card.ok   { border-left: 3px solid var(--success); }
.card.off  { border-left: 3px solid var(--border); }
.card.bad  { border-left: 3px solid var(--danger); }
.card-head { display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; }
.meta { color: var(--dim); font-size: .87rem; margin: .35rem 0 0; }
.meta code { background: var(--bg); padding: .1rem .35rem; border-radius: 4px; font: .85em var(--mono); }

/* ---- badges ----------------------------------------------------------- */

.badge {
  font-size: .7rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em;
  padding: .2rem .45rem; border-radius: 4px; white-space: nowrap;
}
.badge-on   { background: rgba(0,184,148,.15);  color: var(--success); }
.badge-off  { background: rgba(154,154,176,.15); color: var(--dim); }
.badge-warn { background: rgba(253,203,110,.15); color: var(--warning); }
.badge-tier { background: var(--primary); color: #fff; }

/* E911 reads at a glance or it does not do its job. Solid fills rather than
   the tinted badges elsewhere: this is the one status worth shouting. */
.badge-e911-yes { background: #00b894; color: #06281f; }
.badge-e911-no  { background: #d63031; color: #fff; }

/* Buttons need room. A control flush against the text above it reads as part
   of the paragraph, and every form on this site had that problem. */
.btn { margin-top: 1rem; }
.btn-sm { margin-top: 0; }
.inline-form .btn { margin-top: 0; }
form + .btn, .btn + .btn { margin-left: .5rem; }

/* ---- forms ------------------------------------------------------------ */

label { display: block; font-weight: 600; font-size: .85rem; margin: 1rem 0 .35rem; }
label:first-child { margin-top: 0; }
/* Checkboxes and radios are not text fields: the rule below would give them a
   42px height and the full width of the form, which is where the enormous
   controls on the People page came from. */
input[type="checkbox"], input[type="radio"] {
  width: 1.05rem; height: 1.05rem; min-height: 0; padding: 0;
  flex: none; margin: .15rem 0 0; accent-color: var(--accent, #6c5ce7);
}
/* 🔴 `hidden` is only a UA rule of `display: none`, so any author rule setting
   display beats it — which is why the invite checkbox stayed on screen for every
   role. Anything given `hidden` must actually be hidden. */
[hidden] { display: none !important; }

.choice { display: flex; gap: .55rem; align-items: flex-start; margin: .45rem 0; font-weight: 400; font-size: .92rem; }
.choice > span { flex: 1; }
/* Only the control and its name are clickable. A label that also wraps the
   explanation makes reading the explanation change the setting. */
.pickable { display: inline-flex; align-items: center; gap: .5rem; margin: 0; cursor: pointer; font-weight: 400; }
.choice { align-items: center; flex-wrap: wrap; }
.choice .badge { margin-left: .2rem; }
.choice + .choice { margin-top: .35rem; }

/* Two fields that belong together, side by side until there is no room. */
.field-row { display: flex; flex-wrap: wrap; gap: 0 1rem; }
/* 🔴 Every label in the row starts at the same height. `label:first-child` zeroes
   the top margin, so only the first one got it and the second sat a rem lower —
   two fields side by side that did not line up. */
.field-row > label { flex: 1 1 16rem; min-width: 0; margin-top: 0; }

/* A choice on one line: control, bold name, description.
   
   🔴 The name and the description are one inline flow, so they share a baseline
   by construction. They used to be two flex items — a `.pickable` wrapping the
   control and the name, and the description beside it — and an inline-flex box
   takes its baseline from its first item, which is a radio and has no text
   baseline. The bold name therefore sat a little off from the words explaining
   it, on every row.
   
   The control is a sibling instead, aligned to the first line rather than to a
   baseline it cannot supply. Only the name is a label, because reading an
   explanation must not change a setting. */
.choice-row {
  display: flex; flex-wrap: nowrap; align-items: flex-start;
  gap: .55rem; margin: .45rem 0;
}
.choice-row > input { flex: 0 0 auto; margin-top: .25rem; }
.choice-text { flex: 1 1 auto; min-width: 0; }
.choice-text > label {
  /* The gap between the name and its description is deliberate and wide enough
     to read as a separation rather than a word space. */
  display: inline; margin: 0 1.25rem 0 0; font-weight: 600; font-size: inherit;
  cursor: pointer;
}
.choice-text .hint { display: inline; margin: 0; padding: 0; }

/* Inboxes: a tile each, one to three columns depending on the width.
   
   `auto-fit` with a 14rem floor gives three across a wide window, two on a
   tablet and one on a phone without naming any of those widths. */
.inbox-grid {
  display: grid; gap: .5rem; margin-top: .5rem;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
}
.inbox-card {
  display: flex; align-items: flex-start; gap: .55rem; margin: 0;
  padding: .6rem .7rem; font-weight: 400; cursor: pointer;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.inbox-card:has(input:checked) {
  border-color: var(--primary); box-shadow: inset 0 0 0 1px var(--primary);
}
.inbox-card input { flex: 0 0 auto; margin-top: .15rem; }
.inbox-card > span { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.inbox-card .hint { display: block; margin: 0; padding: 0; }
.inbox-card .badge { align-self: flex-start; }

/* 🔴 The description lines up with the label, not with the checkbox.
   
   It was `display: inline`, so the explanation ran on from the bold name and
   wrapped back to the far left — under the control rather than under the words
   it explains. On a list of roles, or of inboxes where the second line is the
   phone number, that reads as though the text belongs to the row above.
   
   The indent is the control plus its gap, so the two lines stack:
   
       [x] Manager
           Everything except the plan and billing. */
.choice .hint {
  display: block;
  margin: .1rem 0 0;
  padding-left: 1.55rem;
}

input:not([type="checkbox"]):not([type="radio"]), select, textarea {
  width: 100%; padding: .6rem .7rem; min-height: 42px;
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  font: inherit;
}
textarea { min-height: 5rem; resize: vertical; }
input:focus, select:focus, textarea:focus, button:focus-visible, a:focus-visible {
  outline: none; box-shadow: 0 0 0 3px rgba(108,92,231,.4);
}
.row { display: flex; gap: .75rem; flex-wrap: wrap; }
.row > * { flex: 1; min-width: 8rem; }

.btn {
  display: inline-block; padding: .6rem 1.1rem; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--bg-elevated); color: var(--text);
  font: inherit; font-weight: 600; cursor: pointer; text-decoration: none;
  transition: background-color .15s cubic-bezier(.4,0,.2,1);
}
.btn:hover { background: var(--card); }
.btn-primary { background: var(--primary); border-color: var(--primary); color: #fff; }
.btn-primary:hover { background: var(--primary-dark); }
.btn-danger { background: var(--danger); border-color: var(--danger); color: #fff; }
.btn-sm { padding: .35rem .7rem; font-size: .85rem; }
button[disabled] { opacity: .5; cursor: not-allowed; }

/* ---- alerts ----------------------------------------------------------- */

.alert { border-radius: var(--radius-sm); padding: .9rem 1rem; margin: 0 0 1rem; font-size: .92rem; }
.alert-error { background: rgba(231,76,60,.12);  border: 1px solid rgba(231,76,60,.4); }
.alert-warn  { background: rgba(253,203,110,.12); border: 1px solid rgba(253,203,110,.4); }
.alert-ok    { background: rgba(0,184,148,.12);   border: 1px solid rgba(0,184,148,.4); }

/* ---- secrets ---------------------------------------------------------- */

.secret { display: flex; align-items: center; gap: .5rem; margin-top: .6rem; flex-wrap: wrap; }
.secret-label { font-size: .8rem; font-weight: 600; color: var(--dim); min-width: 7.5rem; }
.secret input { flex: 0 1 15rem; font-family: var(--mono); font-size: .9rem; }

/* ---- number choices --------------------------------------------------- */

/* 🔴 Scoped to `.choices`. These three rules style a row of *pills* — the
   input is hidden and a `<span>` becomes the control — and unscoped they also
   matched the stacked radio lists that share the `.choice` name, hiding the
   radio and leaving nothing but its text. The setup wizard's "Who may ring
   you" had a Save button and no way to choose anything. */
.choices { display: flex; flex-wrap: wrap; gap: .4rem; }
.choices .choice { display: block; margin: 0; font-weight: 400; }
.choices .choice input { position: absolute; opacity: 0; width: 0; height: 0; }
.choices .choice span {
  display: inline-block; background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: .45rem .7rem; cursor: pointer;
  font-variant-numeric: tabular-nums; font-size: .92rem;
}
.choices .choice input:checked + span {
  border-color: var(--primary); background: rgba(108,92,231,.15);
  box-shadow: inset 0 0 0 1px var(--primary); font-weight: 600;
}
.choices .choice input:focus-visible + span { box-shadow: 0 0 0 3px rgba(108,92,231,.4); }

/* A setup step that cannot be started yet. Dimmed rather than hidden: the
   point is to show what is coming and why it is not available, not to make the
   wizard appear shorter than it is. */
.card.locked { opacity: .55; }
.card.locked h3 { font-weight: 600; }

/* One approved caller, one line.
   
   The name takes the slack and the number is fixed, so the buttons sit in the
   same place down the whole list and it can be read as a column rather than
   parsed row by row. `nowrap` because wrapping is what turned each caller into
   three lines; below 34rem it is allowed to wrap, where there is genuinely no
   room. */
.approved-row {
  display: flex; flex-wrap: nowrap; align-items: center;
  gap: .5rem; margin: 0 0 .35rem;
}
/* Name and number are neighbours; the slack goes between them and the buttons,
   not between the two halves of somebody's identity. The name grew to fill the
   row, which left a name at one end and its number at the other. */
.approved-row .who { flex: 0 1 13rem; min-width: 0; }
.approved-row .num { flex: 0 0 10.5rem; min-width: 0; }
.approved-row .btn { flex: 0 0 auto; margin-top: 0; }
.approved-row > button:first-of-type { margin-left: auto; }

/* A readonly input still draws a box and a border, which made a list whose job
   is to be checked at a glance look like a form waiting to be filled in. */
.approved-row input[readonly] {
  background: transparent; border-color: transparent; padding-left: 0;
  color: var(--text); cursor: default;
}
.approved-row input:not([readonly]) { background: var(--bg); border-color: var(--primary); }

@media (max-width: 34rem) {
  .approved-row { flex-wrap: wrap; }
  .approved-row .num { flex: 1 1 8rem; }
}

.note-actions { display: flex; gap: .5rem; margin: .4rem 0 0; }

/* A heading for a group of choices, matching a label's weight and spacing. */
.field-label { font-weight: 600; font-size: .85rem; margin: 1rem 0 .35rem; }

/* A venue's actions sit on one row above the phones they act on. */
.venue-actions { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin-top: .75rem; }
.venue-actions .inline-form { margin: 0; }

/* Phones belonging to a venue, and the ones belonging to none. */
.venue-phones { list-style: none; margin: .5rem 0 0; padding: 0; }
.venue-phones li {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
  padding: .5rem 0; border-top: 1px solid var(--border);
}
.venue-phones li > a { font-weight: 600; }
.venue-phones .hint { margin: 0; padding: 0; flex: 1 1 auto; }
.venue-phones .inline-form { margin: 0; }
.venue-phones select { width: auto; min-width: 11rem; }

/* Phone cards two across where the width allows, one where it does not. */
.phone-cards {
  display: grid; gap: .75rem;
  grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
}
.phone-cards .card { margin: 0; }

/* Download choices sit in a row and wrap rather than stacking full width. */
.download-row { display: flex; flex-wrap: wrap; gap: .5rem; }

.empty { color: var(--dim); padding: 1.5rem 0; }
a { color: var(--primary); }
.back { display: inline-block; margin-top: 1.5rem; font-size: .9rem; }

@media (prefers-reduced-motion: reduce) { * { transition-duration: .01ms !important; } }

/* Venue picker sits inside a phone card — a control, not a form to fill in. */
.inline-form { margin: .6rem 0 .2rem; }
.inline-form label { display: flex; align-items: center; gap: .5rem; margin: 0; }
.inline-form select { width: auto; min-width: 12rem; }

/* The phone page: two columns on a wide screen. Each setting card is only a
   few lines tall, so one column left the right-hand half of the window empty
   and pushed everything below the fold. Cards marked .wide span both. */
.phone-grid {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(21rem, 1fr));
}
/* Cards in the same row match heights — `align-items: start` left three cards
   of three different heights sitting on a ragged baseline. Stretching them and
   letting the content sit at the top keeps the row square without stretching
   the text with it. */
.phone-grid > .card { margin: 0; display: flex; flex-direction: column; }
.phone-grid > .card > :last-child { margin-bottom: 0; }
.phone-grid > .card .hint:last-child { margin-top: auto; padding-top: .5rem; }
.phone-grid h4 { margin: 0 0 .5rem; font-size: .95rem; }
.phone-grid hr { width: 100%; border: 0; border-top: 1px solid var(--line, #33334d); margin: 1rem 0; }

/* Sits in the action column, under the button it belongs beneath. Opening it
   grows the column leftward, so the choices have room without the closed state
   taking any. */
.phone-row.phone-note .not-mine { justify-self: end; text-align: right; }
.phone-row.phone-note .not-mine .inline-form { justify-content: flex-end; }
.not-mine > summary { list-style: none; display: inline-block; cursor: pointer; }
.not-mine > summary::-webkit-details-marker { display: none; }
.not-mine[open] > summary { margin-bottom: .5rem; }
.not-mine .hint { margin: .3rem 0; }

/* A recovery path that should be findable and never prominent. */
.reset-note { margin-top: .9rem; }
.reset-note > summary { cursor: pointer; font-size: .85rem; color: var(--muted, #9a9ab0); }
.reset-note[open] > summary { margin-bottom: .5rem; }

/* A card that reads as a summary until somebody asks to change it. Built on
   <details> so it works with no JavaScript, and so the browser handles the
   open/closed state itself. */
.reveal > summary { list-style: none; cursor: pointer; display: flex; align-items: flex-start; gap: 1rem; }
.reveal > summary::-webkit-details-marker { display: none; }
.reveal > summary .reveal-toggle { margin: 0 0 0 auto; flex: none; }
.reveal[open] > summary .reveal-toggle::after { content: none; }
.reveal[open] > summary { margin-bottom: 1rem; }
.reveal[open] > summary .facts { opacity: .55; }

dl.facts { margin: 0; display: grid; gap: .45rem; }
dl.facts > div { display: flex; gap: .6rem; align-items: baseline; flex-wrap: wrap; }
dl.facts dt {
  margin: 0; min-width: 6.5rem; font-size: .8rem; text-transform: uppercase;
  letter-spacing: .04em; color: var(--muted, #9a9ab0);
}
dl.facts dd { margin: 0; }
.swatch {
  display: inline-block; width: .85rem; height: .85rem; border-radius: 3px;
  vertical-align: -1px; border: 1px solid rgba(255,255,255,.25);
}

form.stacked label { display: block; }
form.stacked label.choice { display: flex; align-items: center; gap: .5rem; font-weight: 400; }
form.stacked input[type="color"] { width: 4rem; padding: .2rem; }
form.stacked .hint { margin: .35rem 0 1rem; }
form.stacked .btn { margin-top: .3rem; }

/* Preview button sits with the control it previews, not under it. */
.ring-preview { margin-left: .5rem; white-space: nowrap; }
.phone-grid > .card.wide { grid-column: 1 / -1; }
@media (max-width: 700px) { .phone-grid { grid-template-columns: 1fr; } }

/* The sub-account beside a SIP slot: present for the support call, quiet
   enough that nobody mistakes it for the name of the line. */
/* "From <line>" with Change beside it; the picker appears only when asked. */
.from-picker > summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: .5rem; }
.from-picker > summary::-webkit-details-marker { display: none; }
.from-picker[open] > summary { margin-bottom: .5rem; }
.from-picker label { display: flex; align-items: center; gap: .5rem; margin: 0; font-size: .85rem; }
.from-picker select { width: auto; min-width: 12rem; }

/* Which of your numbers a conversation is on. Quiet, but always present —
   the alternative was inferring it from whether a reply box appeared. */
/* 🔴 `vertical-align: middle`, and no top margin.
   
   A top margin on an inline-block pushes the whole box down while the text
   beside it stays put, so the pill sat below the name it belongs to. The margin
   was there for the thread page, where it sits on its own line; that case keeps
   it through the more specific rule below. */
.on-did {
  display: inline-block; font-size: .72rem; letter-spacing: .02em;
  color: var(--muted, #9a9ab0); background: rgba(255,255,255,.06);
  border-radius: 999px; padding: .08rem .5rem; margin: 0;
  vertical-align: middle;
}
.contact-identity .on-did { margin-top: .35rem; }

.line-detail { font-size: .8rem; color: var(--muted, #9a9ab0); }
.line-detail .clash { color: var(--warning, #d89b3c); }
/* The fixed 1, shown as part of the field rather than as instructions. */
.ext-input { display: inline-flex; align-items: stretch; }
.ext-input .ext-prefix {
  display: flex; align-items: center; padding: 0 .55rem;
  border: 1px solid var(--line, #33334d); border-right: 0;
  border-radius: 6px 0 0 6px; background: rgba(255,255,255,.05);
  font-variant-numeric: tabular-nums; color: var(--muted, #9a9ab0);
}
.ext-input input { width: 4rem; border-radius: 0 6px 6px 0; text-align: left; }

.ext-badge {
  display: inline-block; font-variant-numeric: tabular-nums; letter-spacing: .04em;
  background: rgba(108, 92, 231, .18); color: var(--accent, #a99cf5);
  border-radius: 6px; padding: .1rem .45rem;
}
.line-detail code { font-size: .78rem; }

/* Phones list: one row per phone — name and model, status, its numbers, and
   the way in. Grid rather than a table so it can reflow on a narrow screen
   instead of scrolling sideways. */
.phone-list { padding: 0; }
.phone-row {
  display: grid; align-items: center; gap: .35rem 1rem;
  grid-template-columns: minmax(9rem, 1.1fr) auto minmax(8rem, 2fr) auto;
  padding: .8rem 1rem; border-top: 1px solid var(--line, #33334d);
}
.phone-row:first-child { border-top: 0; }
.phone-id strong { display: block; }
.phone-id .muted, .phone-lines .muted { font-size: .82rem; color: var(--muted, #9a9ab0); }
.phone-lines { font-size: .9rem; }
.phone-row .btn { margin: 0; justify-self: end; }
.phone-row.phone-note { grid-template-columns: 1fr auto; padding-top: 0; border-top: 0; }
.phone-row.phone-note .hint { margin: 0; }
.phone-row.pending { border-left: 3px solid var(--warning, #d89b3c); }

@media (max-width: 700px) {
  .phone-row {
    grid-template-columns: 1fr auto;
    grid-template-areas: "id status" "lines lines" "action action";
  }
  .phone-id { grid-area: id; }
  .phone-row .badge { grid-area: status; justify-self: end; }
  .phone-lines { grid-area: lines; }
  .phone-row > .btn { grid-area: action; justify-self: stretch; text-align: center; }
  .phone-row.phone-note { grid-template-columns: 1fr; grid-template-areas: none; }
  .phone-row.phone-note .btn { justify-self: start; }
}

/* One SIP line on the phone page: account, ring, save. Wraps rather than
   scrolls, because this is read on a phone at least as often as at a desk. */
.line-row {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: .6rem 1rem;
  padding: .7rem 0; border-top: 1px solid var(--line, #33334d);
}
.line-row:first-of-type { border-top: 0; }
.line-row label { display: flex; flex-direction: column; gap: .3rem; margin: 0; }
.line-row select { width: auto; min-width: 11rem; }
.line-row .btn { margin: 0; }
/* Save sits at the right-hand edge, away from the controls it commits, so a
   row of them reads as a column of buttons rather than ragged text. */
.line-row .line-save { margin-left: auto; }
.line-row.line-commit { justify-content: flex-end; border-top: 0; padding-top: .2rem; }
.line-row .seconds { display: flex; align-items: center; gap: .25rem; }
.line-row .seconds input { width: 3.4rem; min-width: 0; text-align: right; }
.line-row .hint { flex-basis: 100%; margin: 0; }
.line-row strong { display: block; }
@media (max-width: 560px) {
  .line-row { flex-direction: column; align-items: stretch; }
  .line-row select { width: 100%; min-width: 0; }
  .line-row .line-save { margin-left: 0; }
}

/* Sign-in page: one card, centerd, nothing else to look at. */
.signin { max-width: 24rem; margin: 12vh auto; padding: 0 1rem; }
.signin .wordmark { font-size: 2rem; margin-bottom: .25rem; }
.signin .card { margin-top: 1.25rem; }
.signin label { display: block; margin-bottom: .9rem; }
.signin input { width: 100%; }

/* Identity and sign-out sit opposite the nav. */
.signout { display: flex; align-items: center; gap: .6rem; margin-left: auto; }
.signout span { color: var(--muted, #9a9ab0); font-size: .85rem; }
.topbar-inner { display: flex; align-items: center; gap: 1.5rem; }


/* One caller per row: fields, save, remove. Editing and adding are the same
   shape, so the eye does not have to learn two layouts. */
.row-entry { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin-bottom: .5rem; }
.row-entry .inline-form { margin: 0; }
.row-entry input { width: auto; min-width: 9rem; }
.inline-form { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }

/* A disabled Apply should look unavailable, not merely inert. */
.btn[disabled] { opacity: .45; cursor: not-allowed; }


/* Parked numbers are inventory, so they get a table rather than a card each —
   thirteen cards of held stock buried the one line anybody answers. */
table.parked { width: 100%; border-collapse: collapse; margin-top: .5rem; }
table.parked td { padding: .5rem .6rem; border-top: 1px solid var(--line, #33334d); vertical-align: middle; }
table.parked td:first-child { white-space: nowrap; }
table.parked .inline-form { margin: 0; }
table.parked select { width: auto; min-width: 11rem; }

/* Buttons inside an inline form still need to clear the control beside them
   when the row wraps on a narrow screen. */
.inline-form .btn { margin-top: 0; }


/* Adding a venue, a phone or an extension is occasional, so the panel folds
   away rather than sitting open under the list that already exists. */
details.add-panel summary { cursor: pointer; }
details.add-panel[open] summary { margin-bottom: .5rem; }

/* Screening options read better stacked than in a dropdown: three modes whose
   differences are worth reading, not three interchangeable values. */
.choice { display: block; font-weight: 400; margin: .6rem 0; }
.choice input { width: auto; margin-right: .5rem; }
.choice strong { font-weight: 600; }


/* Fourteen numbers as fourteen cards was a page you scrolled to read. One row
   each, with the destinations side by side, is a page you scan. */
/* Fourteen rows of similar text need help separating. Banding does more than a
   rule does: the eye follows a block of color across a wide row without
   losing which line it is on. */
table.numbers { width: 100%; border-collapse: collapse; }
table.numbers tr:nth-child(odd) { background: rgba(255,255,255,.035); }
table.numbers tr:hover { background: rgba(108,92,231,.14); }
table.numbers td {
  padding: .7rem .7rem;
  border-bottom: 1px solid var(--line, #33334d);
  vertical-align: middle;
}
/* `nowrap` kept Edit and Assign on one line, which is right until the cell also
   holds a select — then the button leaves the row entirely. Wrapping inside the
   cell is the only version that survives a narrow window. */
table.numbers td.actions { text-align: right; }
table.numbers td.actions .inline-form {
  display: inline-flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  max-width: 100%;
  margin-left: .4rem;
}
table.numbers td.actions select { min-width: 7rem; max-width: 100%; width: auto; }
/* Keep Edit, the picker and Assign on one line where they fit; the cell may
   wrap as a whole, but splitting the form from its own button reads as a bug. */
table.numbers td.actions .inline-form { flex-wrap: nowrap; }
table.numbers td.actions { padding-left: 0; }
table.numbers td.actions .btn { white-space: nowrap; }
table.numbers td.num { white-space: nowrap; }
table.numbers td.num .hint { display: block; font-size: .78rem; }
table.numbers .inline-form { margin: 0; }
table.numbers select { width: auto; min-width: 9rem; }

/* A thread reads as a column of turns. Received sits left, sent sits right —
   the shape every messaging app has taught people to read without a legend. */
.thread { margin: 1rem 0; }
.event { max-width: 42rem; margin-bottom: .9rem; padding: .6rem .8rem; border-radius: 8px; background: var(--card, #252540); }
.event-out { margin-left: auto; background: rgba(108,92,231,.22); }
.event-none { background: rgba(253,203,110,.12); }
.event-meta { font-size: .72rem; color: var(--dim, #9a9ab0); margin-bottom: .3rem; }
.event-body { white-space: pre-wrap; }


/* A whole row opens the thread. The buttons inside it must not, so they stop
   the click before it reaches the row. */
table.rows-clickable tr[data-href] { cursor: pointer; }
table.rows-clickable td.actions { cursor: default; }


/* The search bar is a form that must not look like a settings card: controls on
   one line, labels inline, nothing shouting for attention. */
.searchbar label { display: inline-flex; align-items: center; gap: .4rem; margin: 0; font-size: .85rem; }
.searchbar input[type="date"], .searchbar select { width: auto; }
.pill {
  padding: .25rem .6rem; border: 1px solid var(--line, #33334d); border-radius: 999px;
  font-weight: 400; cursor: pointer;
}
.pill input { width: auto; margin: 0; }
.pill:has(input:checked) { background: rgba(108,92,231,.22); border-color: var(--primary, #6c5ce7); }

/* Two doors, one page. The divider is what stops the second form reading as a
   fallback for the first having failed. */
.sso-card { text-align: center; }
.btn-block { display: block; width: 100%; text-align: center; }
.sso-divider {
  display: flex; align-items: center; gap: .75rem;
  margin: 1.25rem 0; color: var(--dim, #9a9ab0); font-size: .82rem;
}
.sso-divider::before, .sso-divider::after {
  content: ""; flex: 1; height: 1px; background: var(--line, #33334d);
}


/* The logo carries its own wordmark, so it replaces the text one where there is
   room to read it — the sign-in page. The top bar keeps the text version,
   because the image is unreadable at nav height. */
.signin-logo { display: block; width: 160px; height: auto; margin: 0 auto .5rem; border-radius: 12px; }


/* A pairing code is read off one screen and typed into another, so it is set
   large and monospaced with the groups kept apart. */
.pairing-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 1.6rem; letter-spacing: .12em; font-weight: 600;
  background: rgba(108,92,231,.14); padding: .6rem 1rem; border-radius: 8px;
  display: inline-block; margin: .3rem 0;
}


/* An incoming call follows you across pages. Fixed to the bottom rather than
   the top: a banner that pushes the page down moves whatever somebody was
   about to click. */
#ringing-bar {
  position: fixed; left: 1rem; right: 1rem; bottom: 1rem; z-index: 50;
  display: flex; align-items: center; gap: 1rem; justify-content: center;
  background: var(--primary, #6c5ce7); color: #fff;
  padding: .8rem 1.2rem; border-radius: 10px;
  box-shadow: 0 6px 24px rgba(0,0,0,.35);
}
#ringing-bar .btn { margin: 0; background: rgba(255,255,255,.18); color: #fff; }


/* A face in a circle. The initial fallback matters more than the photo: most
   contacts will never have one, and a row of empty grey circles is worse than
   no circles at all. */
.avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 50%; overflow: hidden;
  background: var(--primary, #6c5ce7); color: #fff;
  font-size: .78rem; font-weight: 600; vertical-align: middle;
  margin-right: .5rem; flex: 0 0 auto;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar-lg { width: 64px; height: 64px; font-size: 1.5rem; cursor: pointer; margin: 0; }
.contact-head { display: flex; align-items: center; gap: 1rem; margin-bottom: 1rem; }


/* Adding a phone: three short fields, stacked so the labels read as steps. */
.card.danger { border-color: rgba(200,60,60,.45); }
.btn-danger { background: #c0392b; color: #fff; }
.card.pending { opacity: .82; border-style: dashed; }
.waiting { margin: .4rem 0 .6rem; }
.pairing-code { margin: .8rem 0 .4rem; }
#helper-unpaired form, #helper-missing form, #helper-blocked form { margin-bottom: 1rem; }
#add-phone hr { margin: 1.4rem 0; border: 0; border-top: 1px solid rgba(128,128,128,.25); }
.by-mac summary { cursor: pointer; font-weight: 600; font-size: .9rem; }
.attach.seen { border-left: 3px solid var(--accent, #6c5ce7); padding-left: .8rem; margin: .8rem 0 .2rem; }
.btn-quiet { background: none; border: 0; text-decoration: underline; font-size: .85rem; padding: 0; opacity: .7; }
.attach label { display: block; margin: .6rem 0; font-size: .9rem; }
.attach input, .attach select { display: block; width: 100%; max-width: 22rem; margin-top: .2rem; }
.attach + .hint { margin-top: .4rem; }


/* "Add a Person" is a button that happens to open a form. list-style:none stops
   Safari and Chrome drawing their own triangle inside it. */
.add-person > summary { display: inline-block; list-style: none; cursor: pointer; }
.add-person > summary::-webkit-details-marker { display: none; }
.add-person[open] > summary { margin-bottom: .8rem; }
.add-person h4 { margin: 1.1rem 0 .3rem; font-size: .9rem; }
.add-person h4:first-of-type { margin-top: 0; }


/* ---- The bar, on a phone ---------------------------------------------------
   The wordmark became a mark: at 380px a name, eight links and a sign-out
   button cannot share a line, and the name is the part already on the tab. */
.brandmark { display: inline-flex; align-items: center; flex: none; }
.brandmark img { display: block; width: 60px; height: 60px; border-radius: 10px; }
@media (max-width: 760px) { .brandmark img { width: 60px; height: 60px; } }

.hamburger {
  display: none; flex-direction: column; gap: 4px; justify-content: center;
  width: 40px; height: 36px; padding: 0 9px; background: none;
  border: 1px solid var(--line, #33334d); border-radius: var(--radius-sm, 6px);
  cursor: pointer;
}
.hamburger span { display: block; height: 2px; background: currentColor; border-radius: 2px; }

@media (max-width: 760px) {
  .hamburger { display: flex; }
  .topbar-inner { flex-wrap: wrap; gap: .5rem; }
  /* The menu drops below the bar rather than squeezing into it. */
  .topnav { display: none; width: 100%; flex-direction: column; gap: 0; order: 3; }
  .topnav.open { display: flex; }
  .topnav a { padding: .7rem .2rem; border-bottom: 1px solid var(--line, #33334d); }
  /* Sign out stays: it is what somebody looks for when they are in the wrong
     account, which is exactly when a hidden menu is most annoying. */
  .signout { margin-left: auto; }
  .signout span { display: none; }
}


/* ---- Pull to fetch --------------------------------------------------------- */
.pull-hint {
  position: fixed; top: 0; left: 50%; transform: translate(-50%, -100%);
  padding: .5rem 1rem; border-radius: 0 0 8px 8px; font-size: .85rem;
  background: var(--accent, #6c5ce7); color: #fff; z-index: 50;
  transition: transform .15s ease-out;
}
.pull-hint.showing { transform: translate(-50%, 0); }
.pull-hint.ready { background: #00b894; }

/* The fetch explanation, folded until asked for. */
details.quiet > summary { cursor: pointer; font-size: .82rem; opacity: .65; margin-top: .5rem; }
details.filters > summary { cursor: pointer; font-weight: 600; font-size: .92rem; }
details.filters[open] > summary { margin-bottom: .8rem; }

/* ---- The inbox on a phone --------------------------------------------------
   A six-column table cannot be narrowed into 380px; it can only be re-read as
   rows. Each thread becomes two lines — who and when, then what — and the
   columns that were reference material rather than content step out. */
table.numbers.threads, table.numbers.threads tbody { display: block; }

/* 🔴 Cells are addressed by class, never by position.
   These rules were written with nth-child, and adding the selection checkbox
   shifted every one of them by a column: the preview was hidden, two cells
   landed in the same grid area and drew on top of each other, and the row was
   mostly blank. Position is not identity. */
table.numbers.threads tr {
  display: grid;
  grid-template-columns: auto minmax(9rem, auto) minmax(0, 1fr) auto auto auto;
  grid-template-areas: "pick who preview flags when actions";
  align-items: center;
  gap: .2rem .75rem;
  padding: .6rem .3rem;
  border-top: 1px solid var(--line, #33334d);
}
table.numbers.threads td { display: block; border: 0; padding: 0; min-width: 0; }
table.numbers.threads td.pick    { grid-area: pick; display: flex; align-items: center; }
table.numbers.threads td.num     { grid-area: who; display: flex; align-items: center; gap: .5rem; white-space: nowrap; }
table.numbers.threads td.preview {
  grid-area: preview; font-size: .88rem; opacity: .78;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
table.numbers.threads td.when    { grid-area: when; font-size: .78rem; white-space: nowrap; opacity: .7; }
/* Its own column. Sharing "when" with the date meant the SPAM and unread
   badges were painted over the timestamp — grid stacks whatever it is given,
   it does not make room. */
table.numbers.threads td.flags   { grid-area: flags; display: flex; gap: .3rem; }
table.numbers.threads td.actions { grid-area: actions; display: flex; gap: .4rem; }
/* The item count is on the thread's own page; the row is for what was said. */
table.numbers.threads td.items   { display: none; }

table.numbers.threads tr.bulk-head { display: block; padding: .5rem .3rem; border-top: 0; }
table.numbers.threads tr.bulk-head td { display: flex; gap: .75rem; align-items: center; }

@media (max-width: 760px) {
  /* Two lines: who and when, then the message underneath it. Destructive
     buttons step out — at thumb width beside a scrolling list they are how the
     wrong conversation gets deleted. */
  table.numbers.threads tr {
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas: "pick who when" "pick preview flags";
    row-gap: .25rem;
  }
  table.numbers.threads td.actions { display: none; }
  table.numbers.threads td.num .hint { display: none; }
  table.numbers.threads td.num { white-space: normal; }
  table.numbers.threads td.flags { justify-self: end; }
}

.did-picker { display: flex; align-items: center; gap: .6rem; margin: .8rem 0; }
.did-picker label { margin: 0; font-size: .85rem; opacity: .75; }
.did-picker select { width: auto; min-width: 12rem; }

/* ---- A conversation -------------------------------------------------------- */
.contact-head { display: flex; align-items: center; gap: 1rem; margin: 1rem 0 1.2rem; }
.contact-photo { flex: 0 0 20%; max-width: 120px; display: flex; flex-direction: column; align-items: center; gap: .4rem; }
.contact-identity { flex: 1 1 80%; min-width: 0; }
/* The name field and its button on one line: a caller's name is short, and a
   Save button wrapping to its own row wastes more space than the field uses. */
/* Read-only until asked. A profile is looked at far more often than edited, so
   the fields are not the default state of the page. */
.contact-name { margin: 0; font-size: 1.4rem; line-height: 1.2; }
.contact-number { margin: .1rem 0 0; }
.contact-about { margin: .35rem 0 0; font-size: .92rem; }
.contact-edit { flex: none; }
.contact-edit > summary { list-style: none; cursor: pointer; }
.contact-edit > summary::-webkit-details-marker { display: none; }
.contact-edit[open] { flex: 1 1 100%; margin-top: .8rem; }
.contact-edit label { margin-top: .6rem; }

/* Notes are the account talking to itself, so they sit inside the conversation
   rather than alongside what arrived. */
.event.kind-note { margin-left: 2rem; }
.note-edit > summary { cursor: pointer; }
.note-edit[open] > summary { opacity: .5; font-size: .85rem; }
.note-edit textarea { margin: .4rem 0; }
.note-box > summary { cursor: pointer; font-weight: 600; font-size: .95rem; }
.note-box[open] > summary { margin-bottom: .7rem; }

/* The heading and the one control that belongs beside it. */
.page-head { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.page-head h1 { margin: 0; }
.fetch-bar { margin: 0; }

/* The controls that ride up beside a page heading. A row of their own so that
   two of them sit together, and inline-flex on the slot so the whole group is
   one item in the heading's flex row rather than a block that breaks it. */
#head-slot { display: inline-flex; align-items: center; }
.head-actions { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.head-actions form { margin: 0; }


/* ---- Events in a thread ----------------------------------------------------
   A text and a voicemail carry words somebody wrote or said; a call record and
   a note are context around them. Same list, different weight. */
.event { border-left: 3px solid transparent; padding-left: .7rem; }
.event.kind-sms, .event.kind-voicemail {
  border-left-color: var(--accent, #6c5ce7);
  background: rgba(108,92,231,.07);
  border-radius: 0 6px 6px 0;
}
.event.kind-sms .event-body, .event.kind-voicemail .event-body { font-size: 1rem; }
.event.kind-call .event-body, .event.kind-note .event-body { font-size: .9rem; opacity: .78; }
.event.kind-note { border-left-color: #b2bec3; }

/* A message a program sent. Present, and visibly not part of the conversation:
   dimmed and un-accented, so a thread opened with the filter on still reads as
   the two sentences somebody wrote rather than as a machine log. */
.event.event-automated {
  border-left-color: #b2bec3;
  background: rgba(154,154,176,.09);
  opacity: .72;
}
.event.event-automated .event-body { font-size: .9rem; }

.vm-player {
  display: block;
  width: 100%;
  min-width: 16rem;
  max-width: none;
  margin-top: .45rem;
  height: 36px;
}


/* ---- Bulk selection --------------------------------------------------------
   Layout for these rows lives with the rest of the thread-row rules above.
   A second copy down here quietly overrode them, which is how the row came
   apart: it still named a `what` area that no cell claims. */
.bulk-actions { display: flex; gap: .5rem; align-items: center; margin: .8rem 0; font-size: .9rem; }
/* The copy inside the table's header row sits on the same line as Select all,
   rather than starting a block of its own under it. */
.bulk-head .bulk-actions { display: inline-flex; margin: 0 0 0 1rem; vertical-align: middle; }
.bulk-head .bulk-actions[hidden] { display: none; }

/* ---- Pager -----------------------------------------------------------------
   Count on the left, steps in the middle, page size on the right. Wraps to a
   stack on a phone rather than squeezing three groups onto one line. */
.pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .6rem 1.2rem;
  margin: 1rem 0;
  font-size: .9rem;
}
.pager-steps, .pager-sizes { display: inline-flex; align-items: center; gap: .5rem; }
.pager-sizes a { text-decoration: none; }
/* A step that cannot be taken is still drawn, so the row does not reflow as
   you move through it, but it is plainly not a button. */
.pager .btn[aria-disabled="true"] { opacity: .4; pointer-events: none; }

.reply-box textarea { min-height: 4.5rem; }
.reply-foot { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; margin-top: .5rem; }
.reply-foot .btn { margin-left: auto; }
.from-picker { display: flex; align-items: center; gap: .4rem; margin: 0; font-size: .85rem; font-weight: 400; }
.from-picker select { width: auto; min-width: 9rem; }


/* Room to breathe around the one control somebody came here to use. */
.reply-box { margin: 1.5rem 0; }

/* "+ Note" reads as a button because it does what a button does. The event body
   takes the room; the control sits beside it. */
.event { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: .25rem .75rem; }
.event .event-meta, .event .event-body, .event .attached-note { grid-column: 1; }
/* The player spans both tracks: a scrubber squeezed into the text column is
   the width of its own play button, which is what happened when the note
   button arrived. */
.event .vm-player { grid-column: 1 / -1; }
.event .add-attached { grid-column: 2; grid-row: 2; align-self: start; margin: 0; }
.event .add-attached[open] { grid-column: 1 / -1; grid-row: auto; }
.note-button {
  display: inline-block; list-style: none; cursor: pointer;
  padding: .15rem .5rem; border: 1px solid var(--line, #33334d); border-radius: 999px;
  font-size: .75rem; opacity: .6; white-space: nowrap;
}
.note-button::-webkit-details-marker { display: none; }
.add-attached:hover .note-button, .add-attached[open] .note-button { opacity: 1; }

/* `.btn-sm` zeroes the top margin, which is right inside a row of controls and
   wrong for a button sitting under a paragraph of explanation. */
.toggle-form { margin-top: 1rem; }

/* A caller who was turned away, not talked to. */
.card.screened { margin-top: 1.5rem; border-color: rgba(253,203,110,.5); background: rgba(253,203,110,.07); }

/* An expandable group of settings inside a card. A <details> rather than a
   scripted reveal, so the fields inside it are real form fields whether or not
   it has been opened — browsers submit a collapsed one. */
.disclosure {
  margin-top: 1rem;
  border-top: 1px solid var(--border);
  padding-top: .75rem;
}
.disclosure > summary {
  cursor: pointer;
  font-size: .9rem;
  /* --dim, not --muted: this palette has no --muted, and the one other rule
     that reached for it carries a hardcoded fallback rather than a color. */
  color: var(--dim);
  list-style: none;
}
.disclosure > summary::-webkit-details-marker { display: none; }
.disclosure > summary::after { content: ' ▾'; opacity: .6; }
.disclosure[open] > summary { margin-bottom: .75rem; color: var(--text); }
.disclosure[open] > summary::after { content: ' ▴'; }

/* A grid of things to tick — numbers to copy a list onto, and anything else
   where the choice is a short label rather than a sentence. Two or three
   columns by width, one on a phone. */
.pick-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: .5rem;
  margin: .75rem 0 1rem;
}
.pick {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .55rem .7rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  cursor: pointer;
}
.pick:hover { border-color: var(--primary); }
/* The label is the hit area, so the box itself needs no growing or shrinking. */
.pick input { flex: none; margin: 0; }
.pick > span { display: flex; flex-direction: column; line-height: 1.3; min-width: 0; }
.pick .hint { font-size: .78rem; }
/* A <details> that is also a card keeps the card's padding rather than adding
   the disclosure's own top border to it. */
details.disclosure.card { border-top: 1px solid var(--border); padding-top: 1rem; }

/* A run happening one step at a time. Not a progress bar: the steps are named
   and unequal, and which one failed is the useful information. */
.steps { list-style: none; margin: .5rem 0 1.5rem; padding: 0; }
.steps li { padding: .45rem 0; border-top: 1px solid var(--border); font-size: .92rem; }
.steps li:first-child { border-top: none; }
.step-ok { color: var(--text); }
.step-failed { color: var(--danger); font-weight: 600; }
.step-skipped { color: var(--dim); }

/* A readiness list: each line is a claim about the customer's carrier account,
   and the marker says whether we checked it or merely asked. */
.steps > li > strong { display: block; }
.steps > li > .hint { display: block; margin-top: .2rem; }
