/* Vivium.io — small custom CSS layer on top of Tailwind.
   Used for effects Tailwind doesn't ship out of the box: radial
   background gradients on the splash, wordmark text-clip, etc. */

/* Splash backdrop — radial purple/cyan/pink glows + a soft grid
   overlay masked from the edges. Tailwind handles solid bg colours
   well but layering radial gradients gets verbose; cleaner here. */
.phx-splash-bg {
  background:
    radial-gradient(circle at 50% 18%, rgba(168, 85, 247, 0.22) 0%, transparent 55%),
    radial-gradient(circle at 80% 80%, rgba(34, 211, 238, 0.16) 0%, transparent 60%),
    radial-gradient(circle at 12% 78%, rgba(236, 72, 153, 0.12) 0%, transparent 55%),
    #03030a;
  position: relative;
  overflow: hidden;
}
.phx-splash-bg::before {
  content: "";
  position: absolute;
  inset: -2px;
  background-image:
    linear-gradient(rgba(168, 85, 247, 0.04) 1px, transparent 1px),
    linear-gradient(90deg, rgba(168, 85, 247, 0.04) 1px, transparent 1px);
  background-size: 40px 40px;
  pointer-events: none;
  mask-image: radial-gradient(ellipse at center, black 0%, transparent 75%);
  -webkit-mask-image: radial-gradient(ellipse at center, black 0%, transparent 75%);
}

/* Wordmark — gradient text + soft glow. */
.phx-wordmark {
  background: linear-gradient(135deg, #ffffff 0%, #c084fc 55%, #22d3ee 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  text-shadow: 0 0 32px rgba(168, 85, 247, 0.32);
}

/* Shield drop-shadow + float. */
.phx-symbol {
  filter:
    drop-shadow(0 0 32px rgba(168, 85, 247, 0.42))
    drop-shadow(0 0 60px rgba(168, 85, 247, 0.18));
}

/* ============================================================
   Vivium — The Room
   ============================================================
   Dieter's contract: every voice's identity is carried by ONE token
   (--accent) set on the row element at render time. Anything inside
   that needs the colour reads var(--accent). One binding, no drift.
   ============================================================ */

/* Universal focus ring — Jakob non-negotiable #1.
   Any element bearing data-viv-focus or any [tabindex] inside a
   Vivium screen gets a visible 2px ring on keyboard focus. */
.viv [tabindex]:focus-visible,
.viv a:focus-visible,
.viv button:focus-visible,
.viv [role="tab"]:focus-visible,
.viv input:focus-visible,
.viv textarea:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
  border-radius: 4px;
}
/* Who's Here — voice cards grid.
   Leader card spans full width; others sit in a 2-column grid. */
.viv-vc-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  padding: 4px 12px 20px;
}
.viv-vc {
  background: var(--viv-surface);
  border: 1px solid var(--viv-border);
  border-top: 2px solid var(--accent, var(--viv-border));
  border-radius: 12px;
  padding: 14px 10px 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  position: relative;
  transition: border-color 0.15s;
}
.viv-vc:hover { border-color: var(--viv-border-hov); }
.viv-vc--leader { grid-column: 1 / -1; }
.viv-vc__avatar-wrap {
  position: relative;
  flex-shrink: 0;
  margin-bottom: 2px;
}
.viv-vc__leader-badge {
  position: absolute;
  bottom: -3px; right: -3px;
  background: var(--viv-body);
  color: var(--viv-surface);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 2px 6px;
  border-radius: 999px;
  line-height: 1.4;
  white-space: nowrap;
}
.viv-vc__name {
  font-size: 13px;
  font-weight: 600;
  color: var(--viv-body);
  text-align: center;
  line-height: 1.3;
  margin: 0;
  word-break: break-word;
}
.viv-vc__role {
  font-size: 11px;
  color: var(--viv-muted);
  text-align: center;
  line-height: 1.25;
  margin: 0;
}
.viv-vc__actions {
  display: flex;
  gap: 5px;
  margin-top: 8px;
}
.viv-vc__btn {
  width: 28px; height: 28px;
  border-radius: 50%;
  border: 1px solid var(--viv-border);
  background: transparent;
  color: var(--viv-dim);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 0.1s, background-color 0.1s, border-color 0.1s;
  flex-shrink: 0;
}
.viv-vc__btn:hover {
  color: var(--viv-body);
  border-color: var(--viv-border-hov);
  background: rgba(var(--viv-body-rgb), 0.05);
}
.viv-vc__btn:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}
.viv-vc__btn--del:hover {
  border-color: rgba(224, 63, 63, 0.4);
  color: #e04040;
  background: rgba(224, 63, 63, 0.06);
}

/* "+" add-voice button in Who's Here header */
.viv-vc-add {
  width: 26px; height: 26px;
  border-radius: 50%;
  border: 1.5px solid var(--viv-border);
  background: transparent;
  color: var(--viv-muted);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  line-height: 1;
  transition: color 0.12s, border-color 0.12s;
  flex-shrink: 0;
}
.viv-vc-add:hover {
  color: var(--viv-body);
  border-color: var(--viv-border-hov);
}
.viv-vc-add:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}

/* Voice avatar tile - shared across rail, Foyer, Assembly card, and
   the message bubble renderer. Colour ring pulled from --accent so
   identity binding stays single-source. */
.viv-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  flex-shrink: 0;
  border: 1.5px solid var(--accent, transparent);
  overflow: hidden;
  background: rgba(var(--viv-body-rgb), 0.04);
}
.viv-avatar__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 999px;
}
.viv-avatar__emoji {
  font-size: 0.95em;
  line-height: 1;
}

/* Edit affordance per voice row. Stays muted until hover or focus. */
.viv-voice__edit {
  appearance: none;
  background: transparent;
  border: 0;
  color: var(--viv-dim);
  padding: 6px;
  border-radius: 6px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 0.1s, background-color 0.1s;
}
.viv-voice__edit:hover { color: var(--viv-body); background: rgba(var(--viv-body-rgb), 0.06); }
.viv-voice__edit:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}

/* 1-on-1 entry per voice row. Same quiet shape as the edit
   affordance so the rail stays a single visual block. */
.viv-voice__direct-form { display: inline-flex; margin: 0; }
.viv-voice__direct {
  appearance: none;
  background: transparent;
  border: 0;
  color: var(--viv-dim);
  padding: 6px;
  border-radius: 6px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 0.1s, background-color 0.1s;
}
.viv-voice__direct:hover { color: var(--viv-body); background: rgba(var(--viv-body-rgb), 0.06); }
.viv-voice__direct:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}
.viv-voice__rule {
  width: 2px;
  align-self: stretch;
  background: var(--accent, var(--viv-muted));
  border-radius: 1px;
  /* v1 IDENTITY: always full opacity.
     v2 STATE: idle drops to 0.45; .viv-voice--thinking takes it
     back to 1.0 and adds a 2s pulse. */
  opacity: 1;
}
.viv-voice__body {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 2px;
}
.viv-voice__head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.viv-voice__name {
  font-size: 15px;
  font-weight: 500;
  color: var(--viv-body);
  line-height: 1.25;
  word-break: break-word;
}
.viv-voice__role {
  font-size: 13px;
  font-weight: 400;
  color: var(--viv-muted);
  line-height: 1.25;
  letter-spacing: 0.01em;
}

/* Past Rooms — one item.
   The active room (loaded into transcript) gets aria-current="page";
   Addy's helper sets that on htmx:afterSwap. CSS keys off it so the
   visual state can never drift from the swap state. */
.viv-past-room {
  display: block;
  padding: 10px 14px;
  border-radius: 8px;
  text-decoration: none;
  color: inherit;
  border: 1px solid transparent;
  transition: background-color 0.12s, border-color 0.12s;
}
.viv-past-room:hover {
  background-color: var(--viv-surface);
  border-color: var(--viv-border);
}
.viv-past-room[aria-current="page"] {
  background-color: var(--viv-surface);
  border-color: var(--viv-border-hov);
}
.viv-past-room__title {
  font-size: 15px;
  font-weight: 400;
  color: var(--viv-body);
  line-height: 1.3;
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.viv-past-room__stamp {
  font-size: 13px;
  color: var(--viv-muted);
  line-height: 1.3;
  margin-top: 2px;
}

/* ============================================================
   Team card (Foyer doors, team variant). Quiet container, dense
   row of metadata up top, name + subtitle in the middle, avatar
   stack pinned to the bottom. Identity stays on a small dot per
   the minimalist-chrome rule — no role-coloured backgrounds.
   ============================================================ */
.viv-door {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px 16px 14px;
  background: var(--viv-surface);
  border: 1px solid var(--viv-border);
  border-radius: 14px;
  color: inherit;
  text-decoration: none;
  min-height: 148px;
  transition: border-color 0.14s ease, transform 0.14s ease,
              box-shadow 0.14s ease;
}
.viv-door:hover {
  border-color: var(--viv-border-hov);
  transform: translateY(-1px);
  box-shadow: 0 8px 24px rgba(var(--viv-body-rgb), 0.04);
}
.viv-door:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}
.viv-door__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-height: 18px;
}
.viv-door__mode {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--viv-muted);
  text-transform: capitalize;
}
.viv-door__mode-dot {
  width: 6px; height: 6px;
  border-radius: 999px;
  background: var(--viv-dim);
}
.viv-door__mode--consultancy .viv-door__mode-dot {
  background: #4F6BFF;
}
.viv-door__time {
  font-size: 11px;
  color: var(--viv-dim);
  font-variant-numeric: tabular-nums;
}
.viv-door__title {
  font-size: 16px;
  font-weight: 500;
  color: var(--viv-body);
  line-height: 1.25;
  letter-spacing: -0.005em;
  margin: 2px 0 0;
}
.viv-door__subtitle {
  font-size: 12.5px;
  color: var(--viv-muted);
  line-height: 1.35;
  margin: 0 0 4px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.viv-door__voices {
  margin-top: auto;
}

/* ============================================================
   Avatar stack — used in Continue, team doors, future surfaces.
   Negative margin between siblings creates the overlap; each
   tile carries a 1.5px ring in the container's surface colour to
   pop off the stack.
   ============================================================ */
.viv-stack {
  display: inline-flex;
  align-items: center;
}
.viv-stack > .viv-avatar {
  box-shadow: 0 0 0 1.5px var(--viv-surface);
  background: var(--viv-surface);
}
.viv-stack--md > .viv-avatar { margin-right: -7px; }
.viv-stack--lg > .viv-avatar { margin-right: -8px; }
.viv-stack > .viv-avatar:last-child { margin-right: 0; }
.viv-stack__more {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px; height: 26px;
  border-radius: 999px;
  background: var(--viv-canvas);
  color: var(--viv-muted);
  font-size: 11px;
  font-weight: 500;
  box-shadow: 0 0 0 1.5px var(--viv-surface);
  margin-left: 0;
}

/* ============================================================
   Voice edit dialog
   ============================================================ */
.viv-dialog {
  background: var(--viv-surface);
  border: 1px solid var(--viv-border-hov);
  border-radius: 14px;
  color: var(--viv-body);
  padding: 0;
  width: 100%;
  max-width: 520px;
  max-height: 88vh;
  overflow: hidden;
}
.viv-dialog::backdrop {
  background: rgba(var(--viv-canvas-rgb), 0.72);
  backdrop-filter: blur(2px);
}
.viv-voice-edit {
  display: flex;
  flex-direction: column;
  max-height: 88vh;
  width: 100%;
}
.viv-voice-edit__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--viv-border);
  flex-shrink: 0;
}
.viv-voice-edit__title {
  font-size: 17px;
  font-weight: 500;
  color: var(--viv-body);
  margin: 0;
}
.viv-voice-edit__close {
  appearance: none;
  background: transparent;
  border: 0;
  color: var(--viv-muted);
  font-size: 22px;
  line-height: 1;
  width: 32px;
  height: 32px;
  border-radius: 999px;
  cursor: pointer;
}
.viv-voice-edit__close:hover {
  color: var(--viv-body);
  background: rgba(var(--viv-body-rgb), 0.06);
}
.viv-voice-edit__body {
  padding: 16px 20px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  overflow-y: auto;
  flex: 1;
}
.viv-voice-edit__warning {
  margin: 0;
  padding: 10px 12px;
  border-radius: 8px;
  background: rgba(229, 163, 91, 0.10);
  border: 1px solid rgba(229, 163, 91, 0.45);
  color: var(--viv-body);
  font-size: 13px;
  line-height: 1.45;
}
.viv-voice-edit__foot {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  padding: 14px 20px;
  border-top: 1px solid var(--viv-border);
  flex-shrink: 0;
}
.viv-voice-edit__avatar-row {
  display: flex;
  align-items: center;
  gap: 14px;
}
.viv-voice-edit__avatar-current {
  width: 56px;
  height: 56px;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  object-fit: cover;
  border: 1.5px solid var(--accent, var(--viv-border-hov));
  background: rgba(var(--viv-body-rgb), 0.06);
  font-size: 22px;
  flex-shrink: 0;
}
img.viv-voice-edit__avatar-current { background: transparent; }
.viv-voice-edit__avatar-actions {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1;
  min-width: 0;
}
.viv-voice-edit__avatar-clear {
  font-size: 13px;
  color: var(--viv-muted);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.viv-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.viv-field__label {
  font-size: 13px;
  font-weight: 500;
  color: var(--viv-body);
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.viv-field__hint {
  font-size: 12px;
  font-weight: 400;
  color: var(--viv-dim);
}
.viv-field__input {
  width: 100%;
  background: var(--viv-canvas);
  border: 1px solid var(--viv-border);
  border-radius: 8px;
  color: var(--viv-body);
  font-size: 14px;
  padding: 9px 12px;
  font-family: inherit;
}
.viv-field__input:focus {
  outline: 2px solid var(--viv-body);
  outline-offset: 1px;
  border-color: var(--viv-body);
}
.viv-field__textarea {
  resize: vertical;
  min-height: 96px;
  line-height: 1.4;
}

.viv-persona-voice {
  border: 0;
  padding: 0;
  margin: 0;
}
.viv-persona-voice__row {
  display: flex;
  gap: 8px;
}
.viv-persona-voice__pill {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px 12px;
  border: 1px solid var(--viv-border);
  border-radius: 8px;
  background: var(--viv-canvas);
  color: var(--viv-muted);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  user-select: none;
  transition: border-color 0.12s, color 0.12s, background-color 0.12s;
}
.viv-persona-voice__pill:hover {
  border-color: var(--viv-border-hov);
  color: var(--viv-body);
}
.viv-persona-voice__pill input[type="radio"] {
  appearance: none;
  -webkit-appearance: none;
  width: 14px; height: 14px;
  border: 1.5px solid var(--viv-muted);
  border-radius: 999px;
  margin: 0;
  position: relative;
  flex-shrink: 0;
}
.viv-persona-voice__pill input[type="radio"]:checked {
  border-color: var(--viv-body);
}
.viv-persona-voice__pill input[type="radio"]:checked::after {
  content: "";
  position: absolute;
  inset: 2.5px;
  border-radius: 999px;
  background: var(--viv-body);
}
.viv-persona-voice__pill:has(input[type="radio"]:checked) {
  border-color: var(--viv-body);
  color: var(--viv-body);
  background: rgba(var(--viv-body-rgb), 0.04);
}
.viv-persona-voice__pill input[type="radio"]:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}

/* Reusable CTA (also used in Assembly) - ghost variant for Cancel. */
.viv-cta--ghost {
  background: transparent;
  color: var(--viv-body);
  border: 1px solid var(--viv-border-hov);
}
.viv-cta--ghost:hover {
  background: rgba(var(--viv-body-rgb), 0.06);
}

/* ============================================================
   Composer mode chip - replaces the Claude Remote "Code" chip.
   ============================================================ */
/* The Tailwind forms plugin (loaded via CDN) defaults select to
   background-color:#fff. Override explicitly so the chip sits flush
   against the composer's dark surface. !important is intentional - it
   beats the forms plugin in the cascade without churning specificity. */
.viv-mode-chip {
  appearance: none;
  -webkit-appearance: none;
  background-color: transparent !important;
  /* Chevron drawn via mask so it inherits currentColor — that way it
     matches the "Philosophy" text in both light and dark themes
     instead of being a hardcoded grey that drifts. */
  background-image: none;
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 8px 6px;
  color: var(--viv-muted);
  border: 0;
  border-radius: 999px;
  height: 32px;
  padding: 0 24px 0 12px;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  letter-spacing: 0.01em;
  flex-shrink: 0;
  transition: color 0.1s, background-color 0.1s;
}
.viv-mode-chip-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  color: var(--viv-muted);
  transition: color 0.1s;
}
.viv-mode-chip-wrap:hover { color: var(--viv-body); }
.viv-mode-chip-wrap::after {
  content: '';
  position: absolute;
  right: 10px;
  top: 50%;
  width: 8px;
  height: 5px;
  transform: translateY(-50%);
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'><path d='M6 8L0 0h12z'/></svg>") no-repeat center / contain;
          mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'><path d='M6 8L0 0h12z'/></svg>") no-repeat center / contain;
  pointer-events: none;
}
.viv-mode-chip:hover {
  color: var(--viv-body);
  background-color: rgba(var(--viv-body-rgb), 0.06) !important;
}
.viv-mode-chip:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}
.viv-mode-chip option {
  background: var(--viv-canvas);
  color: var(--viv-body);
  font-size: 14px;
}

/* Raw code-block surface inside voice bubbles - used by the Leader's
   prompt-output rule (Steve Jobs spec). The button copies the bare
   code text to the clipboard; copy state announced in place. */
.viv-code {
  position: relative;
  background: var(--viv-code-bg);
  border: 1px solid var(--viv-border);
  border-radius: 8px;
  padding: 36px 14px 14px;
  margin: 12px 0;
  overflow-x: auto;
}
.viv-code pre {
  margin: 0;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 13px;
  line-height: 1.5;
  color: var(--viv-body);
  white-space: pre-wrap;
  word-break: break-word;
}
.viv-code pre code { color: inherit; }
.viv-copy {
  position: absolute;
  top: 8px;
  right: 8px;
  appearance: none;
  background: transparent;
  border: 1px solid var(--viv-border-hov);
  color: var(--viv-muted);
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 11px;
  letter-spacing: 0.02em;
  cursor: pointer;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  transition: background-color 0.12s, color 0.12s, border-color 0.12s;
}
.viv-copy:hover {
  background: rgba(var(--viv-body-rgb), 0.06);
  color: var(--viv-body);
  border-color: var(--viv-border-hov);
}
.viv-copy:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}

/* ---- Prompt Artifact container (strict Copy-button spec) ----
   Used when the Leader emits a fenced prompt. The container holds
   the raw prompt in a monospace pre; the Copy button sits in its
   own 32px footer row with a 6px gap above the final content line.
   The footer reserves the 32px height even when the button is
   absent — that absence is the "stream incomplete" guarantee. The
   button fades in at 120ms; no layout shift, no transient affordance. */
.viv-prompt-artifact {
  background: var(--viv-code-bg);
  border: 1px solid var(--viv-border-hov);
  border-radius: 8px;
  padding: 16px;
  margin: 12px 0;
  overflow-x: auto;
}
.viv-prompt-artifact__body {
  margin: 0;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 13px;
  line-height: 1.5;
  color: var(--viv-body);
  white-space: pre-wrap;
  word-break: break-word;
}
.viv-prompt-artifact__body code { color: inherit; }
.viv-prompt-artifact__footer {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  min-height: 32px;
  margin-top: 6px;
}
.viv-prompt-copy {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 32px;
  padding: 0 12px;
  border-radius: 6px;
  border: 0;
  background: transparent;
  color: var(--viv-muted);
  font-family: 'Inter', ui-sans-serif, system-ui, sans-serif;
  font-size: 13px;
  font-weight: 500;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
  animation: vivPromptCopyFade 120ms ease-out forwards;
}
@keyframes vivPromptCopyFade { to { opacity: 1; } }
.viv-prompt-copy:hover {
  background: rgba(var(--viv-body-rgb), 0.04);
}
.viv-prompt-copy:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}
.viv-prompt-copy__icon  { flex-shrink: 0; }
.viv-prompt-copy__label { white-space: nowrap; }

/* ============================================================
   Admin form fields — override the dark `bg-[rgba(40,32,64,…)]`
   utility every admin template inlines. Inputs become white with
   dark text so typing is legible against the dark admin chrome.
   Scoped under `.admin-shell` which lives on the admin _base
   wrapper, so the user-facing room/composer styling is
   untouched. `!important` because the inline Tailwind arbitrary
   value otherwise wins on cascade order.
   ============================================================ */
.admin-shell input[type="text"],
.admin-shell input[type="email"],
.admin-shell input[type="number"],
.admin-shell input[type="password"],
.admin-shell input[type="search"],
.admin-shell input[type="url"],
.admin-shell input[type="tel"],
.admin-shell input[type="date"],
.admin-shell input:not([type]),
.admin-shell select,
.admin-shell textarea {
  background-color: #FFFFFF !important;
  color: #131318 !important;
  border-color: #D1D1D6 !important;
}
.admin-shell input::placeholder,
.admin-shell textarea::placeholder {
  color: #9B9BA2 !important;
  opacity: 1;
}
.admin-shell input:focus,
.admin-shell select:focus,
.admin-shell textarea:focus {
  border-color: var(--phx-cyan) !important;
  box-shadow: 0 0 0 2px rgba(var(--phx-cyan-rgb), 0.30) !important;
  outline: 0 !important;
}
/* Chrome / Safari autofill leaves a tinted overlay; pin it to
   white so a saved password doesn't repaint the input dark. */
.admin-shell input:-webkit-autofill,
.admin-shell input:-webkit-autofill:hover,
.admin-shell input:-webkit-autofill:focus,
.admin-shell textarea:-webkit-autofill {
  -webkit-box-shadow: 0 0 0 1000px #FFFFFF inset !important;
  -webkit-text-fill-color: #131318 !important;
  caret-color: #131318;
}

/* Two icons in the same slot; CSS swaps which is visible based
   on copy state. No layout shift — both occupy 16x16. */
.viv-prompt-copy__icon--check { display: none; }
.viv-prompt-copy.is-copied .viv-prompt-copy__icon--copy  { display: none; }
.viv-prompt-copy.is-copied .viv-prompt-copy__icon--check { display: inline-block; }
.viv-prompt-copy.is-copied {
  color: var(--viv-body);
  background: rgba(var(--viv-body-rgb), 0.06);
}
.viv-prompt-copy.is-copied .viv-prompt-copy__label { font-weight: 500; }
.viv-prompt-copy.is-failed {
  color: #C04848;
  background: rgba(192, 72, 72, 0.08);
}
@media (prefers-reduced-motion: no-preference) {
  .viv-prompt-copy.is-copied .viv-prompt-copy__icon--check {
    animation: vivCopyCheckPop 220ms ease-out;
  }
  @keyframes vivCopyCheckPop {
    from { transform: scale(0.6); opacity: 0; }
    to   { transform: scale(1);   opacity: 1; }
  }
}

/* ============================================================
   Markdown body — AI message bubbles only.
   The .viv-md wrapper is added by the server on voice-bubble and
   final-answer bodies; marked.js renders the raw markdown inside.
   Element styles are intentionally restrained so a long voice reply
   still reads as conversation, not as a formatted document.
   ============================================================ */
.viv-md > :first-child { margin-top: 0; }
.viv-md > :last-child  { margin-bottom: 0; }

.viv-md p {
  margin: 0 0 0.6em 0;
}
.viv-md p:last-child { margin-bottom: 0; }

/* Headers are demoted: even an ``h1`` in a chat bubble should read as
   "section label", not "page title". Sizes shrink with depth but the
   delta from body text stays small so headings don't bulldoze the
   reply rhythm. */
.viv-md h1, .viv-md h2, .viv-md h3,
.viv-md h4, .viv-md h5, .viv-md h6 {
  margin: 1.1em 0 0.4em;
  font-weight: 600;
  line-height: 1.3;
  color: var(--viv-body);
}
.viv-md h1 { font-size: 1.18em; }
.viv-md h2 { font-size: 1.10em; }
.viv-md h3 { font-size: 1.04em; }
.viv-md h4, .viv-md h5, .viv-md h6 { font-size: 1em; }

.viv-md ul, .viv-md ol {
  margin: 0.2em 0 0.8em;
  padding-left: 1.4em;
}
.viv-md ul { list-style: disc; }
.viv-md ol { list-style: decimal; }
.viv-md li { margin: 0.15em 0; }
.viv-md li > ul, .viv-md li > ol { margin: 0.15em 0; }

.viv-md blockquote {
  margin: 0.5em 0;
  padding: 0.1em 0 0.1em 0.9em;
  border-left: 3px solid var(--viv-border-hov);
  color: var(--viv-muted);
}

.viv-md a {
  color: var(--viv-body);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: rgba(var(--viv-body-rgb), 0.35);
}
.viv-md a:hover {
  text-decoration-color: var(--viv-body);
}

/* Inline code lives inside paragraphs and list items. The viv-code
   block (fenced code) keeps its own surface — see .viv-code above. */
.viv-md :not(pre) > code {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.92em;
  padding: 0.08em 0.36em;
  border-radius: 4px;
  background: var(--viv-code-bg);
  color: var(--viv-body);
  border: 1px solid var(--viv-border);
}

.viv-md strong { font-weight: 600; color: var(--viv-body); }
.viv-md em     { font-style: italic; }

.viv-md hr {
  margin: 1em 0;
  border: 0;
  border-top: 1px solid var(--viv-border);
}

.viv-md table {
  margin: 0.6em 0;
  border-collapse: collapse;
  font-size: 0.94em;
}
.viv-md th, .viv-md td {
  border: 1px solid var(--viv-border);
  padding: 0.35em 0.6em;
  text-align: left;
}
.viv-md th { background: rgba(var(--viv-body-rgb), 0.04); font-weight: 600; }

/* Visually-hidden live region (mounted once in base.html for the
   strict Copy-button announcement). */
.viv-sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Scroll-to-bottom button - floats above the composer wrapper,
   appears only when the user is not near the latest message.
   Light-mode tuning: needs a confident shadow to read as a lifted
   control on a near-white canvas; a feather-light shadow makes it
   disappear into the background. */
.viv-scroll-to-bottom {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translate(-50%, -10px);
  width: 38px;
  height: 38px;
  border-radius: 999px;
  background: var(--viv-surface);
  border: 1px solid var(--viv-border-hov);
  color: var(--viv-body);
  display: none;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 5;
  box-shadow:
    0 2px 4px rgba(var(--viv-body-rgb), 0.10),
    0 10px 24px rgba(var(--viv-body-rgb), 0.14);
  transition: transform 0.12s ease, box-shadow 0.12s ease,
              background-color 0.12s ease;
}
.viv-scroll-to-bottom.is-visible { display: inline-flex; }
.viv-scroll-to-bottom:hover {
  background: var(--viv-canvas);
  border-color: var(--viv-border-hov);
  transform: translate(-50%, -12px);
  box-shadow:
    0 3px 6px rgba(var(--viv-body-rgb), 0.12),
    0 14px 30px rgba(var(--viv-body-rgb), 0.18);
}
.viv-scroll-to-bottom:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}

/* ============================================================
   Private "Look up" surface — full-screen overlay sheet.

   Edge-to-edge: takes the whole viewport, no backdrop, no
   floating-card chrome. The sheet IS the screen while open. The
   room stays mounted underneath so the composer's draft and the
   SSE stream survive, but visually the lookup is the only thing
   you can interact with. Close paths: × button, ESC, or the
   Insert / Copy actions on a result.

   Body content caps at a comfortable reading column so a wide
   desktop doesn't stretch the result line-length to nonsense.
   ============================================================ */
.viv-lookup-backdrop[hidden],
.viv-lookup-panel[hidden] { display: none; }

/* Backdrop is intentionally invisible: with a full-screen sheet
   there's no "outside" to dim. Kept in the markup so the open/close
   JS doesn't have to special-case its presence. */
.viv-lookup-backdrop { display: none; }

.viv-lookup-panel {
  position: fixed;
  inset: 0;
  z-index: 41;
  background: var(--viv-surface);
  display: flex;
  flex-direction: column;
  animation: vivLookupFadeIn 140ms ease-out;
}
@keyframes vivLookupFadeIn {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

.viv-lookup-panel__head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 18px 20px 14px;
  border-bottom: 1px solid var(--viv-border);
  max-width: 720px;
  width: 100%;
  margin: 0 auto;
}
.viv-lookup-panel__title {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  color: var(--viv-body);
  letter-spacing: 0.01em;
}
.viv-lookup-panel__privacy {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-weight: 500;
  color: var(--viv-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.viv-lookup-panel__privacy-dot {
  width: 6px; height: 6px;
  border-radius: 999px;
  background: var(--viv-strategist);  /* a quiet green for "private/safe" */
}
.viv-lookup-panel__close {
  width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px;
  background: transparent;
  color: var(--viv-muted);
  border: 0;
  padding: 0;
  cursor: pointer;
  transition: background-color 0.15s, color 0.15s;
  -webkit-tap-highlight-color: transparent;
  margin-left: 4px;
}
.viv-lookup-panel__close:hover {
  background: rgba(var(--viv-body-rgb), 0.06);
  color: var(--viv-body);
}
.viv-lookup-panel__close:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}

.viv-lookup-panel__search {
  padding: 14px 20px;
  border-bottom: 1px solid var(--viv-border);
  max-width: 720px;
  width: 100%;
  margin: 0 auto;
  box-sizing: border-box;
}
.viv-lookup-panel__input {
  width: 100%;
  height: 36px;
  padding: 0 12px;
  border-radius: 8px;
  border: 1px solid var(--viv-border);
  background: var(--viv-canvas);
  color: var(--viv-body);
  font-size: 14px;
  outline: 0;
  transition: border-color 0.12s, background-color 0.12s;
}
.viv-lookup-panel__input::placeholder {
  color: rgba(var(--viv-muted-rgb), 0.65);
}
.viv-lookup-panel__input:focus {
  border-color: var(--viv-border-hov);
  background: var(--viv-surface);
}

.viv-lookup-panel__result {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 20px;
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--viv-body);
  max-width: 720px;
  width: 100%;
  margin: 0 auto;
  box-sizing: border-box;
}
.viv-lookup-panel__result:empty::before {
  content: 'Type a term and press Enter.';
  display: block;
  color: var(--viv-muted);
  font-size: 12.5px;
}
.viv-lookup-card__title {
  margin: 0 0 8px;
  font-size: 18px;
  font-weight: 600;
  color: var(--viv-body);
  line-height: 1.3;
}
.viv-lookup-card__summary {
  margin: 0 0 12px;
  color: var(--viv-body);
}
.viv-lookup-card__source {
  font-size: 12px;
  color: var(--viv-muted);
  word-break: break-all;
}
.viv-lookup-card__source a {
  color: var(--viv-muted);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: rgba(var(--viv-muted-rgb), 0.45);
}
.viv-lookup-card__source a:hover { color: var(--viv-body); }

.viv-lookup-state {
  color: var(--viv-muted);
  font-size: 13px;
}
.viv-lookup-state__retry {
  display: inline-block;
  margin-top: 8px;
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0;
  color: var(--viv-body);
  font-size: 13px;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.viv-lookup-panel__actions {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 20px max(14px, env(safe-area-inset-bottom));
  border-top: 1px solid var(--viv-border);
  background: var(--viv-surface);
  max-width: 720px;
  width: 100%;
  margin: 0 auto;
  box-sizing: border-box;
}
.viv-lookup-panel__actions[hidden] { display: none; }
.viv-lookup-panel__primary,
.viv-lookup-panel__secondary {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 34px;
  padding: 0 14px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  line-height: 1;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.viv-lookup-panel__primary {
  background: var(--viv-body);
  color: var(--viv-surface);
  border: 1px solid var(--viv-body);
  flex: 1 1 auto;
}
.viv-lookup-panel__primary:hover { opacity: 0.88; }
.viv-lookup-panel__secondary {
  background: transparent;
  color: var(--viv-body);
  border: 1px solid var(--viv-border);
}
.viv-lookup-panel__secondary:hover {
  background: rgba(var(--viv-body-rgb), 0.04);
  border-color: var(--viv-border-hov);
}
.viv-lookup-panel__primary:focus-visible,
.viv-lookup-panel__secondary:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}

.viv-lookup-panel__toast {
  position: absolute;
  left: 50%;
  bottom: 64px;
  transform: translate(-50%, 4px);
  padding: 6px 12px;
  border-radius: 8px;
  background: var(--viv-body);
  color: var(--viv-canvas);
  font-size: 12px;
  font-weight: 500;
  opacity: 0;
  transition: opacity 120ms ease, transform 120ms ease;
  pointer-events: none;
}
.viv-lookup-panel__toast[hidden] { display: none; }
.viv-lookup-panel__toast.is-visible {
  opacity: 1;
  transform: translate(-50%, 0);
}

/* Tabpanel visibility — Addy's contract.
   Mobile (<1024px): hide tabpanels marked aria-hidden="true".
   Desktop (>=1024px): the three Room panes sit side-by-side; aria
   state stays for SR users but display is always shown. */
@media (max-width: 1023.98px) {
  [role="tabpanel"][aria-hidden="true"] { display: none !important; }
}

/* ============================================================
   Voices — Twin pill, trait picker, Assembly card
   ============================================================ */

/* "Twin" pill. Carries the visible word so colour is never the only
   signal (Jakob §6.5). Monochrome, sits inline with the name. Surface
   delta is large enough that the kind registers BEFORE the name on
   a glance (Dieter call after the first screenshot review). */
.viv-twin-pill {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 10px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--viv-body);
  background: rgba(var(--viv-body-rgb), 0.18);
  border: 1px solid var(--viv-border-hov);
  border-radius: 999px;
  white-space: nowrap;
}

/* Trait preset picker — a radio group, never free-text (spec §5).
   Each option is a 44×44-minimum touch target (spec §6.1). */
.viv-trait-picker {
  border: 0;
  padding: 0;
  margin: 8px 0 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.viv-trait-option {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 8px 10px;
  border-radius: 8px;
  cursor: pointer;
  font-size: 13px;
  color: var(--viv-body);
  transition: background-color 0.1s;
}
.viv-trait-option:hover { background-color: rgba(var(--viv-body-rgb), 0.04); }
.viv-trait-option:has(input:checked) {
  background-color: rgba(var(--viv-body-rgb), 0.06);
}
.viv-trait-option input[type="radio"] {
  appearance: none;
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 999px;
  border: 1.5px solid var(--viv-muted);
  background: transparent;
  flex-shrink: 0;
  position: relative;
  cursor: pointer;
}
.viv-trait-option input[type="radio"]:checked {
  border-color: var(--accent, var(--viv-body));
}
.viv-trait-option input[type="radio"]:checked::after {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 8px; height: 8px;
  border-radius: 999px;
  background: var(--accent, var(--viv-body));
  transform: translate(-50%, -50%);
}
.viv-trait-option:focus-within {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}

/* Per-voice rail affordance: details/summary disclosure under each
   Twin row in Who's Here. Spec amendment: never in the thread header,
   only here and in Convene. */
.viv-trait-affordance {
  margin: 2px 0 0;
}
.viv-trait-affordance > summary {
  list-style: none;
  cursor: pointer;
  font-size: 13px;
  color: var(--viv-muted);
  padding: 4px 0;
  min-height: 24px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.viv-trait-affordance > summary::-webkit-details-marker { display: none; }
.viv-trait-affordance > summary::after {
  content: "▾";
  font-size: 10px;
  opacity: 0.6;
  transition: transform 0.15s;
}
.viv-trait-affordance[open] > summary::after { transform: rotate(180deg); }
.viv-trait-affordance > summary:hover { color: var(--viv-body); }

/* Assembly voice card — replaces .viv-tile for Voice-spec cards.
   Card-level checkbox toggles inclusion; selecting a radio inside
   doesn't toggle. Minimum 44×44 target on the head label. */
.viv-tile {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 16px;
  border: 1px solid var(--viv-border);
  border-radius: 12px;
  background: var(--viv-surface);
  transition: border-color 0.12s, background-color 0.12s;
  min-height: 96px;
}
.viv-tile:hover { border-color: var(--viv-border-hov); }
.viv-tile:has(.viv-tile__head input:checked) {
  border-color: var(--viv-body);
  box-shadow: inset 0 0 0 1px var(--viv-body);
}
.viv-tile__head-row {
  display: flex;
  align-items: center;
  gap: 12px;
}
.viv-tile__head {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  min-height: 44px;
  flex: 1;
  min-width: 0;
}
.viv-tile__head:focus-within {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
  border-radius: 4px;
}
.viv-tile__head input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  overflow: hidden;
}
.viv-tile__name {
  font-size: 18px;
  font-weight: 500;
  color: var(--viv-body);
  line-height: 1.25;
  margin: 0;
}
.viv-tile__role {
  font-size: 14px;
  color: var(--accent, var(--viv-muted));
  margin: 4px 0 0;
  letter-spacing: 0.01em;
  line-height: 1.25;
}
.viv-tile__desc {
  font-size: 13px;
  color: var(--viv-muted);
  line-height: 1.4;
  margin: 4px 0 0;
}

/* "Chat with {Name} only" — 1-on-1 entry inside the voice card.
   Quiet pill so it doesn't compete with the card's primary checkbox
   target; gains weight on hover/focus only. */
.viv-tile__direct {
  margin-top: 12px;
  align-self: flex-start;
  padding: 6px 12px;
  font-size: 13px;
  color: var(--viv-muted);
  background: transparent;
  border: 1px solid var(--viv-border);
  border-radius: 999px;
  cursor: pointer;
  transition: color 0.12s, border-color 0.12s, background-color 0.12s;
  -webkit-tap-highlight-color: transparent;
}
.viv-tile__direct:hover {
  color: var(--viv-body);
  border-color: var(--viv-body);
}
.viv-tile__direct:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}

/* Direct-chat door variant — single-Voice room card on the Foyer.
   Big avatar centred, voice's human name, "Direct" badge sitting
   quiet under the title. Container stays neutral (no role colour
   wash) per the minimalist-chrome rule. */
.viv-door--direct {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
  padding: 20px;
  border: 1px solid var(--viv-border);
  border-radius: 14px;
  background: var(--viv-surface);
  text-decoration: none;
  transition: border-color 0.12s, background-color 0.12s;
  min-height: 168px;
}
.viv-door--direct:hover {
  border-color: var(--viv-border-hov);
}
.viv-door--direct__head {
  display: flex;
  align-items: center;
  gap: 14px;
}
.viv-door--direct__body {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.viv-door--direct__title {
  font-size: 18px;
  font-weight: 500;
  color: var(--viv-body);
  line-height: 1.2;
  margin: 0;
  letter-spacing: -0.005em;
}
.viv-door--direct__badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-weight: 500;
  color: var(--viv-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.viv-door--direct__badge::before {
  content: "";
  width: 5px; height: 5px;
  border-radius: 999px;
  background: var(--accent, var(--viv-muted));
}

/* "Currently in this room" stub at the top of expand Assembly.
   Single locked voice rendered as an inert card, no checkbox. */
.viv-locked-voice {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border: 1px dashed var(--viv-border);
  border-radius: 12px;
  background: rgba(var(--viv-body-rgb), 0.02);
  margin-bottom: 18px;
}
.viv-locked-voice__name {
  font-size: 15px;
  font-weight: 500;
  color: var(--viv-body);
  margin: 0;
  line-height: 1.2;
}
.viv-locked-voice__hint {
  font-size: 12px;
  color: var(--viv-muted);
  margin: 2px 0 0;
}

/* Single-voice room: "Add more voices" CTA. Lives below the
   transcript composer; quiet pill aligned with the existing
   share/options chrome. */
.viv-add-voices {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 12px;
  border-radius: 999px;
  border: 1px solid var(--viv-border);
  background: transparent;
  color: var(--viv-muted);
  font-size: 13px;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition: color 0.12s, border-color 0.12s;
  -webkit-tap-highlight-color: transparent;
}
.viv-add-voices:hover {
  color: var(--viv-body);
  border-color: var(--viv-body);
}
.viv-add-voices:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}

/* Tab bar (mobile Room) */
.viv-tabbar {
  display: flex;
  gap: 0;
  border-top: 1px solid var(--viv-border);
  background: var(--viv-surface);
}
.viv-tab {
  flex: 1;
  appearance: none;
  background: transparent;
  border: 0;
  padding: 14px 8px calc(14px + env(safe-area-inset-bottom));
  color: var(--viv-muted);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: color 0.1s;
  border-top: 2px solid transparent;
  margin-top: -1px;
}
.viv-tab[aria-selected="true"] {
  color: var(--viv-body);
  border-top-color: var(--viv-body);
}
.viv-tab:hover { color: var(--viv-body); }

/* ============================================================
   FOYER / DASHBOARD v2 (2026-05-20)
   ----------------------------------------------------------------
   Lives at /. Replaces the bare grid with a top bar, hero, a
   Continue card pulled from the most-recent conversation, a
   primary-action row (Convene + Find a voice), Recent rooms
   (direct chats), and Your teams (multi-voice). The Voice Search
   launcher lives in `.viv-vs` further down.
   ============================================================ */

.viv-foyer-shell {
  /* Counteract the body min-height-screen so the sticky topbar
     anchors against the viewport, not a tall scroll container. */
  min-height: 100dvh;
}

/* ---- Top bar ---- */
.viv-topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 10px 18px;
  background: rgba(var(--viv-canvas-rgb), 0.86);
  backdrop-filter: saturate(140%) blur(10px);
  -webkit-backdrop-filter: saturate(140%) blur(10px);
  border-bottom: 0.5px solid var(--viv-border);
}
.viv-topbar__brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--viv-body);
  text-decoration: none;
}
.viv-topbar__logo {
  display: block;
  width: 28px; height: 28px;
  object-fit: contain;
  flex-shrink: 0;
}
.viv-topbar__name {
  font-size: 14.5px;
  font-weight: 500;
  letter-spacing: -0.005em;
}
.viv-topbar__meta {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}
.viv-balance {
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 12px;
  border-radius: 999px;
  background: rgba(var(--viv-body-rgb), 0.04);
  color: var(--viv-muted);
  font-size: 12.5px;
  font-weight: 500;
  text-decoration: none;
  font-variant-numeric: tabular-nums;
  transition: background-color 0.12s, color 0.12s;
}
.viv-balance:hover {
  background: rgba(var(--viv-body-rgb), 0.07);
  color: var(--viv-body);
}
.viv-userdot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px; height: 30px;
  border-radius: 999px;
  background: var(--viv-body);
  color: var(--viv-canvas);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-decoration: none;
  transition: opacity 0.12s;
}
.viv-userdot:hover { opacity: 0.86; }
.viv-userdot:focus-visible,
.viv-balance:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}

/* ---- Foyer page wrap ---- */
.viv-foyer {
  max-width: 1080px;
  margin: 0 auto;
  padding: 28px 18px 80px;
  display: flex;
  flex-direction: column;
  gap: 28px;
}
@media (min-width: 768px) {
  .viv-topbar  { padding: 12px 28px; }
  .viv-foyer   { padding: 40px 28px 96px; gap: 36px; }
}

/* ---- Hero ---- */
.viv-hero { margin: 4px 0 -4px; }
.viv-hero__hello {
  font-size: 13px;
  color: var(--viv-muted);
  margin: 0 0 6px;
}
.viv-hero__title {
  font-size: 24px;
  font-weight: 500;
  letter-spacing: -0.015em;
  line-height: 1.2;
  margin: 0;
  color: var(--viv-body);
}
@media (min-width: 768px) {
  .viv-hero__title { font-size: 30px; }
}

/* ---- Continue card ----
   Quiet surface, big tap target, single-line ellipsis on the
   preview so the row keeps a stable height regardless of message
   length. */
.viv-continue {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  border-radius: 16px;
  background: var(--viv-surface);
  border: 1px solid var(--viv-border);
  color: inherit;
  text-decoration: none;
  transition: border-color 0.14s, transform 0.14s, box-shadow 0.14s;
}
.viv-continue:hover {
  border-color: var(--viv-border-hov);
  transform: translateY(-1px);
  box-shadow: 0 12px 32px rgba(var(--viv-body-rgb), 0.05);
}
.viv-continue:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}
.viv-continue__avatars { flex-shrink: 0; }
.viv-continue__body {
  flex: 1;
  min-width: 0;
}
.viv-continue__title {
  font-size: 14px;
  font-weight: 500;
  color: var(--viv-body);
  margin: 0 0 3px;
  line-height: 1.25;
  letter-spacing: -0.005em;
}
.viv-continue__preview {
  font-size: 12.5px;
  color: var(--viv-muted);
  margin: 0;
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.viv-continue__preview--muted { color: var(--viv-dim); }
.viv-continue__arrow {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px; height: 28px;
  border-radius: 999px;
  color: var(--viv-muted);
  transition: color 0.12s, transform 0.14s;
}
.viv-continue:hover .viv-continue__arrow {
  color: var(--viv-body);
  transform: translateX(2px);
}

/* ---- Primary actions row ---- */
.viv-actions {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
}
@media (min-width: 640px) {
  .viv-actions {
    grid-template-columns: 1.4fr 1fr;
  }
}
.viv-action {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 52px;
  padding: 0 18px;
  border-radius: 14px;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -0.003em;
  cursor: pointer;
  text-decoration: none;
  transition: background-color 0.14s, border-color 0.14s,
              color 0.14s, transform 0.14s;
  -webkit-tap-highlight-color: transparent;
}
.viv-action:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}
.viv-action--primary {
  background: var(--viv-body);
  color: var(--viv-canvas);
  border: 1px solid var(--viv-body);
}
.viv-action--primary:hover {
  opacity: 0.92;
  transform: translateY(-1px);
}
.viv-action--secondary {
  background: var(--viv-surface);
  color: var(--viv-body);
  border: 1px solid var(--viv-border);
  position: relative;
}
.viv-action--secondary:hover {
  border-color: var(--viv-border-hov);
  transform: translateY(-1px);
}
.viv-action__kbd {
  display: none;
  font-family: inherit;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--viv-muted);
  background: rgba(var(--viv-body-rgb), 0.05);
  padding: 2px 7px;
  border-radius: 6px;
  margin-left: 4px;
}
@media (min-width: 640px) {
  .viv-action__kbd { display: inline-flex; align-items: center; }
}

/* ---- Section headers ---- */
.viv-section { display: flex; flex-direction: column; gap: 12px; }
.viv-section__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
.viv-section__title {
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--viv-muted);
  margin: 0;
}
.viv-section__count {
  font-size: 11.5px;
  color: var(--viv-dim);
  font-variant-numeric: tabular-nums;
}

/* ---- Grids ---- */
.viv-grid {
  display: grid;
  gap: 12px;
}
.viv-grid--rooms {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.viv-grid--teams {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 720px) {
  .viv-grid--rooms { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .viv-grid--teams { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .viv-grid--rooms { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .viv-grid--teams { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ---- "+ New team" door (dashed placeholder in grid) ---- */
.viv-door--new {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 148px;
  border: 1px dashed var(--viv-border-hov);
  border-radius: 14px;
  background: transparent;
  color: var(--viv-muted);
  text-decoration: none;
  transition: color 0.14s, border-color 0.14s, background-color 0.14s;
}
.viv-door--new:hover {
  color: var(--viv-body);
  border-color: var(--viv-body);
  background: rgba(var(--viv-body-rgb), 0.02);
}
.viv-door--new:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}
.viv-door--new__plus {
  font-size: 22px;
  line-height: 1;
  color: inherit;
}
.viv-door--new__label {
  font-size: 12.5px;
  font-weight: 500;
}
.viv-door--new--solo { max-width: 280px; }

/* ---- Preset / featured team door ---- */
.viv-door--preset {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 8px;
  padding: 16px 16px 14px;
  background: var(--viv-surface);
  border: 1px solid var(--viv-border);
  border-radius: 14px;
  color: inherit;
  text-decoration: none;
  text-align: left;
  width: 100%;
  min-height: 148px;
  cursor: pointer;
  transition: border-color 0.14s ease, transform 0.14s ease,
              box-shadow 0.14s ease;
}
.viv-door--preset:hover {
  border-color: var(--viv-border-hov);
  transform: translateY(-1px);
  box-shadow: 0 8px 24px rgba(var(--viv-body-rgb), 0.04);
}
.viv-door--preset:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}
.viv-door--preset .viv-door__body { flex: 1; }
.viv-door--preset .viv-door__name {
  font-size: 14px;
  font-weight: 600;
  color: var(--viv-body);
  margin: 0 0 6px;
}
.viv-door--preset .viv-door__desc {
  font-size: 12.5px;
  color: var(--viv-muted);
  line-height: 1.45;
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.viv-door--preset .viv-door__action {
  font-size: 11.5px;
  font-weight: 500;
  color: var(--viv-muted);
  margin-top: 8px;
}
.viv-door--preset:hover .viv-door__action { color: var(--viv-body); }

/* ---- Empty state for first-run users ---- */
.viv-empty {
  padding: 32px 18px;
  text-align: center;
  border: 1px dashed var(--viv-border);
  border-radius: 14px;
  background: rgba(var(--viv-body-rgb), 0.015);
}
.viv-empty__title {
  font-size: 15px;
  font-weight: 500;
  color: var(--viv-body);
  margin: 0 0 6px;
}
.viv-empty__hint {
  font-size: 13px;
  color: var(--viv-muted);
  margin: 0;
}
.viv-empty__link {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--viv-body);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

/* ---- Direct door — extra caption + tightened header ---- */
.viv-door--direct__caption {
  margin: auto 0 0;
  font-size: 11.5px;
  color: var(--viv-dim);
  letter-spacing: 0.01em;
}

/* ============================================================
   Voice Search launcher (.viv-vs)
   Native <dialog>. Centered modal on every breakpoint; the
   list scrolls within a fixed-height card so the keyboard nav
   has a predictable surface. Backdrop is a soft canvas wash so
   the dashboard stays legible behind it.
   ============================================================ */
.viv-vs {
  width: min(560px, calc(100vw - 32px));
  max-width: 100%;
  padding: 0;
  border: 0;
  border-radius: 18px;
  background: transparent;
  color: var(--viv-body);
  margin: auto;
  /* Native dialog default is `display:block`; we want flex on
     the inner card so the list can fill the card height. */
}
.viv-vs::backdrop {
  background: rgba(var(--viv-body-rgb), 0.34);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.viv-vs[open] {
  animation: vivVsIn 0.18s ease-out;
}
@keyframes vivVsIn {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .viv-vs[open] { animation: none; }
}
.viv-vs__card {
  display: flex;
  flex-direction: column;
  max-height: min(72vh, 560px);
  background: var(--viv-surface);
  border: 1px solid var(--viv-border);
  border-radius: 18px;
  overflow-y: auto;
  box-shadow: 0 28px 80px rgba(0, 0, 0, 0.22),
              0 2px 6px rgba(0, 0, 0, 0.08);
}
.viv-vs__head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--viv-border);
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--viv-surface);
  flex-shrink: 0;
  border-radius: 18px 18px 0 0;
}
.viv-vs__search-icon { color: var(--viv-muted); flex-shrink: 0; }
.viv-vs__input {
  flex: 1;
  appearance: none;
  background: transparent;
  border: 0;
  outline: none;
  font: inherit;
  font-size: 15px;
  color: var(--viv-body);
  padding: 6px 4px;
  min-width: 0;
}
.viv-vs__input::placeholder { color: var(--viv-dim); }
.viv-vs__input::-webkit-search-cancel-button { display: none; }
.viv-vs__close {
  appearance: none;
  background: transparent;
  border: 0;
  color: var(--viv-muted);
  width: 32px; height: 32px;
  border-radius: 8px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 0.12s, background-color 0.12s;
}
.viv-vs__close:hover {
  color: var(--viv-body);
  background: rgba(var(--viv-body-rgb), 0.06);
}
.viv-vs__close:focus-visible {
  outline: 2px solid var(--viv-body);
  outline-offset: 2px;
}

.viv-vs__list {
  list-style: none;
  margin: 0;
  padding: 6px;
  overflow-y: auto;
  flex: 1;
}
.viv-vs__item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  padding: 10px 10px;
  border-radius: 12px;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  min-height: 56px;
}
.viv-vs__item[aria-selected="true"] {
  background: rgba(var(--viv-body-rgb), 0.05);
}
.viv-vs__item[hidden] { display: none; }
.viv-vs__avatar {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px; height: 36px;
  border-radius: 999px;
  background: rgba(var(--viv-body-rgb), 0.04);
  position: relative;
}
.viv-vs__avatar::after {
  /* role-accent dot, lower-right */
  content: "";
  position: absolute;
  right: -1px; bottom: -1px;
  width: 8px; height: 8px;
  border-radius: 999px;
  background: var(--accent, var(--viv-muted));
  box-shadow: 0 0 0 2px var(--viv-surface);
}
.viv-vs__avatar-glyph {
  font-size: 13px;
  font-weight: 600;
  color: var(--viv-body);
  letter-spacing: -0.01em;
}
.viv-vs__avatar-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 999px;
}
.viv-vs__body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.viv-vs__line1 {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  color: var(--viv-body);
}
.viv-vs__name {
  font-size: 14px;
  font-weight: 500;
  color: var(--viv-body);
  letter-spacing: -0.005em;
}
.viv-vs__kind {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--viv-muted);
  padding: 1px 6px;
  border-radius: 999px;
  border: 1px solid var(--viv-border);
}
.viv-vs__role {
  font-size: 12px;
  color: var(--viv-muted);
}
.viv-vs__country {
  font-size: 11px;
  color: var(--viv-muted);
  letter-spacing: 0.01em;
  padding: 1px 7px;
  border-radius: 999px;
  background: rgba(var(--viv-body-rgb), 0.05);
}
.viv-vs__desc {
  font-size: 12.5px;
  color: var(--viv-muted);
  line-height: 1.35;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.viv-vs__enter {
  flex-shrink: 0;
  font-size: 12px;
  color: var(--viv-dim);
  opacity: 0;
  transition: opacity 0.12s;
  font-variant-numeric: tabular-nums;
  padding-right: 4px;
}
.viv-vs__item[aria-selected="true"] .viv-vs__enter {
  opacity: 1;
  color: var(--viv-body);
}
.viv-vs__empty {
  text-align: center;
  padding: 24px 16px;
  font-size: 13px;
  color: var(--viv-muted);
}
.viv-vs__foot {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
  padding: 10px 14px;
  border-top: 1px solid var(--viv-border);
  background: rgba(var(--viv-body-rgb), 0.015);
  font-size: 11px;
  color: var(--viv-dim);
}
.viv-vs__foot kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  margin-right: 4px;
  border-radius: 4px;
  border: 1px solid var(--viv-border);
  background: var(--viv-surface);
  font-family: inherit;
  font-size: 10px;
  color: var(--viv-muted);
}
@media (max-width: 480px) {
  .viv-vs__foot { gap: 10px; font-size: 10.5px; }
}

/* Screen-reader-only utility — used by titles/help text in the
   voice search dialog so the visible UI stays calm. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

