/* =========================================================================
   Lumino Forge — component styles (rc186)

   Styles for the lf-* primitives. Every value here is a token. If you find
   yourself typing a number, the scale in tokens.css is missing a step — fix
   it there, not here. That rule is what keeps 1,044 magic numbers from
   growing back.
   ========================================================================= */

/* ---------------------------------------------------------------- lf-icon */
lf-icon { display: inline-flex; align-items: center; justify-content: center;
          flex: 0 0 auto; line-height: 0; }
lf-icon svg { width: 1.15em; height: 1.15em; display: block; }
lf-icon svg[data-size="sm"]  { width: 14px; height: 14px; }
lf-icon svg[data-size="md"]  { width: 16px; height: 16px; }
lf-icon svg[data-size="lg"]  { width: 20px; height: 20px; }
lf-icon svg[data-size="xl"]  { width: 32px; height: 32px; }
lf-icon.lf-spin svg { animation: lf-spin 900ms linear infinite; }
@keyframes lf-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  lf-icon.lf-spin svg { animation-duration: 2.4s; }
}

/* -------------------------------------------------------------- lf-button */
/* rc214 — a button never compresses into its own icon. Without flex-shrink:0
   these collapse inside any tight flex row, which is why "Speak a test
   sentence" and "Preview / Save" overlapped their icons on a phone. */
lf-button { display: inline-flex; flex: 0 0 auto; max-width: 100%; }
lf-button button {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-4);
  border-radius: var(--r-sm);
  border: 1px solid var(--edge);
  background: var(--surface-2);
  color: var(--text-normal);
  font-family: var(--ff-sans);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  line-height: var(--lh-tight);
  cursor: pointer;
  white-space: nowrap;
  transition: border-color var(--mo-fast) var(--ease),
              background var(--mo-fast) var(--ease),
              box-shadow var(--mo-fast) var(--ease),
              transform var(--mo-fast) var(--ease);
}
lf-button button:hover:not(:disabled) {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--glow);
}
lf-button button:active:not(:disabled) { transform: translateY(1px); }
lf-button button:focus-visible { outline: none; box-shadow: var(--focus-ring); }
lf-button button:disabled { opacity: .5; cursor: not-allowed; }
lf-button button[aria-busy="true"] { cursor: progress; }

lf-button button[data-variant="primary"] {
  background: var(--accent); border-color: var(--accent); color: #fff;
}
lf-button button[data-variant="primary"]:hover:not(:disabled) {
  background: color-mix(in srgb, var(--accent) 88%, #fff 12%);
}
lf-button button[data-variant="ghost"] {
  background: transparent; border-color: transparent; color: var(--text-dim);
}
lf-button button[data-variant="ghost"]:hover:not(:disabled) {
  background: var(--surface-2); color: var(--text-normal);
  box-shadow: none; border-color: transparent;
}
lf-button button[data-variant="danger"] {
  background: color-mix(in srgb, var(--danger) 14%, transparent);
  border-color: var(--danger); color: var(--danger);
}
lf-button button[data-variant="danger"]:hover:not(:disabled) {
  background: color-mix(in srgb, var(--danger) 22%, transparent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 20%, transparent);
}
lf-button button[data-size="sm"] {
  padding: var(--sp-1) var(--sp-3); font-size: var(--fs-xs);
}
lf-button button[data-size="lg"] {
  padding: var(--sp-3) var(--sp-5); font-size: var(--fs-base);
}
/* An icon-only button is square and has a real hit area. The old .iconbtn
   row produced targets well under the 44px touch guidance, which is a large
   part of why the composer felt fiddly on a phone. */
lf-button button:has(.lf-btn-label[style*="none"]) { padding: var(--sp-2); }
lf-button[icon-only] button { min-width: 34px; min-height: 34px; padding: var(--sp-2); }
@media (pointer: coarse) {
  lf-button[icon-only] button { min-width: 44px; min-height: 44px; }
}

/* ---------------------------------------------------------------- lf-chip */
lf-chip {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-3);
  border-radius: var(--r-xs);
  border: 1px solid var(--edge);
  background: var(--surface-2);
  color: var(--text-dim);
  font-family: var(--ff-mono);
  font-size: var(--fs-xs);
  line-height: var(--lh-tight);
}
lf-chip[data-tone="accent"]  { color: var(--accent2); }
lf-chip[data-tone="local"]   { color: var(--local);
  border-color: color-mix(in srgb, var(--local) 45%, var(--edge)); }
lf-chip[data-tone="cloud"]   { color: var(--cloud);
  border-color: color-mix(in srgb, var(--cloud) 45%, var(--edge)); }
lf-chip[data-tone="warn"]    { color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 55%, var(--edge)); }
lf-chip[data-tone="error"]   { color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 55%, var(--edge)); }
lf-chip[data-tone="success"] { color: var(--success);
  border-color: color-mix(in srgb, var(--success) 45%, var(--edge)); }

/* --------------------------------------------------------------- lf-field */
lf-field { display: block; margin-block: var(--sp-3); }
.lf-field-label {
  display: block; margin-bottom: var(--sp-2);
  font-size: var(--fs-xs); font-weight: var(--fw-semi);
  color: var(--text-dim); letter-spacing: .3px;
}
.lf-field-control input, .lf-field-control textarea,
.lf-field-control select {
  width: 100%;
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-sm);
  border: 1px solid var(--edge);
  background: var(--surface-2);
  color: var(--text-normal);
  font-family: var(--ff-sans);
  font-size: var(--fs-base);
  transition: border-color var(--mo-fast) var(--ease),
              box-shadow var(--mo-fast) var(--ease);
}
.lf-field-control input:focus, .lf-field-control textarea:focus,
.lf-field-control select:focus {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--glow);
}
.lf-field-control [aria-invalid="true"] { border-color: var(--danger); }
.lf-field-msg { margin-top: var(--sp-2); font-size: var(--fs-xs);
                color: var(--text-dim); }
.lf-field-msg[data-tone="error"] { color: var(--danger); }

/* ---------------------------------------------------------------- lf-tabs */
lf-tabs { display: block; min-width: 0; }
.lf-tablist {
  display: flex; gap: var(--sp-1); align-items: center;
  min-width: 0; overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
}
.lf-tablist::-webkit-scrollbar { display: none; }
.lf-tablist[data-variant="primary"] {
  background: var(--surface-2); padding: var(--sp-1);
  border-radius: var(--r-sm); border: 1px solid var(--edge);
}
.lf-tab {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  flex: 0 0 auto; white-space: nowrap;
  padding: var(--sp-2) var(--sp-4);
  border: none; background: none; cursor: pointer;
  border-radius: var(--r-xs);
  color: var(--text-dim);
  font-family: var(--ff-sans);
  font-size: var(--fs-sm); font-weight: var(--fw-medium);
  transition: color var(--mo-fast) var(--ease),
              background var(--mo-fast) var(--ease);
}
.lf-tab:hover { color: var(--text-normal); }
.lf-tab:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.lf-tab.is-active { color: var(--text-normal); background: var(--surface); }
.lf-tablist[data-variant="primary"] .lf-tab.is-active { box-shadow: var(--el-1); }
.lf-tablist[data-variant="sub"] .lf-tab.is-active {
  background: var(--surface-2); box-shadow: inset 0 0 0 1px var(--edge);
}
@media (pointer: coarse) { .lf-tab { padding: var(--sp-3) var(--sp-4); } }
.lf-tab-badge {
  min-width: 18px; padding: 0 var(--sp-2);
  border-radius: var(--r-full);
  background: var(--accent); color: #fff;
  font-size: var(--fs-2xs); font-weight: var(--fw-bold);
  text-align: center;
}

/* -------------------------------------------------------------- lf-dialog */
.lf-dialog {
  border: 1px solid var(--edge);
  border-radius: var(--r-lg);
  background: var(--surface);
  color: var(--text-normal);
  padding: 0;
  box-shadow: var(--el-3);
  max-width: min(94vw, 720px);
  width: 100%;
}
.lf-dialog[data-size="sm"] { max-width: min(94vw, 440px); }
.lf-dialog[data-size="lg"] { max-width: min(94vw, 1040px); }
.lf-dialog::backdrop { background: rgba(0, 0, 0, .5);
                       backdrop-filter: blur(2px); }
.lf-dialog-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-5) var(--sp-6);
  border-bottom: 1px solid var(--edge);
}
.lf-dialog-head h2 { font-size: var(--fs-lg); font-weight: var(--fw-semi);
                     margin: 0; }
.lf-dialog-body { padding: var(--sp-6); max-height: 70vh; overflow-y: auto; }

/* --------------------------------------------------------------- lf-empty */
lf-empty {
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: var(--sp-3);
  padding: var(--sp-8) var(--sp-5);
  text-align: center; color: var(--text-dim);
}
lf-empty lf-icon { color: var(--text-dim); opacity: .55; }
.lf-empty-title { font-size: var(--fs-md); font-weight: var(--fw-semi);
                  color: var(--text-normal); }
.lf-empty-body { font-size: var(--fs-sm); max-width: 46ch;
                 line-height: var(--lh-base); }
.lf-empty-actions { display: flex; gap: var(--sp-3); margin-top: var(--sp-3); }

/* ------------------------------------------------------------ lf-skeleton */
.lf-skel-line {
  height: var(--sp-4); margin-bottom: var(--sp-3);
  border-radius: var(--r-xs);
  background: linear-gradient(90deg, var(--surface-2) 25%,
              var(--surface-3) 37%, var(--surface-2) 63%);
  background-size: 400% 100%;
  animation: lf-shimmer 1.4s ease infinite;
}
.lf-skel-line:last-child { width: 62%; }
@keyframes lf-shimmer { 0% { background-position: 100% 50%; }
                        100% { background-position: 0 50%; } }
@media (prefers-reduced-motion: reduce) {
  .lf-skel-line { animation: none; }
}

/* --------------------------------------------------------------- lf-toast */
lf-toasts {
  position: fixed; right: var(--sp-5); bottom: var(--sp-5);
  z-index: var(--z-toast);
  display: flex; flex-direction: column; gap: var(--sp-3);
  pointer-events: none;
}
.lf-toast {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-5);
  border-radius: var(--r-sm);
  border: 1px solid var(--edge);
  background: var(--surface);
  color: var(--text-normal);
  box-shadow: var(--el-2);
  font-size: var(--fs-sm);
  max-width: 42ch;
  pointer-events: auto; cursor: pointer;
  animation: lf-toast-in var(--mo-base) var(--ease-out);
}
.lf-toast[data-tone="error"]   { border-color: var(--danger); color: var(--danger); }
.lf-toast[data-tone="success"] { border-color: var(--success); }
.lf-toast[data-leaving] { opacity: 0; transform: translateY(4px);
                          transition: all var(--mo-base) var(--ease); }
@keyframes lf-toast-in { from { opacity: 0; transform: translateY(8px); }
                         to { opacity: 1; transform: none; } }

/* ---------------------------------------------------------------- the kit
   The /#/_kit gallery route. It is a dev surface, so it stays plain. */
.lf-kit { padding: var(--sp-6); overflow-y: auto; }
.lf-kit h2 { font-size: var(--fs-lg); margin: var(--sp-6) 0 var(--sp-3); }
.lf-kit h2:first-child { margin-top: 0; }
.lf-kit .row { display: flex; flex-wrap: wrap; gap: var(--sp-3);
               align-items: center; margin-bottom: var(--sp-4); }
.lf-kit .icongrid { display: grid; gap: var(--sp-3);
  grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); }
.lf-kit .icongrid figure {
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-2);
  padding: var(--sp-3); border: 1px solid var(--edge);
  border-radius: var(--r-sm); font-size: var(--fs-2xs);
  color: var(--text-dim); text-align: center; word-break: break-all;
}

/* =========================================================================
   rc188 — the composer
   ========================================================================= */
.lf-composer { align-items: flex-end; }

/* ---- attach menu: six toggling icon buttons collapse into one "+" ------- */
.lf-attach { position: relative; flex: 0 0 auto; }
.lf-attach-menu {
  position: absolute; bottom: calc(100% + var(--sp-3)); left: 0;
  min-width: 236px;
  padding: var(--sp-2);
  border: 1px solid var(--edge);
  border-radius: var(--r-md);
  background: var(--surface);
  box-shadow: var(--el-3);
  z-index: var(--z-overlay);
}
.lf-attach-item {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
  cursor: pointer;
}
.lf-attach-item:hover { background: var(--surface-2); }
.lf-attach-item[hidden] { display: none; }
.lf-attach-label {
  font-size: var(--fs-sm); color: var(--text-normal);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* The moved buttons keep working; they just stop competing for the row. */
.lf-attach-btn { flex: 0 0 auto; }

/* ---- slash palette ----------------------------------------------------- */
.lf-slash-item {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  cursor: pointer;
  font-size: var(--fs-sm);
  border-left: 2px solid transparent;
}
.lf-slash-item:hover { background: var(--surface-2); }
.lf-slash-item.is-active {
  background: var(--surface-2);
  border-left-color: var(--accent);
}
.lf-slash-item b { color: var(--text-normal); font-family: var(--ff-mono); }
.lf-slash-desc { color: var(--text-dim); font-size: var(--fs-xs); }

/* ---- the input itself -------------------------------------------------- */
.lf-composer textarea {
  min-height: 44px;
  line-height: var(--lh-base);
  transition: height var(--mo-fast) var(--ease),
              border-color var(--mo-fast) var(--ease),
              box-shadow var(--mo-fast) var(--ease);
}
@media (prefers-reduced-motion: reduce) {
  .lf-composer textarea { transition: border-color var(--mo-fast) linear; }
}

/* =========================================================================
   rc189 — the model bar. Always says who will answer, and why.
   ========================================================================= */
.lf-modelbar {
  display: flex; align-items: center;
  padding: var(--sp-2) var(--sp-6) 0;
  position: relative;
}
.lf-modelbar[hidden] { display: none; }
.lf-modelbar-btn {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-3);
  border: 1px solid transparent; border-radius: var(--r-full);
  background: transparent; cursor: pointer;
  color: var(--text-dim);
  font-family: var(--ff-sans); font-size: var(--fs-xs);
  max-width: 100%; min-width: 0;
  transition: border-color var(--mo-fast) var(--ease),
              background var(--mo-fast) var(--ease);
}
.lf-modelbar-btn:hover { background: var(--surface-2); border-color: var(--edge); }
.lf-modelbar-btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.lf-modelbar-name {
  color: var(--text-normal); font-weight: var(--fw-medium);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lf-modelbar-why { color: var(--text-dim); white-space: nowrap;
                   overflow: hidden; text-overflow: ellipsis; }
.lf-modelbar[data-locality="local"] .lf-modelbar-btn lf-icon:first-child
  { color: var(--local); }
.lf-modelbar[data-locality="cloud"] .lf-modelbar-btn lf-icon:first-child
  { color: var(--cloud); }
/* A fallback or a dead route is not a detail to bury. */
.lf-modelbar[data-reason="fallback"] .lf-modelbar-why { color: var(--warn); }
.lf-modelbar[data-reason="none"] .lf-modelbar-why,
.lf-modelbar[data-reason="unknown"] .lf-modelbar-why { color: var(--danger); }

.lf-modelbar-menu {
  position: absolute; bottom: calc(100% + var(--sp-2));
  left: var(--sp-6); min-width: 280px; max-width: min(92vw, 420px);
  max-height: 52vh; overflow-y: auto;
  padding: var(--sp-2);
  border: 1px solid var(--edge); border-radius: var(--r-md);
  background: var(--surface); box-shadow: var(--el-3);
  z-index: var(--z-overlay);
}
.lf-modelbar-item {
  display: flex; align-items: center; gap: var(--sp-3);
  width: 100%; padding: var(--sp-2) var(--sp-3);
  border: none; background: none; cursor: pointer; text-align: left;
  border-radius: var(--r-sm); color: var(--text-normal);
  font-family: var(--ff-sans); font-size: var(--fs-sm);
}
.lf-modelbar-item:hover { background: var(--surface-2); }
.lf-modelbar-item:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.lf-modelbar-item.is-current { background: var(--surface-2); }
.lf-modelbar-item.is-current .lf-modelbar-item-name { color: var(--accent); }
.lf-modelbar-item.is-dim { opacity: .55; }
.lf-modelbar-item-name { flex: 1; overflow: hidden;
                         text-overflow: ellipsis; white-space: nowrap; }
.lf-modelbar-item-sub { color: var(--text-dim); font-size: var(--fs-xs);
                        white-space: nowrap; }
.lf-modelbar-sep {
  padding: var(--sp-3) var(--sp-3) var(--sp-1);
  font-size: var(--fs-2xs); text-transform: uppercase;
  letter-spacing: .8px; color: var(--text-dim);
}
@media (max-width: 860px) {
  .lf-modelbar { padding-left: var(--sp-4); padding-right: var(--sp-4); }
  .lf-modelbar-menu { left: var(--sp-4); right: var(--sp-4); min-width: 0; }
}

/* =========================================================================
   rc190 — THE OPERATOR SURFACE.

   Eight Console panes shared a chrome that had drifted: 25 tables, cards,
   sections, stat tiles and pills, each carrying its own hardcoded sizes and
   spacing. Individually fine; together they were the reason the Console
   never read as one product.

   These rules re-express that shared chrome in tokens and OVERRIDE the
   originals in index.html — the panes' markup is untouched, so all eight
   move at once with no render function rewritten and nothing to regress.
   The old rules are deleted from index.html in the same release, so there is
   one definition, not two.
   ========================================================================= */

/* ---- tables ------------------------------------------------------------ */
table {
  border-collapse: separate; border-spacing: 0; width: 100%;
  font-size: var(--fs-sm);
  background: var(--surface);
  border: 1px solid var(--edge);
  border-radius: var(--r-md);
  overflow: hidden;
  margin: var(--sp-2) 0 var(--sp-3);
}
th, td {
  text-align: left;
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--edge);
  vertical-align: top;
}
tr:last-child td { border-bottom: none; }
table tr { transition: background var(--mo-fast) var(--ease); }
table tr:hover td {
  background: color-mix(in srgb, var(--surface-2) 60%, transparent);
}
th {
  color: var(--text-dim);
  font-weight: var(--fw-semi);
  text-transform: uppercase;
  font-size: var(--fs-2xs);
  letter-spacing: .8px;
  position: sticky; top: 0;
  background: var(--surface-2);
  z-index: var(--z-base);
}
/* Numeric columns line up. A throughput table where the decimal points wander
   is materially harder to scan, and this pane is read during incidents. */
td.num, th.num, .num { font-variant-numeric: tabular-nums; text-align: right; }

/* ---- panes, sections, cards -------------------------------------------- */
.pad { padding: var(--sp-6) var(--sp-7); overflow-y: auto;
       scroll-behavior: smooth; }
.pad.tidy { max-width: 1080px; }
.sect {
  background: var(--surface); border: 1px solid var(--edge);
  border-radius: var(--r-lg); margin: var(--sp-4) 0;
  box-shadow: var(--el-1); overflow: hidden;
}
.sect > summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  font-size: var(--fs-sm); font-weight: var(--fw-semi);
  color: var(--text-normal); user-select: none;
  transition: background var(--mo-fast) var(--ease);
}
.sect > summary:hover { background: var(--surface-2); }
.sect > summary:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.sect > summary::before {
  content: ""; width: 6px; height: 6px;
  border-right: 2px solid var(--text-dim);
  border-bottom: 2px solid var(--text-dim);
  transform: rotate(-45deg);
  transition: transform var(--mo-base) var(--ease);
  flex: none;
}
.sect[open] > summary::before { transform: rotate(45deg); }
.sect > summary .smeta {
  margin-left: auto; font-size: var(--fs-xs); color: var(--text-dim);
  font-weight: var(--fw-regular);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 46%;
}
.sectbody { padding: var(--sp-2) var(--sp-5) var(--sp-5);
            border-top: 1px solid var(--edge); }
.card {
  background: var(--surface); border: 1px solid var(--edge);
  border-radius: var(--r-lg); padding: var(--sp-5);
  margin: var(--sp-4) 0; box-shadow: var(--el-1); max-width: 1020px;
}
h3 {
  font-size: var(--fs-xs); margin: var(--sp-6) 0 var(--sp-3);
  color: var(--text-dim); text-transform: uppercase;
  letter-spacing: 1.1px; font-weight: var(--fw-bold);
}
h3:first-child { margin-top: 0; }

/* ---- stat tiles --------------------------------------------------------
   These are read at a glance during an incident, so the number is the loud
   part and the label is quiet — the reverse of how they were weighted. */
.statgrid {
  display: grid; gap: var(--sp-4); margin: var(--sp-3) 0 var(--sp-5);
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
}
.stat {
  background: var(--surface); border: 1px solid var(--edge);
  border-radius: var(--r-md); padding: var(--sp-4) var(--sp-5);
  box-shadow: var(--el-1);
}
.stat b {
  font-size: var(--fs-xl); font-weight: var(--fw-bold); display: block;
  line-height: var(--lh-tight); font-variant-numeric: tabular-nums;
}
.stat span { font-size: var(--fs-xs); color: var(--text-dim); }

/* ---- pills, dots, cards grid ------------------------------------------- */
.pill {
  display: inline-block; font-size: var(--fs-2xs);
  padding: var(--sp-1) var(--sp-3); border-radius: var(--r-full);
  font-weight: var(--fw-bold); letter-spacing: .3px;
}
.pill.on { background: color-mix(in srgb, var(--local) 18%, transparent);
           color: var(--local); }
.pill.off { background: color-mix(in srgb, var(--danger) 15%, transparent);
            color: var(--danger); }
.dot { width: 9px; height: 9px; border-radius: var(--r-full);
       display: inline-block; margin-right: var(--sp-2);
       vertical-align: middle; }
.cardgrid {
  display: grid; gap: var(--sp-4); margin: var(--sp-3) 0;
  grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
}
.provcard {
  background: var(--surface); border: 1px solid var(--edge);
  border-radius: var(--r-lg); padding: var(--sp-4) var(--sp-5);
  box-shadow: var(--el-1);
  transition: transform var(--mo-base) var(--ease),
              box-shadow var(--mo-base) var(--ease);
}
.provcard:hover { transform: translateY(-2px); box-shadow: var(--el-2); }
@media (prefers-reduced-motion: reduce) {
  .provcard:hover { transform: none; }
}
.kv { display: grid; grid-template-columns: auto 1fr;
      gap: var(--sp-2) var(--sp-5); font-size: var(--fs-sm); }

/* ---- loading + empty ---------------------------------------------------
   The old .skel was a single grey block; a skeleton should hint at the shape
   of what is coming, so a table pane does not flash a featureless slab. */
.skel {
  position: relative; overflow: hidden;
  background: var(--surface-2); border-radius: var(--r-sm);
  min-height: 64px;
}
.skel::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent,
              color-mix(in srgb, var(--text-normal) 7%, transparent),
              transparent);
  animation: lf-shimmer-x 1.2s infinite;
}
@keyframes lf-shimmer-x { from { transform: translateX(-100%); }
                          to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) { .skel::after { animation: none; } }
.empty-state { text-align: center; padding: var(--sp-8) var(--sp-5);
               color: var(--text-dim); }

/* ---- container queries -------------------------------------------------
   rc190 introduces the mechanism the responsive pass (rc198) builds on: a
   pane adapts to ITS OWN width, not the viewport's. This is what stops the
   Console header from breaking when a drawer narrows the content area —
   the rc174 "server monitor cut off" report, fixed at the cause rather than
   with another viewport breakpoint. */
.pad { container-type: inline-size; container-name: pane; }
@container pane (max-width: 620px) {
  .statgrid { grid-template-columns: repeat(auto-fit, minmax(112px, 1fr));
              gap: var(--sp-3); }
  .cardgrid { grid-template-columns: 1fr; }
  .pad, .card { padding-left: var(--sp-4); padding-right: var(--sp-4); }
  table { font-size: var(--fs-xs); }
  th, td { padding: var(--sp-2) var(--sp-3); }
}

/* ---- lf-files (rc191) ---------------------------------------------------
   ONE file surface. The chat dock and the Console Files tab mount the same
   element and differ only by `density`, so a row cannot look one way in one
   place and another way in the other — which is what they did through rc190
   (raw byte counts vs "1.4 MB", thumbnails vs none, delete vs no delete).   */
.lf-files { display: block; container-type: inline-size; container-name: files; }

.lf-files-bar {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-2) 0 var(--sp-3);
  flex-wrap: wrap;
}
.lf-files-spacer { flex: 1 1 auto; }
.lf-files-count {
  font-size: var(--fs-xs); color: var(--text-dim);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.lf-files-search {
  flex: 0 1 240px; min-width: 120px;
  font: inherit; font-size: var(--fs-sm);
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--edge); border-radius: var(--r-sm);
  background: var(--surface-2); color: var(--text-normal);
}
.lf-files-search:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.lf-files-link {
  font-size: var(--fs-xs); color: var(--accent);
  text-decoration: none; white-space: nowrap;
}
.lf-files-link:hover { text-decoration: underline; }

.lf-files-drop {
  border: 2px dashed var(--accent); border-radius: var(--r-md);
  padding: var(--sp-5); text-align: center;
  font-size: var(--fs-sm); color: var(--accent);
  margin-bottom: var(--sp-3);
}

.lf-files-group { margin: 0 0 var(--sp-4); }
.lf-files-group-head {
  display: flex; align-items: center; gap: var(--sp-2);
  width: 100%; text-align: left;
  background: none; border: 0; cursor: pointer;
  padding: var(--sp-2) var(--sp-2);
  color: var(--text-dim); font: inherit; font-size: var(--fs-xs);
  border-radius: var(--r-sm);
}
.lf-files-group-head:hover { background: var(--surface-2); color: var(--text-normal); }
.lf-files-group-head:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.lf-files-group-name { font-family: var(--ff-mono); }
.lf-files-group-meta {
  margin-left: auto; font-variant-numeric: tabular-nums; opacity: .75;
}

.lf-files-list {
  border: 1px solid var(--edge); border-radius: var(--r-md);
  overflow: hidden; background: var(--surface);
}
.lf-files-row {
  display: flex; align-items: center;
  border-bottom: 1px solid var(--edge);
  transition: background var(--mo-fast) var(--ease);
}
.lf-files-row:last-child { border-bottom: 0; }
.lf-files-row:hover { background: var(--surface-2); }

.lf-files-main {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; gap: var(--sp-3);
  background: none; border: 0; cursor: pointer;
  padding: var(--sp-3) var(--sp-4);
  color: var(--text-normal); font: inherit; font-size: var(--fs-sm);
  text-align: left;
}
.lf-files-main:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.lf-files-thumb {
  flex: 0 0 auto; width: 28px; height: 28px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-xs); overflow: hidden;
  background: var(--surface-3); color: var(--text-dim);
}
.lf-files-thumb img { width: 100%; height: 100%; object-fit: cover; }

.lf-files-name {
  flex: 1 1 auto; min-width: 0;
  font-family: var(--ff-mono); font-size: var(--fs-xs);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lf-files-size {
  flex: 0 0 auto; width: 72px; text-align: right;
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-xs); color: var(--text-dim);
}
.lf-files-when {
  flex: 0 0 auto; width: 72px; text-align: right;
  font-size: var(--fs-xs); color: var(--text-dim);
}

.lf-files-actions {
  flex: 0 0 auto; display: flex; align-items: center;
  gap: var(--sp-1); padding-right: var(--sp-3);
}
.lf-files-act {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border: 0; background: none; cursor: pointer;
  border-radius: var(--r-sm); color: var(--text-dim); text-decoration: none;
}
.lf-files-act:hover { background: var(--surface-3); color: var(--text-normal); }
.lf-files-act.is-danger:hover { color: var(--danger); }
.lf-files-act:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* Preview */
.lf-files-preview-meta {
  display: flex; align-items: center; gap: var(--sp-3);
  font-size: var(--fs-xs); color: var(--text-dim);
  font-family: var(--ff-mono);
  padding-bottom: var(--sp-3); margin-bottom: var(--sp-3);
  border-bottom: 1px solid var(--edge);
  overflow: hidden; text-overflow: ellipsis;
}
.lf-files-preview-meta .lf-files-link { margin-left: auto; }
.lf-files-preview { min-height: 160px; }
.lf-files-preview img {
  max-width: 100%; max-height: 68vh; display: block; margin: 0 auto;
  border-radius: var(--r-sm);
}
.lf-files-preview iframe {
  width: 100%; height: 68vh; border: 1px solid var(--edge);
  border-radius: var(--r-sm); background: var(--surface-2);
}
.lf-files-text {
  margin: 0; max-height: 68vh; overflow: auto;
  font-family: var(--ff-mono); font-size: var(--fs-xs);
  line-height: var(--lh-base); white-space: pre-wrap; word-break: break-word;
  background: var(--surface-2); border: 1px solid var(--edge);
  border-radius: var(--r-sm); padding: var(--sp-4);
}
.lf-files-nopreview {
  text-align: center; padding: var(--sp-8) var(--sp-5);
  color: var(--text-dim); font-size: var(--fs-sm);
}

/* Compact = the chat dock. Same rows, tighter, and the modified column is the
   first thing to go when the dock is narrow — not the file name. */
.lf-files.is-compact .lf-files-main { padding: var(--sp-2) var(--sp-3); }
.lf-files.is-compact .lf-files-thumb { width: 22px; height: 22px; }
.lf-files.is-compact .lf-files-group { margin-bottom: var(--sp-3); }

@container files (max-width: 420px) {
  .lf-files-when { display: none; }
}
@container files (max-width: 300px) {
  .lf-files-size { display: none; }
}

/* ---- rc192: Tasks / Inbox / Review panes --------------------------------
   These three panes share one skeleton — head (a form or a title), body
   (a keyed list), meta — so a status chip means the same thing in all of
   them and a row is the same height wherever you look.                     */
.lf-pane { display: block; }
.lf-pane-head, .lf-pane-body { margin: 0 0 var(--sp-5); }
.lf-pane-title {
  display: flex; align-items: baseline; gap: var(--sp-3);
  margin: 0 0 var(--sp-4); font-size: var(--fs-md);
  font-weight: var(--fw-semi); color: var(--text-normal);
}
.lf-pane-scope, .lf-pane-meta {
  font-size: var(--fs-xs); font-weight: var(--fw-regular);
  color: var(--text-dim); font-variant-numeric: tabular-nums;
}
.lf-pane-meta { margin-left: auto; }
.lf-pane-note {
  margin: var(--sp-3) 0 0; font-size: var(--fs-xs);
  color: var(--text-dim); line-height: var(--lh-base); max-width: 78ch;
}

.lf-task-form { display: flex; gap: var(--sp-4); align-items: stretch; }
.lf-task-prompt, .lf-input {
  flex: 1 1 auto; min-width: 0; font: inherit; font-size: var(--fs-sm);
  line-height: var(--lh-base); padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--edge); border-radius: var(--r-sm);
  background: var(--surface-2); color: var(--text-normal); resize: vertical;
}
.lf-task-prompt:focus-visible, .lf-input:focus-visible {
  outline: none; box-shadow: var(--focus-ring);
}
.lf-task-side {
  flex: 0 0 auto; display: flex; flex-direction: column;
  gap: var(--sp-3); justify-content: flex-start;
}
.lf-task-overnight, .lf-check {
  display: flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-xs); color: var(--text-dim); margin: 0; cursor: pointer;
}

.lf-tasklist, .lf-inboxlist, .lf-schedlist, .lf-blockers {
  display: flex; flex-direction: column; gap: var(--sp-3);
}
.lf-task, .lf-inbox-item, .lf-sched, .lf-blocker {
  border: 1px solid var(--edge); border-radius: var(--r-md);
  background: var(--surface); padding: var(--sp-4);
}
.lf-task-top, .lf-sched-top, .lf-blocker-top {
  display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
}
.lf-task-id {
  font-family: var(--ff-mono); font-size: var(--fs-2xs);
  color: var(--text-dim);
}
.lf-task-badges { display: flex; gap: var(--sp-2); }
.lf-task-badge {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  font-size: var(--fs-2xs); color: var(--text-dim);
  border: 1px solid var(--edge); border-radius: var(--r-full);
  padding: 0 var(--sp-2);
}
.lf-task-acts, .lf-inbox-acts, .lf-sched-acts, .lf-blocker-acts {
  display: flex; gap: var(--sp-2); flex-wrap: wrap;
}
.lf-task-acts { margin-left: auto; }
.lf-task-prompt-text, .lf-sched-prompt {
  margin: var(--sp-3) 0 0; font-size: var(--fs-sm);
  line-height: var(--lh-base); color: var(--text-normal);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.lf-task-result {
  margin: var(--sp-2) 0 0; font-size: var(--fs-xs); color: var(--text-dim);
}
/* A running task should be visibly running, without animation noise. */
.lf-task[data-status="running"] { border-left: 3px solid var(--accent); }
.lf-task[data-status="error"], .lf-task[data-status="interrupted"] {
  border-left: 3px solid var(--danger);
}
.lf-task[data-status="paused"] { border-left: 3px solid var(--warn); }

/* Inbox */
.lf-inbox-item { padding: 0; overflow: hidden; }
.lf-inbox-head {
  display: flex; align-items: center; gap: var(--sp-3); width: 100%;
  background: none; border: 0; cursor: pointer; text-align: left;
  padding: var(--sp-3) var(--sp-4); font: inherit;
  font-size: var(--fs-sm); color: var(--text-normal);
}
.lf-inbox-head:hover { background: var(--surface-2); }
.lf-inbox-head:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.lf-inbox-item.is-unread .lf-inbox-title { font-weight: var(--fw-semi); }
.lf-inbox-item.is-unread { border-left: 3px solid var(--accent); }
.lf-inbox-title {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lf-inbox-when {
  flex: 0 0 auto; font-size: var(--fs-2xs); color: var(--text-dim);
}
.lf-inbox-body {
  margin: 0; padding: var(--sp-4); max-height: 44vh; overflow: auto;
  white-space: pre-wrap; word-break: break-word;
  font-family: var(--ff-mono); font-size: var(--fs-xs);
  line-height: var(--lh-base);
  background: var(--surface-2); border-top: 1px solid var(--edge);
}
.lf-inbox-acts { padding: var(--sp-2) var(--sp-4) var(--sp-3); }
.lf-tone-ok { color: var(--success); }
.lf-tone-warn { color: var(--warn); }

/* Schedules */
.lf-sched-name { font-size: var(--fs-sm); }
.lf-sched-when {
  margin-left: auto; font-size: var(--fs-xs); color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.lf-sched-history {
  margin-top: var(--sp-3); padding-top: var(--sp-3);
  border-top: 1px solid var(--edge);
  font-size: var(--fs-xs); color: var(--text-dim);
}
.lf-sched-run { display: flex; gap: var(--sp-3); align-items: center; }
.lf-schedform { display: flex; flex-direction: column; gap: var(--sp-4); }
.lf-schedform .lf-input { width: 100%; }
.lf-schedform-preview {
  font-size: var(--fs-xs); font-family: var(--ff-mono);
  color: var(--text-dim); padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm); background: var(--surface-2);
}
.lf-schedform-preview[data-ok="0"] { color: var(--danger); }
.lf-dialog-actions {
  display: flex; gap: var(--sp-3); justify-content: flex-end;
  margin-top: var(--sp-2);
}

/* Review */
.lf-blocker { border-left: 3px solid var(--warn); max-width: 980px; }
.lf-blocker-title { font-size: var(--fs-sm); }
.lf-blocker-meta {
  margin-left: auto; font-size: var(--fs-2xs); color: var(--text-dim);
}
.lf-blocker-detail, .lf-blocker-check {
  white-space: pre-wrap; word-break: break-word; margin: var(--sp-3) 0;
  font-family: var(--ff-mono); font-size: var(--fs-xs);
  color: var(--text-dim); background: var(--surface-2);
  padding: var(--sp-3); border-radius: var(--r-sm);
  max-height: 30vh; overflow: auto;
}
.lf-blocker-cause { margin: var(--sp-3) 0; font-size: var(--fs-sm); }
.lf-blocker-options {
  display: flex; gap: var(--sp-3); flex-wrap: wrap; margin: var(--sp-3) 0;
}
.lf-blocker-proposed {
  margin: var(--sp-3) 0 var(--sp-3) var(--sp-6);
  font-size: var(--fs-sm); line-height: var(--lh-base);
}
.lf-blocker-checks { font-size: var(--fs-xs); }
.lf-blocker-checks summary { cursor: pointer; color: var(--accent); }
.lf-blocker-label {
  display: block; margin: var(--sp-4) 0 var(--sp-2);
  font-size: var(--fs-xs); color: var(--text-dim);
}
.lf-blocker-input { width: 100%; }
.lf-blocker-acts { margin-top: var(--sp-3); }

.lf-settled { display: flex; flex-direction: column; }
.lf-settled-row {
  display: grid;
  grid-template-columns: 15em 8em 1fr 7em 1fr;
  gap: var(--sp-3); align-items: center;
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--edge);
  font-size: var(--fs-xs); color: var(--text-dim);
}
.lf-settled-title { color: var(--text-normal); }
.lf-settled-title, .lf-settled-res {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

@container pane (max-width: 620px) {
  .lf-task-form { flex-direction: column; }
  .lf-task-side { flex-direction: row; align-items: center; }
  .lf-settled-row { grid-template-columns: 1fr 1fr; }
  .lf-task-acts { margin-left: 0; width: 100%; }
}

/* ---- rc193: sections, pane filter, log bar ------------------------------
   `sect()` used to emit a raw <details> whose open/closed state died with
   every rebuild — and renderAdmin() is called from EIGHTEEN places. The
   section now remembers itself, so a save no longer collapses your place. */
lf-section { display: block; margin: 0 0 var(--sp-4); }
lf-section[hidden] { display: none; }
.lf-section {
  border: 1px solid var(--edge); border-radius: var(--r-md);
  background: var(--surface); overflow: hidden;
}
.lf-section-head {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); cursor: pointer;
  font-size: var(--fs-sm); font-weight: var(--fw-semi);
  color: var(--text-normal); list-style: none;
  transition: background var(--mo-fast) var(--ease);
}
.lf-section-head::-webkit-details-marker { display: none; }
.lf-section-head:hover { background: var(--surface-2); }
.lf-section-head:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.lf-section-chev {
  flex: 0 0 auto; width: 10px; height: 10px;
  border-right: 2px solid var(--text-dim);
  border-bottom: 2px solid var(--text-dim);
  transform: rotate(-45deg); margin-left: 2px;
  transition: transform var(--mo-fast) var(--ease);
}
.lf-section[open] > .lf-section-head .lf-section-chev { transform: rotate(45deg); }
.lf-section-icon { flex: 0 0 auto; display: inline-flex; align-items: center; }
.lf-section-title { flex: 0 1 auto; }
.lf-section-meta {
  margin-left: auto; font-size: var(--fs-xs);
  font-weight: var(--fw-regular); color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.lf-section-body {
  padding: var(--sp-4) var(--sp-5) var(--sp-5);
  border-top: 1px solid var(--edge);
}

.lf-panefilter {
  display: flex; align-items: center; gap: var(--sp-3);
  margin: 0 0 var(--sp-4); position: sticky; top: 0;
  z-index: var(--z-sticky); background: var(--bg);
  padding: var(--sp-2) 0;
}
.lf-panefilter-input {
  flex: 0 1 320px; min-width: 140px; font: inherit;
  font-size: var(--fs-sm); padding: var(--sp-2) var(--sp-4);
  border: 1px solid var(--edge); border-radius: var(--r-full);
  background: var(--surface-2); color: var(--text-normal);
}
.lf-panefilter-input:focus-visible {
  outline: none; box-shadow: var(--focus-ring);
}
.lf-panefilter-count {
  font-size: var(--fs-xs); color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

.lf-logbar {
  display: flex; align-items: center; gap: var(--sp-4);
  flex-wrap: wrap; margin: 0 0 var(--sp-3);
}
.lf-logbar input[type="search"] {
  flex: 0 1 320px; min-width: 140px; font: inherit;
  font-size: var(--fs-sm); padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--edge); border-radius: var(--r-sm);
  background: var(--surface-2); color: var(--text-normal);
}

/* ---- rc194: the transcript ----------------------------------------------
   The jump-back affordance. It exists because the log no longer drags you
   to the bottom on every token — so there has to be a way back that is
   yours to press rather than the page's to impose. */
/* The chat view is the positioning context for the jump button; the button
   then measures its own offset from the log's bottom edge, because the
   composer changes height as the textarea autosizes. */
#v-chat { position: relative; }
.lf-jump {
  position: absolute; left: 50%; transform: translateX(-50%);
  bottom: var(--sp-5);
  z-index: var(--z-sticky);
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-4);
  border: 1px solid var(--edge-strong); border-radius: var(--r-full);
  background: var(--surface); color: var(--text-normal);
  font: inherit; font-size: var(--fs-xs); cursor: pointer;
  box-shadow: var(--el-2);
  animation: lf-jump-in var(--mo-base) var(--ease-out);
}
.lf-jump:hover { background: var(--surface-2); }
.lf-jump:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.lf-jump[hidden] { display: none; }
@keyframes lf-jump-in {
  from { opacity: 0; transform: translate(-50%, 8px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}
@media (prefers-reduced-motion: reduce) { .lf-jump { animation: none; } }

/* The assistant bubble's content lives in .msgbody so the copy and feedback
   controls are siblings of the text rather than casualties of it. */
.msgbody { min-width: 0; }
.msg.assistant.streaming .msgbody::after {
  content: "▍"; opacity: .55; animation: lf-caret 1s steps(2) infinite;
}
@keyframes lf-caret { 50% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .msg.assistant.streaming .msgbody::after { animation: none; }
}

/* ---- rc195: artifacts in the conversation -------------------------------
   Ask for an image and get one, and through rc194 the chat showed no image:
   the file landed in the workspace and the only trace in the transcript was
   a tool chip inside run chrome that is hidden by default. */
.lf-artifacts {
  display: block; align-self: flex-start; max-width: 100%;
  border: 1px solid var(--edge); border-radius: var(--r-md);
  background: var(--surface); padding: var(--sp-3) var(--sp-4);
}
.lf-artifacts-head {
  display: flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-xs); color: var(--success);
  margin-bottom: var(--sp-3);
}
.lf-artifacts-grid {
  display: flex; flex-wrap: wrap; gap: var(--sp-3);
}
.lf-artifact {
  display: flex; flex-direction: column; gap: var(--sp-2);
  width: 168px; max-width: 100%;
}
.lf-artifact-open {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 108px; padding: 0; cursor: pointer;
  border: 1px solid var(--edge); border-radius: var(--r-sm);
  background: var(--surface-2); color: var(--text-dim); overflow: hidden;
}
.lf-artifact-open:hover { border-color: var(--edge-strong); }
.lf-artifact-open:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.lf-artifact-open img { width: 100%; height: 100%; object-fit: cover; }
.lf-artifact-meta {
  display: grid; grid-template-columns: 1fr auto;
  grid-template-areas: "name dl" "sub dl";
  align-items: center; gap: 0 var(--sp-2);
}
.lf-artifact-name {
  grid-area: name; font-size: var(--fs-xs); color: var(--text-normal);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lf-artifact-sub {
  grid-area: sub; font-size: var(--fs-2xs); color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.lf-artifact-dl {
  grid-area: dl; display: inline-flex; align-items: center;
  justify-content: center; width: 28px; height: 28px;
  border-radius: var(--r-sm); color: var(--text-dim); text-decoration: none;
}
.lf-artifact-dl:hover { background: var(--surface-3); color: var(--text-normal); }

/* The clean-mode run line: one always-visible row that says what the run is
   doing and reveals the detail when pressed. Clean mode used to show
   nothing at all between your message and the answer. */
.lf-runline {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  align-self: flex-start; cursor: pointer;
  border: 1px solid var(--edge); border-radius: var(--r-full);
  background: var(--surface-2); color: var(--text-dim);
  font: inherit; font-size: var(--fs-xs);
  padding: var(--sp-1) var(--sp-4);
}
.lf-runline:hover { border-color: var(--edge-strong); color: var(--text-normal); }
.lf-runline:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.lf-runline[hidden] { display: none; }
.lf-runline-text { font-variant-numeric: tabular-nums; }
.lf-runline-hint { opacity: .7; }
.lf-runline lf-icon { animation: lf-spin 1.1s linear infinite; }
@keyframes lf-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .lf-runline lf-icon { animation: none; }
}

/* ---- rc196: toasts, drawer, one-time secrets ----------------------------
   An error that vanishes in 4.2 seconds with no way to keep it is not a
   notification; it is a rumour. Errors now stay until dismissed, repeats
   collapse into a count, and every toast has a close control. */
#toasts .toast {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  animation: lf-toast-in var(--mo-base) var(--ease-out);
}
#toasts .toast.is-leaving {
  opacity: 0; transform: translateY(4px);
  transition: opacity var(--mo-fast) var(--ease),
              transform var(--mo-fast) var(--ease);
}
#toasts .toast.is-bump { animation: lf-toast-bump var(--mo-base) var(--ease); }
.toast-body { flex: 1 1 auto; min-width: 0; word-break: break-word; }
.toast-count {
  flex: 0 0 auto; font-variant-numeric: tabular-nums;
  font-size: var(--fs-2xs); opacity: .75; align-self: center;
}
.toast-count[hidden] { display: none; }
.toast-close {
  flex: 0 0 auto; width: 20px; height: 20px; line-height: 1;
  border: 0; background: none; cursor: pointer; color: inherit;
  opacity: .6; font-size: 16px; border-radius: var(--r-xs);
}
.toast-close:hover { opacity: 1; }
.toast-close:focus-visible { outline: none; box-shadow: var(--focus-ring); }
@keyframes lf-toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes lf-toast-bump {
  0% { transform: scale(1); } 40% { transform: scale(1.03); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  #toasts .toast, #toasts .toast.is-bump { animation: none; }
}

.lf-once-warn {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-4);
  border: 1px solid var(--warn); border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--warn) 10%, transparent);
  font-size: var(--fs-sm); color: var(--text-normal);
}
.lf-once-row { display: flex; gap: var(--sp-3); align-items: center; }
.lf-once-value { flex: 1 1 auto; font-family: var(--ff-mono); }

/* ---- rc197: the auth gate -----------------------------------------------
   Small things, on the one screen everybody sees. */
#authGate form { display: contents; }
.lf-pw { position: relative; display: block; }
.lf-pw input { width: 100%; padding-right: 40px; }
.lf-pw-eye {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  width: 28px; height: 28px; display: inline-flex;
  align-items: center; justify-content: center;
  border: 0; background: none; cursor: pointer;
  color: var(--text-dim); border-radius: var(--r-sm);
}
.lf-pw-eye:hover { color: var(--text-normal); background: var(--surface-2); }
.lf-pw-eye:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.lf-pw-eye[aria-pressed="true"] { color: var(--accent); }
/* Caps Lock is the most common cause of "the password is wrong", and
   nothing said so. */
.lf-caps {
  display: flex; align-items: center; gap: var(--sp-2);
  margin-top: var(--sp-2); font-size: var(--fs-xs); color: var(--warn);
}
.lf-caps[hidden] { display: none; }
#authGate .err:empty { display: none; }
#authGate button[aria-busy="true"] { opacity: .75; cursor: progress; }

/* ---- rc198: responsive + density ----------------------------------------
   Measured, not guessed: both viewports were loaded in a real browser and
   every element outside them was listed. See forge/ui/density.js.          */

/* 4K — THE CENTRED MEASURE.
   `.msg` caps at 900px but #chatlog did not, so on a 3840px screen the log
   was 3568px wide: a user message pinned to the far right, its reply to the
   far left, a metre apart. Padding the column rather than capping it keeps
   every child's own alignment (user right, assistant left) inside a
   readable measure. */
:root { --measure: 1180px; --gutter: var(--sp-7); }
#chatlog, #composer, #nowstrip, #attstrip, #chatbar, #filesPanel {
  padding-inline: max(var(--gutter), calc((100% - var(--measure)) / 2));
}
#chatlog > lf-artifacts,
#chatlog > .lf-runline { align-self: flex-start; }

/* The Console sub-nav scrolls (rc174) but gave no sign that it does, so
   Admin, Files and Audit sat off the right edge of a phone with nothing to
   suggest they existed. */
.subtabs {
  -webkit-mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 24px),
                                      transparent 100%);
  mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 24px),
                              transparent 100%);
  scrollbar-width: none;
}
.subtabs::-webkit-scrollbar { display: none; }

/* Touch. The 390px sweep found nine controls under 32px tall, one of them
   15px. A coarse pointer has no pixel precision; these are the platform
   floor, applied only where the pointer actually is coarse so a mouse-driven
   desktop keeps its density. */
@media (pointer: coarse) {
  .hbtn, .btn, .iconbtn, .addbtn, .subtab, .tab,
  .toast-close, .lf-files-act, .lf-pw-eye {
    min-height: 40px; min-width: 40px;
  }
  .filelink, .lf-files-link { display: inline-block; padding: var(--sp-2) 0; }
  .example-card { min-height: 56px; }
  #input { font-size: 16px; }     /* iOS zooms anything smaller on focus */
}

/* Density. One 8pt scale (rc185) means this is five variables, not six
   hundred rules. */
:root[data-density="compact"] {
  --sp-3: 6px;  --sp-4: 9px;   --sp-5: 12px;
  --sp-6: 18px; --sp-7: 24px;  --sp-8: 34px;
  --lh-base: 1.45;
  --gutter: var(--sp-6);
}
:root[data-density="compact"] .lf-files-main { padding-block: var(--sp-2); }
:root[data-density="compact"] th,
:root[data-density="compact"] td { padding: var(--sp-2) var(--sp-3); }
.lf-density-btn[data-density="compact"] { color: var(--accent); }

/* Narrow: the measure collapses to the gutter, and the chat column uses the
   full width rather than paying for centring it. */
@media (max-width: 700px) {
  :root { --gutter: var(--sp-4); }
  .lf-artifact { width: 132px; }
  .lf-artifact-open { height: 88px; }
  .lf-settled-row { grid-template-columns: 1fr; gap: var(--sp-1); }
  .lf-files-size, .lf-files-when { display: none; }
  .lf-jump { max-width: calc(100% - var(--sp-6)); }
}

/* ---- rc199: focus, motion, accessibility --------------------------------
   The audit walked the real DOM and found FIFTY-FIVE interactive elements
   with no visible focus ring — every tab, every Console sub-tab, the new-chat
   button, the details toggle, the model select. Tab through the app with a
   keyboard and you could not tell where you were.

   :focus-visible, not :focus — a mouse click must not leave a ring behind.  */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible,
[role="button"]:focus-visible,
.tab:focus-visible, .subtab:focus-visible,
.hbtn:focus-visible, .btn:focus-visible, .iconbtn:focus-visible,
.addbtn:focus-visible, .filelink:focus-visible,
.example-card:focus-visible, .stepgroup > summary:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--r-sm);
  position: relative;
  z-index: var(--z-sticky);
}
/* Elements the app drives focus into programmatically (the content region
   after a skip, a drawer with no focusable child) should not flash a ring at
   a mouse user who never asked for it. */
#content:focus, #content:focus-visible { outline: none; }

/* The skip link: a keyboard user tabbed through the entire header and
   sidebar to reach the content, on every view, every time. */
.lf-skip {
  position: fixed; top: var(--sp-3); left: var(--sp-3);
  z-index: calc(var(--z-toast) + 1);
  padding: var(--sp-3) var(--sp-5);
  background: var(--surface); color: var(--text-normal);
  border: 1px solid var(--edge-strong); border-radius: var(--r-sm);
  box-shadow: var(--el-2); text-decoration: none;
  font-size: var(--fs-sm);
  transform: translateY(-200%);
  transition: transform var(--mo-fast) var(--ease);
}
.lf-skip:focus { transform: translateY(0); }

/* Motion. index.html carried twelve `animation:` uses behind ONE
   reduced-motion guard; the new layer honours it everywhere. This covers the
   legacy ones in one place rather than editing twelve rules. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
  /* A spinner that never moves reads as "frozen", so the one piece of
     motion that carries meaning is kept — slowed, not stopped. */
  .lf-runline lf-icon, .lf-spin {
    animation-duration: 2.4s !important;
    animation-iteration-count: infinite !important;
  }
}

/* ---- rc204: voice status + audio unlock ---------------------------------
   The instrument for "why do I never hear a reply". Every failure is
   visible; the speak-test proves the whole path end to end. */
.lf-voicepanel { display: block; }
.lf-voicepanel-body { display: flex; flex-direction: column; gap: var(--sp-2); }
.lf-voicecap {
  display: flex; align-items: center; gap: var(--sp-3);
  font-size: var(--fs-sm);
  flex-wrap: wrap;           /* rc214 — the note drops below on a narrow screen */
}
.lf-voicecap-note { min-width: 0; }
/* A long endpoint URL wraps instead of widening the whole panel (rc214). */
.lf-voice-ep-url { word-break: break-all; min-width: 0; }
.lf-voice-ep { min-width: 0; }
.lf-voicecap.is-ok { color: var(--success); }
.lf-voicecap.is-off { color: var(--text-dim); }
.lf-voicecap-label { color: var(--text-normal); }
.lf-voicecap-note {
  margin-left: auto; font-size: var(--fs-xs); color: var(--text-dim);
}
.lf-voice-ep {
  border: 1px solid var(--edge); border-radius: var(--r-sm);
  padding: var(--sp-2) var(--sp-3); margin-top: var(--sp-2);
  font-size: var(--fs-xs);
}
.lf-voice-ep.is-bad { border-left: 3px solid var(--danger); }
.lf-voice-ep.is-good { border-left: 3px solid var(--success); }
.lf-voice-ep-url { font-family: var(--ff-mono); }
.lf-voice-ep-status { margin-left: var(--sp-3); color: var(--text-dim); }
.lf-voice-ep-detail {
  margin-top: var(--sp-1); color: var(--danger);
  font-family: var(--ff-mono); word-break: break-word;
}
.lf-voicepanel-actions {
  display: flex; gap: var(--sp-3); margin: var(--sp-4) 0 var(--sp-3);
  flex-wrap: wrap;           /* rc214 — wrap on a phone rather than overlap */
}
.lf-voicepanel-result {
  display: flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-sm); min-height: var(--sp-6);
}
.lf-voicepanel-result.is-ok { color: var(--success); }
.lf-voicepanel-result.is-fail, .lf-voicepanel-result.is-playfail { color: var(--danger); }
.lf-voicepanel-result.is-blocked { color: var(--warn); }

.audiounlock {
  align-self: center; display: flex; align-items: center; gap: var(--sp-3);
  margin: var(--sp-4) auto; padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--warn); border-radius: var(--r-md);
  background: color-mix(in srgb, var(--warn) 10%, transparent);
  font-size: var(--fs-sm); max-width: 640px;
}

/* ---- rc205: hands-free converse — live mic level ------------------------
   The mic button glows with your voice while VAD is listening, so you can
   see it is hearing you. --mic-level is set per animation frame by the VAD
   level callback. */
#micbtn[data-vad] {
  box-shadow: 0 0 0 calc(2px + var(--mic-level, 0) * 8px)
              color-mix(in srgb, var(--accent) calc(var(--mic-level, 0) * 60%),
                        transparent);
  transition: box-shadow 60ms linear;
}
#micbtn[data-vad="speaking"] { color: var(--accent); }
@media (prefers-reduced-motion: reduce) {
  #micbtn[data-vad] { transition: none; }
}

/* ---- rc207: interruptible converse — hard-mute + mic-armed indicator -----
   A live microphone must ALWAYS be visible. #micArmed is a persistent badge
   driven SOLELY by the mic monitor (voice/bargein.js): it is shown iff a
   capture is live and not muted, so it cannot lie about the mic. The mute
   button turns danger-toned and shows a slashed mic while muted. */
#mutebtn.on {
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 14%, transparent);
}
.mic-armed {
  position: fixed;
  top: 12px; right: 12px;
  z-index: 1200;
  display: none;               /* JS shows it only when the mic is live/muted */
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1;
  box-shadow: 0 1px 4px rgba(0,0,0,.3);
  pointer-events: none;        /* informs only — never intercepts a click */
  user-select: none;
}
.mic-armed lf-icon { width: 12px; height: 12px; }
.mic-armed.is-live {
  color: #fff;
  background: var(--danger);   /* the universal "recording" red */
}
.mic-armed.is-live lf-icon { animation: micArmedPulse 1.4s ease-in-out infinite; }
.mic-armed.is-muted {
  color: var(--muted, #888);
  background: color-mix(in srgb, var(--muted, #888) 16%, var(--panel, #111));
  border: 1px solid color-mix(in srgb, var(--muted, #888) 30%, transparent);
}
@keyframes micArmedPulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .35; }
}
@media (prefers-reduced-motion: reduce) {
  .mic-armed.is-live lf-icon { animation: none; }
}

/* ---- rc208: the voice picker (choose / preview / save a voice) -----------*/
.lf-voicepick { margin-top: var(--sp-3, 12px); }
.lf-voicepick-title {
  font-size: 12px; font-weight: 600; color: var(--muted, #888);
  margin-bottom: 6px; text-transform: uppercase; letter-spacing: .04em;
}
.lf-voicepick-row {
  display: flex; align-items: center; gap: var(--sp-2, 8px); flex-wrap: wrap;
}
.lf-voicepick-sel {
  min-width: 180px; padding: 6px 8px; border-radius: var(--radius, 8px);
  border: 1px solid var(--border, #333); background: var(--panel, #1a1a1a);
  color: var(--fg, #eee); font: inherit; font-size: 13px;
}
.lf-voicepick-rate { flex: 0 0 120px; accent-color: var(--accent); }

/* ---- rc219: media quality panel + toast hygiene --------------------------*/
.quality-panel {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 8px;
  z-index: 60;
  min-width: 240px;
  max-width: min(92vw, 340px);
  padding: 12px 14px;
  border-radius: 12px;
  border: 1px solid var(--border, #333);
  background: var(--panel, #16181d);
  box-shadow: 0 8px 30px rgba(0,0,0,.45);
}
.qp-title { font-size: 12px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted, #999); margin-bottom: 8px; }
.qp-row { display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin: 8px 0; font-size: 13px; }
.qp-row select { flex: 0 1 62%; min-width: 0; padding: 6px 8px;
  border-radius: 8px; border: 1px solid var(--border, #333);
  background: var(--panel2, #1c1f26); color: var(--text, #eee); font: inherit; }
.qp-note { font-size: 11.5px; line-height: 1.45; color: var(--muted, #999);
  margin-top: 8px; max-width: 34ch; }
/* toasts: never a wall — narrower, capped (JS caps count), calmer on phones */
#toasts .toast { max-width: min(86vw, 420px); word-break: break-word; }
#toasts .toast.bump { animation: toastbump .18s ease-out; }
@keyframes toastbump { 50% { transform: scale(1.04); } }
@media (max-width: 720px) {
  #toasts .toast { font-size: 13px; padding: 9px 12px; }
}

/* ---- rc216: the voice readout — diagnosis without a console --------------*/
.vad-readout {
  position: fixed;
  left: max(8px, env(safe-area-inset-left, 0px));
  right: max(8px, env(safe-area-inset-right, 0px));
  bottom: max(8px, env(safe-area-inset-bottom, 0px));
  z-index: 1300;
  padding: 6px 10px;
  border-radius: 8px;
  background: rgba(0,0,0,.82);
  color: #cfe8ff;
  font-family: var(--ff-mono, monospace);
  font-size: 11px;
  line-height: 1.4;
  pointer-events: none;      /* never intercepts a tap */
  word-break: break-word;
}

/* ---- rc212: capability hints — readable, not a wall of red ----------------*/
.lf-voice-hint {
  margin: 2px 0 0 2px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--muted, #9a9a9a);
  max-width: 70ch;                 /* a paragraph, not a banner */
}
.lf-voice-hint.is-ok { color: var(--success, #3fa34d); }

/* ---- rc209: a node running old shim code says so --------------------------*/
.lf-voice-ep-stale {
  margin-top: 4px; font-size: 12px; font-weight: 600;
  color: var(--warn, #d98b00);
}

/* ---- rc210: platforms — voice controls usable on a phone -----------------
   Two real problems on a handset: the voice buttons are below the 44px touch
   target every mobile HIG asks for, and the fixed mic-armed badge sits exactly
   where iOS Safari puts its own UI. Both are fixed here rather than by
   shrinking the desktop layout. */
@media (max-width: 720px), (pointer: coarse) {
  #micbtn, #voicebtn, #conversebtn, #mutebtn {
    min-width: 44px; min-height: 44px;   /* real thumb targets */
  }
  .mic-armed {
    /* clear of the iOS status bar / notch, and out of the composer's way */
    top: max(10px, env(safe-area-inset-top, 0px));
    right: max(10px, env(safe-area-inset-right, 0px));
    font-size: 13px;
    padding: 7px 12px;
  }
  .lf-voicepick-row { gap: 10px; }
  .lf-voicepick-sel { flex: 1 1 100%; min-width: 0; }
  .lf-voicepick-rate { flex: 1 1 100%; }
  .audiounlock { font-size: 14px; }
}

/* rc223 — the video player card: the rendered clip plays in the transcript
   where it landed. Bounded so a 704x480 clip never dominates the chat
   column; the mobile gate (rc215) keeps it inside the viewport. */
.vid-card {
  display: block;
  width: 100%;
  max-width: 440px;
  margin: 6px 0 2px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: #000;
}
@media (max-width: 860px) {
  .vid-card { max-width: 100%; }
}
