/* ECHO 2.0 — design system
   Clean minimal light. White/soft-grey surfaces, hairline borders, generous
   whitespace, one blue accent. Compact type: only titles carry weight. */

:root {
  --bg:          #ffffff;
  --bg-soft:     #f7f8fa;
  --bg-sunken:   #f1f3f7;
  --bg-accent:   #eff4ff;

  --line:        #e6e8ec;
  --line-strong: #d4d9e0;

  --ink:         #0f1720;
  --ink-2:       #4a5566;
  --ink-3:       #838c9a;

  --accent:      #2563eb;
  --accent-ink:  #1d4ed8;
  --ok:          #15803d;
  --warn:        #b45309;
  --danger:      #b91c1c;

  --r:           9px;
  --r-sm:        6px;
  --r-lg:        14px;

  --sh-1: 0 1px 2px rgba(16,24,40,.05);
  --sh-2: 0 1px 2px rgba(16,24,40,.05), 0 10px 28px -14px rgba(16,24,40,.22);
  --sh-3: 0 2px 6px rgba(16,24,40,.06), 0 24px 60px -20px rgba(16,24,40,.28);

  --fs:    12px;
  --fs-sm: 11px;
  --fs-md: 13px;
  --fs-lg: 15px;

  --font: "Inter", "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system,
          "Helvetica Neue", Arial, sans-serif;
}

* { box-sizing: border-box; }

html, body {
  margin: 0; padding: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--fs);
  font-weight: 400;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 { margin: 0; font-weight: 650; letter-spacing: -0.011em; line-height: 1.25; }
h1 { font-size: 22px; letter-spacing: -0.021em; }
h2 { font-size: 15px; }
h3 { font-size: 13px; }
p  { margin: 0 0 10px; }
a  { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
[hidden] { display: none !important; }

.muted     { color: var(--ink-2); }
.dim       { color: var(--ink-3); }
.small     { font-size: var(--fs-sm); }
.mono      { font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; font-size: var(--fs-sm); }
.center    { text-align: center; }
.grow      { flex: 1 1 auto; min-width: 0; }
.row       { display: flex; align-items: center; gap: 8px; }
.row.wrap  { flex-wrap: wrap; }
.col       { display: flex; flex-direction: column; gap: 8px; }
.spacer    { flex: 1 1 auto; }
.stack > * + * { margin-top: 12px; }
.nowrap    { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ---------- buttons ---------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 30px; padding: 0 12px;
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  background: #fff; color: var(--ink);
  font: inherit; font-size: var(--fs); font-weight: 500;
  cursor: pointer; white-space: nowrap;
  transition: background .12s ease, border-color .12s ease, box-shadow .12s ease;
}
.btn:hover:not(:disabled) { background: var(--bg-soft); border-color: #c3cad4; text-decoration: none; }
.btn:active:not(:disabled) { background: var(--bg-sunken); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.btn.primary {
  background: var(--accent); border-color: var(--accent); color: #fff;
  box-shadow: 0 1px 2px rgba(37,99,235,.28);
}
.btn.primary:hover:not(:disabled) { background: var(--accent-ink); border-color: var(--accent-ink); }
.btn.ghost { border-color: transparent; background: transparent; color: var(--ink-2); }
.btn.ghost:hover:not(:disabled) { background: var(--bg-sunken); color: var(--ink); }
.btn.danger { color: var(--danger); border-color: #f0d5d5; }
.btn.danger:hover:not(:disabled) { background: #fdf3f3; border-color: #e5b8b8; }
.btn.lg { height: 36px; padding: 0 18px; font-size: var(--fs-md); }
.btn.sm { height: 25px; padding: 0 9px; font-size: var(--fs-sm); }

/* ---------- inputs ---------- */

label.field { display: block; }
label.field > span {
  display: block; margin-bottom: 5px;
  font-size: var(--fs-sm); font-weight: 500; color: var(--ink-2);
}
input[type=text], input[type=email], input[type=password], input[type=number],
select, textarea {
  width: 100%; padding: 6px 9px;
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  background: #fff; color: var(--ink);
  font: inherit; font-size: var(--fs);
  transition: border-color .12s ease, box-shadow .12s ease;
}
textarea { resize: vertical; min-height: 70px; line-height: 1.6; }
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(37,99,235,.12);
}
input::placeholder, textarea::placeholder { color: var(--ink-3); }
.hint { margin-top: 4px; font-size: var(--fs-sm); color: var(--ink-3); }

input[type=range] {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 18px; background: transparent; cursor: pointer;
}
input[type=range]::-webkit-slider-runnable-track {
  height: 4px; border-radius: 2px; background: var(--bg-sunken);
}
input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 14px; height: 14px; margin-top: -5px;
  border-radius: 50%; background: var(--accent);
  border: 2px solid #fff; box-shadow: var(--sh-1);
}

/* ---------- surfaces ---------- */

.card {
  background: #fff; border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--sh-1);
}
.card.pad   { padding: 15px; }
.card.pad-l { padding: 20px; }
.card.flat  { box-shadow: none; background: var(--bg-soft); }

.panel-head {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 16px; border-bottom: 1px solid var(--line);
}
.panel-body { padding: 16px; }

.pill {
  display: inline-flex; align-items: center; gap: 5px;
  height: 19px; padding: 0 8px; border-radius: 999px;
  background: var(--bg-sunken); color: var(--ink-2);
  font-size: 10.5px; font-weight: 500; letter-spacing: .01em;
}
.pill.blue   { background: var(--bg-accent); color: var(--accent-ink); }
.pill.green  { background: #edf7f0; color: var(--ok); }
.pill.amber  { background: #fdf5e9; color: var(--warn); }
.pill.grey   { background: var(--bg-sunken); color: var(--ink-3); }
.dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

.sep { height: 1px; background: var(--line); border: 0; margin: 16px 0; }

/* ---------- app shell ---------- */

.topbar {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; gap: 12px;
  height: 48px; padding: 0 18px;
  background: rgba(255,255,255,.88); backdrop-filter: saturate(1.6) blur(10px);
  border-bottom: 1px solid var(--line);
}
.brand { display: flex; align-items: center; gap: 8px; font-weight: 650; font-size: var(--fs-md); color: var(--ink); }
.brand:hover { text-decoration: none; }
.brand .mark {
  width: 20px; height: 20px; border-radius: 6px;
  background: linear-gradient(150deg, var(--accent), #5b8bf7);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.35);
}
.brand .sub { color: var(--ink-3); font-weight: 400; font-size: var(--fs-sm); }

.shell { display: grid; grid-template-columns: 216px 1fr; min-height: calc(100vh - 48px); }
.side {
  border-right: 1px solid var(--line); background: var(--bg-soft);
  padding: 14px 10px; overflow-y: auto;
  position: sticky; top: 48px; height: calc(100vh - 48px);
}
.side-label {
  padding: 0 8px 6px; font-size: 10px; font-weight: 600;
  letter-spacing: .07em; text-transform: uppercase; color: var(--ink-3);
}
.side-item {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 9px; margin-bottom: 1px;
  border-radius: var(--r-sm); color: var(--ink-2);
  cursor: pointer; font-size: var(--fs); user-select: none;
}
.side-item:hover { background: #eef0f4; color: var(--ink); text-decoration: none; }
.side-item.active { background: #fff; color: var(--ink); font-weight: 550; box-shadow: var(--sh-1); }
/* The avatar's own face where there is one, initials where there is not.
   Focused at 35% rather than dead centre: a square crop of a landscape frame
   loses nothing vertically, but a portrait-orientation phone photo does, and
   centring it takes the crop off the top of the head — which at 22px is the
   difference between a face and a chin. */
.side-item .avatar-chip {
  width: 22px; height: 22px; border-radius: 6px; flex: 0 0 auto;
  background: var(--bg-sunken) center 35%/cover no-repeat;
  display: grid; place-items: center; font-size: 10px; font-weight: 600; color: var(--ink-3);
}
/* The content column, and the main fix for "wide empty spaces".
   It was capped at 1080px and left-aligned, so on any monitor wider than about
   1300px the studio sat in a column with a third of the screen blank beside it.
   It now fills the shell up to a genuine reading limit and centres what is
   left over, so nothing is stranded against one edge. */
.main {
  padding: 20px 28px 56px;
  max-width: 1560px; width: 100%; margin: 0 auto;
}

@media (max-width: 780px) {
  .shell { grid-template-columns: 1fr; min-height: 0; }
  .side {
    border-right: 0; border-bottom: 1px solid var(--line);
    padding: 10px; display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
    position: static; height: auto;
  }
  .side-label { display: none; }
  .side #avatarList { display: flex; flex-wrap: wrap; gap: 6px; flex: 1 1 auto; }
  .side-item { margin: 0; background: #fff; border: 1px solid var(--line); }
  .side > div:last-child { width: auto !important; padding: 0 !important; }
  .side > div:last-child .btn { width: auto !important; }
  .main { padding: 16px 14px 50px; }
  .tabs { overflow-x: auto; }
  .tab { white-space: nowrap; }
}

.page-head { margin-bottom: 14px; }
/* The name in the accent blue, so the page says whose studio this is before it
   says anything else. The role and organisation stay grey underneath. */
.page-head h1 { color: var(--accent); }
.page-head .sub { color: var(--ink-2); margin-top: 3px; }

/* Tabs and the setup strip stay put while a long page scrolls under them, so
   switching sections never means scrolling back to the top first, and what is
   still missing stays readable from anywhere on the page rather than only at
   the top. They stick as one block: sticking them separately leaves a gap
   between the two that scrolling content shows through. */
.tabbar {
  position: sticky; top: 48px; z-index: 20;
  padding-bottom: 16px;
  background: rgba(255,255,255,.92); backdrop-filter: saturate(1.6) blur(10px);
}
.tabs {
  display: flex; gap: 2px;
  border-bottom: 1px solid var(--line);
}
.tabbar .setup { margin: 10px 0 0; }
.tab {
  padding: 8px 12px; margin-bottom: -1px; cursor: pointer;
  border-bottom: 2px solid transparent; color: var(--ink-2); font-weight: 500;
  display: flex; align-items: center; gap: 6px; white-space: nowrap;
}
.tab:hover { color: var(--ink); }
.tab.active { color: var(--accent-ink); border-bottom-color: var(--accent); }
/* A dot on a tab whose section still wants attention. Quieter than a badge and
   it does not move the label around when it appears. */
.tab .need { width: 5px; height: 5px; border-radius: 50%; background: var(--warn); opacity: .8; }

.grid { display: grid; gap: 14px; align-items: start; }
.grid.c2 { grid-template-columns: repeat(2, minmax(0,1fr)); }
.grid.c3 { grid-template-columns: repeat(3, minmax(0,1fr)); }

/* Content-width columns rather than fixed counts.
   A two-column grid on a 2560px display gives two 700px cards holding a text
   field each — the "wide empty space" problem restated. These let a row hold as
   many columns as genuinely fit and reflow on their own. */
.grid.fit    { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid.fit-sm { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
/* Studio working layout: a wide editing column beside a fixed-width preview.
   The preview is square, so letting it grow with the viewport wasted the space
   it grew into. */
.grid.work { grid-template-columns: minmax(0,1fr) 340px; gap: 16px; }
/* A grid cell holding a run of stacked cards rather than one. min-width keeps a
   long unbroken string inside it from widening the column. */
.col-stack { min-width: 0; }
/* .grid puts its items at the top of the row and lets them keep their own
   height. A column that has to be taller than its contents — because something
   inside it is pinned and needs room to travel — asks for the whole row. */
.col-fill { min-width: 0; align-self: stretch; }
/* A pinned side rail: everything in it stays on screen while the column beside
   it scrolls. It stops under the tab bar and is never taller than what is left
   of the window below that, so it cannot push its own contents out of sight.
   --stick-top is measured in renderDetail, because the tab bar's height changes
   with the setup strip.

   One child may carry .rail-scroll: it takes the height the others do not want
   and scrolls inside itself, which is how a list of unknown length lives in a
   rail without either being cut off or pushing the rail past the window. */
.rail {
  position: sticky; top: var(--stick-top, 140px); z-index: 5;
  display: flex; flex-direction: column; gap: 12px;
  max-height: calc(100vh - var(--stick-top, 140px) - 18px);
}
.rail > * { flex: 0 0 auto; }
.rail-scroll {
  flex: 1 1 auto; min-height: 120px;
  display: flex; flex-direction: column; overflow: hidden;
}
.rail-scroll > .card-head { flex: 0 0 auto; }
.rail-scroll > .card-body {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto; overscroll-behavior: contain;
}

/* Pinning needs a window to pin to. Once the columns stack, or the window is
   too short to hold the rail and still show anything of it, the rail goes back
   to being ordinary content in the flow. */
@media (max-width: 1180px), (max-height: 620px) {
  .rail { position: static; max-height: none; }
  .rail-scroll { overflow: visible; min-height: 0; }
  .rail-scroll > .card-body { overflow-y: visible; }
}
/* Knowledge base: the folder tree is a navigation column, so it stays narrow and
   the documents get the width. Imported folder names are long and dated
   ("2025-09 AI Session - CSMT-OMT Retreat in Kenya"), so the column is wide
   enough for two or three words a line rather than one. */
.grid.kb { grid-template-columns: 340px minmax(0,1fr); gap: 14px; }
@media (max-width: 1180px) { .grid.work { grid-template-columns: 1fr; } }
@media (max-width: 900px) { .grid.c2, .grid.c3, .grid.kb { grid-template-columns: 1fr; } }

/* A card that titles itself, so a section header does not cost 14px of stack
   margin plus a heading line plus a hint line. */
.card > .card-head {
  display: flex; align-items: center; gap: 8px;
  padding: 11px 15px; border-bottom: 1px solid var(--line);
}
.card > .card-head h2, .card > .card-head h3 { flex: 1 1 auto; min-width: 0; }
.card > .card-body { padding: 15px; }
.card > .card-body > * + * { margin-top: 12px; }
.card > .card-body.tight > * + * { margin-top: 9px; }
.card > .card-body > .list { margin-left: -15px; margin-right: -15px;
                             border-left: 0; border-right: 0; border-radius: 0; }
.card > .card-body > .list:last-child { margin-bottom: -15px; border-bottom: 0; }

/* Field pairs that sit side by side down to a real minimum, then stack. */
.fields { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }

.stack.tight > * + * { margin-top: 9px; }

/* ---------- tables / lists ---------- */

.list { border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; background: #fff; }
.list-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px; border-bottom: 1px solid var(--line);
}
.list-row:last-child { border-bottom: 0; }
.list-row:hover { background: var(--bg-soft); }

/* Folder tree. Rows are indented inline by depth, so the only thing the
   stylesheet owns is how a row looks and which one is selected. */
.tree { max-height: 62vh; overflow: auto; margin: -4px -6px; }
.tree-row {
  display: flex; align-items: flex-start; gap: 6px;
  padding: 5px 8px; border-radius: var(--r-sm);
  cursor: pointer; user-select: none; color: var(--ink-2);
}
.tree-row:hover { background: var(--bg-soft); color: var(--ink); }
.tree-row.sel { background: var(--bg-accent); color: var(--accent-ink); font-weight: 550; }
.tree-row.sel .dim { color: var(--accent-ink); opacity: .75; }
.tree-row .small { padding-top: 2px; }
.twisty {
  flex: 0 0 auto; width: 11px; text-align: center; padding-top: 5px;
  font-size: 9px; line-height: 1; color: var(--ink-3);
}
.twisty:hover { color: var(--ink); }
/* Depth is carried by the icon as well as the indent, so a folder that holds
   folders reads differently from one that only holds files. */
.ficon { flex: 0 0 auto; font-size: 11px; line-height: 1.45; opacity: .85; }
/* A long name wraps instead of being cut off mid-word. */
.wrap-text { white-space: normal; overflow-wrap: anywhere; line-height: 1.4; }

/* A folder heading inside the document list: the files under it drop the path
   from their own line, so it is written once here. */
.list-group {
  display: flex; align-items: flex-start; gap: 7px;
  padding: 7px 14px; border-bottom: 1px solid var(--line);
  background: var(--bg-soft); color: var(--ink-2);
  font-size: var(--fs-sm); font-weight: 600;
  position: sticky; top: 0; z-index: 1;
}
.list-group + .list-group { border-top: 0; }

.empty {
  padding: 18px 16px; text-align: center; color: var(--ink-3);
  border: 1px dashed var(--line-strong); border-radius: var(--r); background: var(--bg-soft);
  font-size: var(--fs-sm); line-height: 1.55;
}
.empty b { display: block; color: var(--ink-2); font-size: var(--fs); margin-bottom: 2px; }

.stat { padding: 12px 14px; }
.stat .k { font-size: 10px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: var(--ink-3); }
.stat .v { font-size: 20px; font-weight: 650; letter-spacing: -.02em; margin-top: 2px; }
.stat .n { color: var(--ink-3); font-size: var(--fs-sm); }

/* ---------- setup strip ----------
   Replaces the publish checklist card. Same information — what is set up and
   what is not — in one line at the top of the page instead of a panel at the
   bottom of a tab nobody opens until the end. Nothing here blocks publishing. */

.setup {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 9px 13px; margin-bottom: 14px;
  border: 1px solid var(--line); border-radius: var(--r); background: var(--bg-soft);
}
.setup .track { flex: 0 0 88px; height: 4px; border-radius: 2px; background: var(--bg-sunken); overflow: hidden; }
.setup .track i { display: block; height: 4px; border-radius: 2px; background: var(--ok); transition: width .35s ease; }
.setup .item {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px 3px 6px; border-radius: 999px;
  border: 1px solid var(--line); background: #fff;
  color: var(--ink-2); font-size: var(--fs-sm); cursor: pointer; white-space: nowrap;
}
.setup .item:hover { border-color: var(--line-strong); color: var(--ink); }
.setup .item .tick {
  width: 14px; height: 14px; border-radius: 50%; flex: 0 0 auto;
  display: grid; place-items: center; font-size: 9px; line-height: 1;
  background: var(--bg-sunken); color: var(--ink-3);
}
.setup .item.done { color: var(--ink-3); }
.setup .item.done .tick { background: #dff1e5; color: var(--ok); }
/* Nothing left to do: the row stays, and says so quietly. */
.setup.all-done { background: #f5faf6; border-color: #dfeee4; }

/* ---------- selectable option card ---------- */

.opt {
  display: block; width: 100%; text-align: left; cursor: pointer;
  padding: 11px 13px; border: 1px solid var(--line); border-radius: var(--r);
  background: #fff; font: inherit; color: inherit;
  transition: border-color .12s ease, background .12s ease, box-shadow .12s ease;
}
.opt:hover { border-color: var(--line-strong); background: var(--bg-soft); }
.opt.on { border-color: var(--accent); background: var(--bg-accent); box-shadow: 0 0 0 1px var(--accent); }
.opt .t { display: flex; align-items: center; gap: 7px; font-weight: 550; }
.opt .t .grow { flex: 1 1 auto; min-width: 0; }
.opt .d { margin-top: 3px; font-size: var(--fs-sm); color: var(--ink-3); line-height: 1.5; }

/* ---------- inline progress bar ---------- */

.bar { height: 4px; border-radius: 2px; background: var(--bg-sunken); overflow: hidden; }
.bar > i { display: block; height: 4px; border-radius: 2px; background: var(--accent);
           transition: width .3s ease; }

/* ---------- collapsible section ----------
   Advanced controls stay on the page and stay one click away, without spending
   vertical space on people who will never touch them. */

details.fold { border-top: 1px solid var(--line); padding-top: 10px; }
details.fold > summary {
  cursor: pointer; list-style: none; user-select: none;
  display: flex; align-items: center; gap: 6px;
  font-size: var(--fs-sm); font-weight: 550; color: var(--ink-2);
}
details.fold > summary::-webkit-details-marker { display: none; }
details.fold > summary::before {
  content: '›'; display: inline-block; font-size: 14px; line-height: 1;
  color: var(--ink-3); transition: transform .15s ease;
}
details.fold[open] > summary::before { transform: rotate(90deg); }
details.fold > summary:hover { color: var(--ink); }
details.fold > .fold-body { padding-top: 11px; }
details.fold > .fold-body > * + * { margin-top: 11px; }

/* ---------- key/value rows ---------- */

.kv { display: flex; align-items: baseline; gap: 8px; font-size: var(--fs-sm); }
.kv .k { color: var(--ink-3); flex: 0 0 auto; }
.kv .v { color: var(--ink-2); min-width: 0; }

/* ---------- avatar stage (shared: studio preview + public page) ---------- */

.stage {
  position: relative; width: 100%; aspect-ratio: 1 / 1;
  border-radius: var(--r-lg); overflow: hidden;
  background: var(--bg-sunken); box-shadow: var(--sh-2);
  /* Cap the square by viewport height, not by a fixed pixel size: the stage is
     1:1 and full-width, so on a wide column it grew as tall as the column was
     broad and pushed the conversation off the bottom of the screen. Capping the
     *width* keeps it square — capping the height would stretch it — and centring
     stops the narrower box from sitting hard against one edge. */
  max-width: min(100%, 48vh);
  margin-inline: auto;
}
.stage video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; transition: opacity .28s ease;
}
.stage video.idle    { opacity: 1; }
.stage video.speak   { opacity: 0; }
.stage.speaking video.idle  { opacity: 0; }
.stage.speaking video.speak { opacity: 1; }
/* neural lip-sync: the rendered answer sits above both loops and, while it is
   playing, is the only layer visible. The loops keep running underneath so the
   hand-off back to listening has nothing to load. */
.stage video.render { opacity: 0; z-index: 2; }
.stage video.render.on { opacity: 1; }
.stage.rendered video.idle,
.stage.rendered video.speak { opacity: 0; }
/* the captured thumbnail, held as a fixed frame while an answer is worked out.
   Above every moving layer and below the badge, so the face is still but the
   avatar does not look switched off. */
/* the live face: streamed over WebRTC, above every other layer while a session
   is open, and completely transparent until one is — so the stage never flashes
   black while the connection is negotiated. */
.stage video.live { opacity: 0; z-index: 3; }
.stage video.live.on { opacity: 1; }
.stage .still {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0; z-index: 2;
  transition: opacity .28s ease; pointer-events: none;
  user-select: none; -webkit-user-drag: none;
}
.stage.waiting .still { opacity: 1; }
/* photo-built avatar: stacked stills cross-faded by the audio envelope */
.stage.photos { background: var(--bg-sunken); }
.stage .frame {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; user-select: none; -webkit-user-drag: none;
  will-change: opacity;
}
/* a slow breath so a still photo doesn't read as a frozen error state */
.stage.photos .frame { animation: breathe 6s ease-in-out infinite; }
.stage.photos.speaking .frame { animation-duration: 4s; }
@keyframes breathe {
  0%, 100% { transform: scale(1.000) translateY(0); }
  50%      { transform: scale(1.012) translateY(-0.35%); }
}
@media (prefers-reduced-motion: reduce) {
  .stage.photos .frame { animation: none; }
}

.stage .stage-empty {
  position: absolute; inset: 0; display: grid; place-items: center;
  color: var(--ink-3); font-size: var(--fs-sm); text-align: center; padding: 20px;
}
.stage .badge {
  position: absolute; left: 10px; bottom: 10px; z-index: 4;
  display: flex; align-items: center; gap: 6px;
  padding: 4px 9px; border-radius: 999px;
  background: rgba(15,23,32,.62); backdrop-filter: blur(6px);
  color: #fff; font-size: 10.5px; font-weight: 500;
}
.stage .badge .dot { background: #5ee08a; }
.stage.speaking .badge .dot { background: #7fb0ff; animation: pulse 1.1s ease-in-out infinite; }
@keyframes pulse { 0%,100% { opacity:1 } 50% { opacity:.35 } }

/* clip range picker */
.timeline {
  position: relative; height: 34px; margin-top: 8px;
  border-radius: var(--r-sm); background: var(--bg-sunken);
  border: 1px solid var(--line); overflow: hidden;
}
.timeline .sel {
  position: absolute; top: 0; bottom: 0;
  background: rgba(37,99,235,.16); border-left: 2px solid var(--accent);
  border-right: 2px solid var(--accent);
}
.timeline .cursor { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--ink); opacity: .5; }
.timeline .ticks { position: absolute; inset: 0; display: flex; }
.timeline .ticks i { flex: 1; border-right: 1px solid rgba(0,0,0,.05); }

/* ---------- chat ---------- */

.chat { display: flex; flex-direction: column; gap: 12px; }
.msg { display: flex; gap: 9px; align-items: flex-start; }
.msg .who {
  flex: 0 0 auto; width: 22px; height: 22px; border-radius: 6px;
  display: grid; place-items: center; font-size: 10px; font-weight: 650;
  background: var(--bg-sunken); color: var(--ink-3); margin-top: 1px;
}
.msg.avatar .who { background: var(--bg-accent); color: var(--accent-ink); }
.msg .bubble {
  padding: 9px 12px; border-radius: var(--r);
  font-size: var(--fs-md); line-height: 1.6; max-width: 62ch;
}
.msg.visitor .bubble { background: var(--bg-sunken); color: var(--ink); }
.msg.avatar  .bubble { background: #fff; border: 1px solid var(--line); box-shadow: var(--sh-1); }
.msg .meta { margin-top: 4px; font-size: 10.5px; color: var(--ink-3); }
/* replay an earlier answer — quiet until wanted, so the thread still reads as a
   conversation rather than a row of controls. */
.msg .replay-row { margin-top: 6px; }
.msg .btn.replay {
  padding: 3px 10px; font-size: 11.5px; border-radius: 999px;
  color: var(--ink-2); opacity: .75; transition: opacity .15s ease;
}
.msg:hover .btn.replay, .msg .btn.replay:focus-visible { opacity: 1; }
.msg .btn.replay[disabled] { opacity: .4; cursor: default; }

/* generation progress — a status line, not a message. Keep it slim. */
.msg.prog { align-items: center; }
.msg.prog .grow { max-width: 300px; }
.progress-line {
  display: flex; align-items: baseline; gap: 8px;
  font-size: var(--fs-sm); color: var(--ink-2); line-height: 1.3;
}
.progress-line .phase { flex: 0 1 auto; min-width: 0; }
.progress-line .pct {
  margin-left: auto; color: var(--ink-3);
  font-variant-numeric: tabular-nums; font-size: 10.5px;
}
.msg.prog .track {
  margin-top: 5px; height: 3px; border-radius: 2px;
  background: var(--bg-sunken); overflow: hidden;
}
.msg.prog .fill {
  height: 3px; width: 4%; border-radius: 2px; background: var(--accent);
  transition: width .4s cubic-bezier(.4,0,.2,1);
}

.typing { display: inline-flex; gap: 3px; padding: 3px 0; }
.typing i { width: 5px; height: 5px; border-radius: 50%; background: var(--ink-3); animation: bounce 1.2s infinite; }
.typing i:nth-child(2) { animation-delay: .15s; }
.typing i:nth-child(3) { animation-delay: .3s; }
@keyframes bounce { 0%,60%,100% { opacity:.25; transform: translateY(0) } 30% { opacity:.9; transform: translateY(-3px) } }

.composer {
  display: flex; gap: 8px; align-items: flex-end;
  padding: 10px; border: 1px solid var(--line-strong); border-radius: var(--r);
  background: #fff; box-shadow: var(--sh-1);
}
.composer textarea {
  border: 0; padding: 3px 2px; min-height: 22px; max-height: 130px;
  font-size: var(--fs-md); resize: none;
}
.composer textarea:focus { box-shadow: none; }

/* always-on listening bar */
.listen {
  display: flex; align-items: center; gap: 9px;
  padding: 9px 12px; margin-bottom: 10px;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--bg-soft); font-size: var(--fs);
}
.listen .mic {
  width: 26px; height: 26px; border-radius: 50%; flex: 0 0 auto;
  display: grid; place-items: center; font-size: 13px;
  background: var(--bg-sunken); color: var(--ink-3);
  border: 1px solid var(--line-strong); cursor: pointer;
}
.listen.on  { border-color: #bcd2fb; background: var(--bg-accent); }
.listen.on  .mic { background: var(--accent); color: #fff; border-color: var(--accent); }
.listen.off .mic { background: #fff; }
.listen .what { color: var(--ink-2); }
.listen.on .what { color: var(--accent-ink); }
/* The question so far. It gives way to the status text rather than pushing it
   out of the bar, however long the question runs. */
.listen .heard { color: var(--ink); font-style: italic; flex: 0 1 auto; min-width: 0; max-width: 46%; }

/* pulsing ring while the microphone is live */
.listen.on .mic::after {
  content: ''; position: absolute; width: 26px; height: 26px; border-radius: 50%;
  border: 2px solid var(--accent); opacity: .55; animation: ring 1.6s ease-out infinite;
}
.listen .mic { position: relative; }
@keyframes ring {
  0%   { transform: scale(1);   opacity: .55; }
  100% { transform: scale(1.9); opacity: 0; }
}

.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  padding: 5px 10px; border: 1px solid var(--line-strong); border-radius: 999px;
  background: #fff; color: var(--ink-2); cursor: pointer; font-size: var(--fs-sm);
}
.chip:hover { border-color: var(--accent); color: var(--accent-ink); background: var(--bg-accent); }

/* ---------- toast + modal ---------- */

#toasts {
  position: fixed; right: 16px; bottom: 16px; z-index: 200;
  display: flex; flex-direction: column; gap: 8px; align-items: flex-end;
}
.toast {
  padding: 9px 13px; border-radius: var(--r-sm);
  background: var(--ink); color: #fff; font-size: var(--fs);
  box-shadow: var(--sh-3); max-width: 380px;
  animation: rise .18s ease;
}
.toast.err { background: var(--danger); }
.toast.ok  { background: #14532d; }
/* Did what you asked, but something is still outstanding at a provider — which
   is neither of the two above and should not be dressed as either. */
.toast.warn { background: var(--warn); }
@keyframes rise { from { opacity: 0; transform: translateY(6px) } }

.backdrop {
  position: fixed; inset: 0; z-index: 150; display: grid; place-items: center;
  background: rgba(15,23,32,.34); backdrop-filter: blur(2px); padding: 20px;
}
.modal {
  width: min(560px, 100%); max-height: 88vh; overflow: auto;
  background: #fff; border-radius: var(--r-lg); box-shadow: var(--sh-3);
}
.modal .head { padding: 16px 20px 0; }
.modal .body { padding: 14px 20px; }
.modal .foot {
  display: flex; gap: 8px; justify-content: flex-end;
  padding: 14px 20px; border-top: 1px solid var(--line); background: var(--bg-soft);
  border-radius: 0 0 var(--r-lg) var(--r-lg);
}

/* ---------- camera and microphone capture ----------
   The one dark surface in a light interface, and deliberately so: a viewfinder
   reads as a viewfinder, and a face on white is a face competing with the page.
   Only the live frame is dark; everything around it is the same modal as
   anywhere else. */

.cap-frame {
  position: relative; width: 100%; aspect-ratio: 4 / 3;
  background: #0f1720; border-radius: var(--r); overflow: hidden;
  display: grid; place-items: center;
}
.cap-frame video, .cap-frame img {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
/* A mirror for the preview only. The captured frame is stored the right way
   round — see the note in capture.js. */
.cap-frame.mirror video { transform: scaleX(-1); }

/* Where the face should sit. Loose enough to be guidance rather than a mask:
   nothing is cropped to it, and a photo that overflows it still works. */
.cap-guide {
  position: absolute; inset: 0; margin: auto;
  width: 46%; height: 78%; border-radius: 50%;
  border: 1px dashed rgba(255, 255, 255, .5);
  box-shadow: 0 0 0 100vmax rgba(15, 23, 32, .22);
  pointer-events: none;
}

.cap-count {
  position: absolute; inset: 0; display: grid; place-items: center;
  color: #fff; font-size: 64px; font-weight: 650; letter-spacing: -.03em;
  text-shadow: 0 2px 18px rgba(0, 0, 0, .5);
  pointer-events: none;
}

/* The recording level. Wider and taller than a progress bar, because it is read
   at a glance while talking rather than studied. */
.cap-level { flex: 1 1 auto; height: 8px; border-radius: 4px; }
.cap-level > i { height: 8px; border-radius: 4px; transition: width .06s linear; }

.cap-clock {
  flex: 0 0 auto; min-width: 46px;
  font-variant-numeric: tabular-nums; font-size: var(--fs-md);
  font-weight: 650; color: var(--ink-2);
}

/* ---------- landing ----------
   One screen, two columns: the argument on the left, the form on the right.
   Signed-in requests never reach this page, so nothing here has two states. */

.landing {
  max-width: 1040px; margin: 0 auto; padding: 72px 28px 40px;
  display: grid; grid-template-columns: minmax(0, 1fr) 384px; gap: 72px;
  align-items: start;
}
.pitch h1 { font-size: 34px; letter-spacing: -.028em; max-width: 17ch; }
.pitch .lead {
  max-width: 52ch; margin: 14px 0 0;
  font-size: var(--fs-lg); color: var(--ink-2); line-height: 1.62;
}

/* Three steps as one line rather than three cards — the detail lives in the
   studio, where it is actually needed. */
.flow {
  display: flex; flex-wrap: wrap; gap: 8px 22px;
  margin: 26px 0 0; padding: 0; list-style: none;
  font-size: var(--fs-md); font-weight: 500; color: var(--ink-2);
}
.flow li { display: flex; align-items: center; gap: 8px; }
.flow .n {
  width: 19px; height: 19px; border-radius: 50%;
  background: var(--bg-accent); color: var(--accent-ink);
  display: grid; place-items: center; font-size: 10.5px; font-weight: 650;
}

.points { margin: 30px 0 0; }
.points > div { padding: 13px 0; border-top: 1px solid var(--line); }
.points dt { font-size: var(--fs-md); font-weight: 650; letter-spacing: -.011em; }
.points dd {
  margin: 3px 0 0; max-width: 60ch;
  color: var(--ink-2); line-height: 1.6;
}

.pitch .note {
  margin: 22px 0 0; padding-top: 14px; border-top: 1px solid var(--line);
  font-size: var(--fs-sm); color: var(--ink-3);
}

.landing-foot {
  max-width: 1040px; margin: 0 auto; padding: 0 28px 40px;
  font-size: var(--fs-sm); color: var(--ink-3);
}

@media (max-width: 940px) {
  .landing { grid-template-columns: 1fr; gap: 36px; padding-top: 44px; }
  .gate { order: -1; }                        /* on a phone the form is the point */
  .gate .card { max-width: 420px; }           /* ...but never a form field this wide */
  .pitch h1 { font-size: 28px; }
}

/* There was a page-container `.wrap` here — max-width, auto margins and 24px of
   side padding. Nothing used it as a container, but `wrap` is also the flex
   modifier on `.row`, so every `row wrap` silently picked up the padding: the
   avatar's name and role sat 24px to the right of the tabs and the card below
   them. Removed rather than renamed — no markup asked for it. */

.steps { counter-reset: s; }
.step { display: flex; gap: 12px; padding: 14px 0; border-top: 1px solid var(--line); }
.step:first-child { border-top: 0; }
.step .n {
  counter-increment: s; flex: 0 0 auto;
  width: 21px; height: 21px; border-radius: 50%;
  background: var(--bg-accent); color: var(--accent-ink);
  display: grid; place-items: center; font-size: 10.5px; font-weight: 650;
}
.step .n::before { content: counter(s); }


/* ---------- public avatar page ---------- */

.pub {
  min-height: 100vh; background:
    radial-gradient(1100px 460px at 50% -160px, #eef3fd 0%, rgba(238,243,253,0) 62%), var(--bg);
}
/* The conversation is a fixed frame, not a long page. It fills the window under
   the top bar and does not scroll itself: the avatar column stays where it is
   and the thread is the only thing that moves. Scrolling the face out of view
   while you are talking to it defeated the point of it being there.
   49px is .topbar's height plus its bottom border. */
.pub-inner {
  max-width: 940px; margin: 0 auto; padding: 26px 22px 22px;
  display: grid; grid-template-columns: 300px minmax(0,1fr); gap: 30px;
  align-items: stretch;
  height: calc(100vh - 49px);
}
.pub-aside { display: flex; flex-direction: column; min-height: 0; }
.pub-aside .stage { flex: 0 0 auto; }
/* Only the text under the avatar gives way on a short window — never the face. */
.pub-aside-meta { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.pub-panel { display: flex; flex-direction: column; min-height: 0; }
.pub-head { flex: 0 0 auto; margin-bottom: 14px; }
.pub-thread {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  overscroll-behavior: contain; padding-right: 4px;
}
.pub-foot { flex: 0 0 auto; padding-top: 14px; }
.pub-foot .chips { margin-top: 10px; }

/* Maximized: edge to edge, one third avatar and two thirds conversation. */
.pub.max .pub-inner {
  max-width: none; grid-template-columns: minmax(0,1fr) minmax(0,2fr);
  gap: 26px; padding: 18px 26px 20px;
}
.pub.max .stage { max-width: min(100%, 58vh); }

/* Stacked and narrow, a fixed frame leaves no room for either half, so the page
   goes back to scrolling as one piece. Thread.scroll follows the window here —
   see chat.js. */
@media (max-width: 860px) {
  .pub-inner, .pub.max .pub-inner {
    grid-template-columns: 1fr; gap: 20px; height: auto; padding-bottom: 60px;
  }
  .pub-aside { position: static !important; }
  .pub-aside-meta, .pub-thread { overflow: visible; }
  .stage, .pub.max .stage { max-width: 300px; margin: 0 auto; }
}
.pub-name { margin-top: 14px; font-size: 17px; font-weight: 650; letter-spacing: -.015em; }
.pub-role { color: var(--ink-2); font-size: var(--fs-md); }
.pub-note { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); font-size: var(--fs-sm); color: var(--ink-3); line-height: 1.55; }
