/* ============================================================
   PANEL: only what base.css does not already have.
   Every value is a --lp-* token. Nothing here restyles a base.css
   component; the panel reuses .sidebar .nav-item .topbar .content-header
   .metric-tile .affiliate-panel .affiliate-table .btn .filter-chip
   .empty-state .modal as they ship.
   ============================================================ */

/* base.css styles .nav-item for a <div>; here the four surfaces are real links,
   so the browser's underline has to go and the colour must stay the token's. */
a.nav-item, a.nav-item:hover, a.nav-item:visited { text-decoration: none; }
a.entry-card, a.entry-card:hover, a.entry-card:visited { text-decoration: none; }

/* --- page frame ------------------------------------------------------ */

.panel-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--lp-bg-base);
  /* base.css declares --lp-leading-* and uses them on its own components, but
     never sets a leading for the page, so every block the panel adds inherited
     the browser default of about 1.2: measured 13.2px line boxes on 11px text
     and 15.6px on 13px. Vietnamese stacks a dấu above the x-height and a hook
     below it, so at 1.2 the mark of one line meets the ascender of the next.
     A unitless value so each element computes leading from its own size. */
  line-height: var(--lp-leading-snug);
}

/* Small text needs proportionally MORE leading than body text, not less: the
   marks are the same shape at 11px as at 13px while the space between the lines
   shrank. These are the panel blocks that carry whole sentences at 10-11px. */
.kpi-sub,
.attention-row__desc,
.cell-stack__sub,
.entry-card__desc,
.field__note,
.funnel__drop,
.serp__url,
.thread-end,
.chat-msg__prompt summary {
  line-height: var(--lp-leading-normal);
}

.panel-scroll {
  flex: 1;
  overflow-y: auto;
  padding: var(--lp-space-20);
  padding-bottom: var(--lp-space-32);
  display: flex;
  flex-direction: column;
  gap: var(--lp-space-20);
}

.panel-section {
  display: flex;
  flex-direction: column;
  gap: var(--lp-space-12);
}

/* base.css puts the card's inset on .affiliate-panel__header and __body, so a
   note written as a DIRECT child of the card had none: measured 1px from the
   1px border, while every sibling in the same card sat 20px in. Give it the
   inset the card's own body uses rather than a new number. */
.affiliate-panel > .field__note,
.affiliate-panel > .funnel__drop {
  padding: var(--lp-space-12) var(--lp-space-20);
}

/* --- KPI row --------------------------------------------------------- */

.kpi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--lp-space-12);
}

/* base.css .metric-tile sits on --lp-bg-base; on the page ground it needs a
   stroke to keep depth by surface step, per composition law 2. */
.kpi-grid .metric-tile {
  background: var(--lp-bg-surface);
  border: var(--lp-border-thin) solid var(--lp-stroke-default);
  border-radius: var(--lp-radius-m);
  padding: var(--lp-space-16);
}

.kpi-label {
  font-size: var(--lp-text-xs);
  color: var(--lp-text-tertiary);
  /* NOT uppercase: Vietnamese in caps carries its diacritics badly and reads
     slower, and the tired-phone reader is the floor this label serves. */
  display: flex;
  align-items: center;
  gap: var(--lp-space-4);
}

.kpi-sub {
  font-size: var(--lp-text-xs);
  color: var(--lp-text-tertiary);
  margin-top: var(--lp-space-4);
}

/* The ONE accent per view: the single number the surface exists to answer. */
.kpi-focus .metric-tile__value { color: var(--lp-accent-text); }

/* --- state pills: semantic colour plus form, never the accent -------- */

.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--lp-space-4);
  height: 20px;
  padding: 0 var(--lp-space-8);
  border-radius: var(--lp-radius-full);
  font-size: var(--lp-text-xs);
  font-weight: var(--lp-font-medium);
  white-space: nowrap;
  border: var(--lp-border-thin) solid var(--lp-stroke-default);
  color: var(--lp-text-secondary);
}
.pill::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: var(--lp-radius-full);
  background: currentColor;
}
/* Text takes the darker text-only token, the border keeps the original so the
   pill's outline colour does not shift (measured 2026-09-06: as text these sat
   at 3.46 / 2.92 / 4.43 against the light surface, under the 4.5 they need). */
.pill--success { color: var(--lp-success-text); border-color: var(--lp-success); }
.pill--warning { color: var(--lp-warning-text); border-color: var(--lp-warning); }
.pill--error   { color: var(--lp-error-text);   border-color: var(--lp-error); }

/* --- tables ---------------------------------------------------------- */

.panel-table-wrap { overflow-x: auto; }

.panel-table { min-width: 640px; }

.panel-table tbody tr[role="button"] { cursor: pointer; }

.panel-table tbody tr[role="button"]:hover {
  background: var(--lp-wash-subtle);
}

.panel-table tbody tr[role="button"]:focus-visible {
  outline: none;
  box-shadow: var(--lp-focus-ring);
}

.panel-table td.num, .panel-table th.num { text-align: right; font-variant-numeric: tabular-nums; }

.panel-table .cell-strong { color: var(--lp-text-primary); }

.cell-stack { display: flex; flex-direction: column; gap: 2px; }
.cell-stack__sub { font-size: var(--lp-text-xs); color: var(--lp-text-tertiary); }

/* --- tabs ------------------------------------------------------------ */

.panel-tabs {
  display: flex;
  gap: var(--lp-space-4);
  border-bottom: 1px solid var(--lp-stroke-default);
}

.panel-tab {
  appearance: none;
  border: none;
  background: transparent;
  color: var(--lp-text-secondary);
  font: inherit;
  font-size: var(--lp-text-base);
  padding: var(--lp-space-8) var(--lp-space-12);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color var(--lp-motion-fast) var(--lp-ease-out),
              border-color var(--lp-motion-fast) var(--lp-ease-out);
}
.panel-tab:hover { color: var(--lp-text-primary); }
.panel-tab:focus-visible { outline: none; box-shadow: var(--lp-focus-ring); }
.panel-tab--active { color: var(--lp-text-primary); border-bottom-color: var(--lp-accent-base); }

.chip-row { display: flex; flex-wrap: wrap; gap: var(--lp-space-8); align-items: center; }

/* The label of a chip row sat 8px from the first chip, the same 8px the chips
   sit from each other, so it read as one more chip. Only the leading label: the
   pager puts a .kpi-label BETWEEN two buttons, where a one-sided gap would lean. */
.chip-row > .kpi-label:first-child { margin-right: var(--lp-space-4); }

/* base.css .input fills its container; in a chip row it must sit beside the
   button, not push it onto its own line */
.chip-row .input { flex: 0 1 320px; min-width: 180px; width: auto; }

/* --- attention list: every row ends in an action --------------------- */

.attention-row {
  display: flex;
  align-items: center;
  gap: var(--lp-space-12);
  padding: var(--lp-space-12) var(--lp-space-16);
  border-bottom: 1px solid var(--lp-stroke-default);
}
.attention-row:last-child { border-bottom: none; }

.attention-row__stripe {
  width: 3px;
  align-self: stretch;
  border-radius: var(--lp-radius-full);
  background: var(--lp-stroke-light);
  flex-shrink: 0;
}
.attention-row--error .attention-row__stripe   { background: var(--lp-error); }
.attention-row--warning .attention-row__stripe { background: var(--lp-warning); }
.attention-row--success .attention-row__stripe { background: var(--lp-success); }

.attention-row__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.attention-row__title { font-size: var(--lp-text-base); color: var(--lp-text-primary); }
.attention-row__desc  { font-size: var(--lp-text-xs);  color: var(--lp-text-tertiary); overflow-wrap: anywhere; }
.attention-row__action { flex-shrink: 0; }
.attention-row__note { font-size: var(--lp-text-xs); color: var(--lp-text-tertiary); white-space: nowrap; }

/* --- revenue chart --------------------------------------------------- */

.chart {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 120px;
  padding: var(--lp-space-8) 0;
}

.chart__bar {
  flex: 1;
  min-width: 2px;
  border-radius: var(--lp-radius-xs) var(--lp-radius-xs) 0 0;
  background: var(--lp-stroke-light);
  transition: background var(--lp-motion-fast) var(--lp-ease-out);
  align-self: flex-end;
}
.chart__bar:hover { background: var(--lp-accent-base); }
.chart__bar--peak { background: var(--lp-accent-base); }

.chart__axis {
  display: flex;
  justify-content: space-between;
  font-size: var(--lp-text-2xs);
  color: var(--lp-text-tertiary);
  margin-top: var(--lp-space-4);
}

/* --- funnel ---------------------------------------------------------- */

.funnel { display: flex; flex-direction: column; gap: var(--lp-space-12); }

.funnel__step { display: flex; flex-direction: column; gap: var(--lp-space-4); }

.funnel__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: var(--lp-text-base);
  color: var(--lp-text-secondary);
}

.funnel__value { color: var(--lp-text-primary); font-weight: var(--lp-font-semibold); }

.funnel__track {
  height: 10px;
  border-radius: var(--lp-radius-full);
  background: var(--lp-bg-input);
  overflow: hidden;
}

.funnel__fill {
  height: 100%;
  border-radius: var(--lp-radius-full);
  /* a stroke token disappeared into the track on the dark ground, which hid the
     very drop-off the funnel exists to show; a text-weight token reads on both */
  background: var(--lp-text-tertiary);
  transition: width var(--lp-motion-slow) var(--lp-ease-out);
}
.funnel__fill--last { background: var(--lp-accent-base); }

.funnel__drop {
  font-size: var(--lp-text-xs);
  color: var(--lp-text-tertiary);
  /* Not a gap: a return sweep. This block ran the full content width, measured
     1160px at 1440 wide, about 180 characters a line against the 50-75 the
     sensory floor asks for, and no amount of leading fixes a line the eye
     cannot find its way back along. */
  max-width: 78ch;
}

/* --- sidebar footer + header bits ------------------------------------ */

.sidebar__nav { display: flex; flex-direction: column; gap: var(--lp-space-4); }

.sidebar__spacer { flex: 1; }

.user-chip {
  display: flex;
  align-items: center;
  gap: var(--lp-space-8);
  padding: var(--lp-space-8);
  border-radius: var(--lp-radius-s);
  min-width: 0;
}

.user-chip__avatar {
  width: 28px;
  height: 28px;
  border-radius: var(--lp-radius-full);
  background: var(--lp-accent-base);
  color: var(--lp-text-on-accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--lp-text-sm);
  font-weight: var(--lp-font-semibold);
  flex-shrink: 0;
}

.user-chip__text { display: flex; flex-direction: column; min-width: 0; }
.user-chip__name { font-size: var(--lp-text-base); color: var(--lp-text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.user-chip__role { font-size: var(--lp-text-xs); color: var(--lp-text-tertiary); }

.sidebar--collapsed .user-chip__text,
.sidebar--collapsed .nav-item__content span,
.sidebar--collapsed .sidebar__brand-text { display: none; }

.sidebar__brand { display: flex; align-items: center; gap: var(--lp-space-8); min-width: 0; }
.sidebar__brand-mark {
  width: 24px; height: 24px; border-radius: var(--lp-radius-s);
  background: var(--lp-accent-base); color: var(--lp-text-on-accent);
  display: flex; align-items: center; justify-content: center;
  font-size: var(--lp-text-sm); font-weight: var(--lp-font-semibold); flex-shrink: 0;
}
.sidebar__brand-text { font-size: var(--lp-text-md); font-weight: var(--lp-font-semibold); white-space: nowrap; }

/* --- bell popover ---------------------------------------------------- */

.bell { position: relative; }

.bell__dot {
  position: absolute;
  top: 6px; right: 8px;
  width: 7px; height: 7px;
  border-radius: var(--lp-radius-full);
  background: var(--lp-error);
}

.notif-popover {
  position: absolute;
  top: calc(100% + var(--lp-space-8));
  right: 0;
  width: 340px;
  max-width: calc(100vw - var(--lp-space-24));
  background: var(--lp-bg-surface);
  border: var(--lp-border-thin) solid var(--lp-stroke-default);
  border-radius: var(--lp-radius-m);
  box-shadow: var(--lp-shadow-m);
  z-index: var(--lp-z-popover);
  overflow: hidden;
}

.notif-popover__header {
  padding: var(--lp-space-12) var(--lp-space-16);
  border-bottom: 1px solid var(--lp-stroke-default);
  font-size: var(--lp-text-base);
  color: var(--lp-text-primary);
}

.notif-popover__list { max-height: 320px; overflow-y: auto; }

/* --- misc ------------------------------------------------------------ */

.panel-loading {
  padding: var(--lp-space-32);
  text-align: center;
  color: var(--lp-text-tertiary);
  font-size: var(--lp-text-base);
}

.panel-error {
  padding: var(--lp-space-16) var(--lp-space-20);
  border-radius: var(--lp-radius-m);
  border: var(--lp-border-thin) solid var(--lp-error);
  color: var(--lp-error);
  font-size: var(--lp-text-base);
}

.kv { display: grid; grid-template-columns: 150px 1fr; gap: var(--lp-space-8) var(--lp-space-16); font-size: var(--lp-text-base); }
.kv dt { color: var(--lp-text-tertiary); }
.kv dd { color: var(--lp-text-primary); margin: 0; overflow-wrap: anywhere; }

.entry-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--lp-space-12); }

.entry-card {
  display: flex; flex-direction: column; gap: var(--lp-space-4);
  padding: var(--lp-space-16);
  border-radius: var(--lp-radius-m);
  border: var(--lp-border-thin) solid var(--lp-stroke-default);
  background: var(--lp-bg-surface);
  color: var(--lp-text-secondary);
  text-decoration: none;
  transition: background var(--lp-motion-fast) var(--lp-ease-out),
              border-color var(--lp-motion-fast) var(--lp-ease-out);
}
.entry-card:hover { background: var(--lp-wash-subtle); border-color: var(--lp-stroke-light); }
.entry-card:focus-visible { outline: none; box-shadow: var(--lp-focus-ring); }
.entry-card__title { font-size: var(--lp-text-md); color: var(--lp-text-primary); font-weight: var(--lp-font-medium); }
.entry-card__desc { font-size: var(--lp-text-xs); color: var(--lp-text-tertiary); }
/* A card's count line ("13 đang bật trên 15") is a different fact from the
   description above it, but the card's single 4px gap put all three lines at one
   spacing, so the count read as the last sentence of the description. */
.entry-card__desc + .entry-card__desc { margin-top: var(--lp-space-4); }

/* --- phone: the glance layer must survive a tired thumb -------------- */

@media (max-width: 900px) {
  .kv { grid-template-columns: 1fr; gap: var(--lp-space-4); }
}

@media (max-width: 768px) {
  .sidebar {
    position: fixed;
    left: 0; top: 0; bottom: 0;
    z-index: var(--lp-z-modal);
    transform: translateX(-100%);
    transition: transform var(--lp-motion-mid) var(--lp-ease-out);
  }
  .sidebar--open { transform: translateX(0); }
  .panel-scroll { padding: var(--lp-space-12); }
  .kpi-grid { grid-template-columns: repeat(2, 1fr); }
  .topbar__toggle-mobile { display: flex; }
}

@media (prefers-reduced-motion: reduce) {
  .funnel__fill, .chart__bar, .sidebar { transition: none; }
}

/* --- search-result preview: the channel the reader actually meets ---- */

.serp {
  padding: var(--lp-space-16);
  border-radius: var(--lp-radius-m);
  border: var(--lp-border-thin) solid var(--lp-stroke-default);
  background: var(--lp-bg-base);
  display: flex;
  flex-direction: column;
  gap: var(--lp-space-4);
}
.serp__url   { font-size: var(--lp-text-xs); color: var(--lp-text-tertiary); }
.serp__title { font-size: var(--lp-text-lg); color: var(--lp-accent-base); }
.serp__desc  { font-size: var(--lp-text-base); color: var(--lp-text-secondary); }
.serp__title--missing,
.serp__desc--missing { color: var(--lp-text-tertiary); font-style: italic; }

.miss-list {
  margin: 0;
  padding-left: var(--lp-space-20);
  font-size: var(--lp-text-base);
  color: var(--lp-warning);
  display: flex;
  flex-direction: column;
  gap: var(--lp-space-4);
}

.cell-muted { color: var(--lp-text-tertiary); font-style: italic; }

.cell-clamp {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  max-width: 42ch;
}

/* --- config screens: forms, the only place this panel writes ---------- */

.field-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--lp-space-16);
}

.field { display: flex; flex-direction: column; gap: var(--lp-space-4); min-width: 0; }
.field__label { font-size: var(--lp-text-xs); color: var(--lp-text-tertiary); }
.field__note { font-size: var(--lp-text-2xs); color: var(--lp-text-tertiary); }
.field__dirty { font-size: var(--lp-text-2xs); color: var(--lp-warning); }
.field__toggle { align-self: flex-start; }
.field .input, .field .textarea { width: 100%; }

/* base.css .textarea is sized for the app's prompt box, which next to a
   single-line input reads as a big empty hole. In a field grid it takes its
   height from its rows attribute instead, so a label and its pair line up. */
.field .textarea {
  min-height: 0;
  height: auto;
  resize: vertical;
}

/* The prompt field is the exception to the grid: it is the thing being edited,
   not a value beside other values, so it takes the full width under the row of
   short fields and is read in a monospaced face where whitespace carries
   meaning. */
.field--wide { margin-top: var(--lp-space-16); }
.field .textarea--prompt {
  font-family: var(--lp-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: var(--lp-text-sm);
  line-height: 1.6;
}

.engine-mode__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--lp-space-8);
  margin-top: var(--lp-space-16);
}


.save-note {
  font-size: var(--lp-text-base);
  color: var(--lp-text-secondary);
  padding: var(--lp-space-12) var(--lp-space-16);
  border-radius: var(--lp-radius-m);
  border: var(--lp-border-thin) solid var(--lp-stroke-default);
  background: var(--lp-bg-surface);
}

/* The save bar sticks to the bottom: on a long form the button must never be
   somewhere the operator has to go hunting for. */
.save-bar {
  position: sticky;
  bottom: 0;
  display: flex;
  align-items: center;
  gap: var(--lp-space-12);
  padding: var(--lp-space-12) var(--lp-space-16);
  border-radius: var(--lp-radius-m);
  border: var(--lp-border-thin) solid var(--lp-stroke-default);
  background: var(--lp-bg-surface);
  box-shadow: var(--lp-shadow-m);
}
.save-bar__status { flex: 1; font-size: var(--lp-text-base); color: var(--lp-success); }
.save-bar__status--error { color: var(--lp-error); }
.save-bar__status--quiet { color: var(--lp-text-tertiary); }

/* Sub-screens inside a surface: a back link plus the screen's own title. */
.subnav {
  display: flex; align-items: center; gap: var(--lp-space-8);
  font-size: var(--lp-text-base); color: var(--lp-text-tertiary);
}
.subnav__back {
  appearance: none; border: none; background: transparent; cursor: pointer;
  font: inherit; color: var(--lp-text-secondary); padding: 0;
}
.subnav__back:hover { color: var(--lp-text-primary); }
.subnav__back:focus-visible { outline: none; box-shadow: var(--lp-focus-ring); }

/* ------------------------------------------------------------------ hội thoại
   One message block per chat row. The body wraps and is never clipped: the
   column is longText, so any cut-off text in the old admin was the display
   losing the data, not the database. Long words and pasted URLs break rather
   than push the modal sideways. */
.chat-msg {
  border: var(--lp-border-thin) solid var(--lp-stroke-default);
  border-radius: var(--lp-radius-m);
  padding: var(--lp-space-12);
  margin-bottom: var(--lp-space-12);
  background: var(--lp-bg-surface);
}
/* Ai đang nói, đọc được bằng HÌNH chứ không chỉ bằng nhãn nhỏ. An outside reader
   (2026-09-03) lost track of the speaker in a long thread: both sides shared one
   frame, one alignment and nearly one background. The customer's turn now sits
   left with a solid accent edge; the product's answer is inset and washed. */
.chat-msg--khach {
  border-left: 3px solid var(--lp-accent-base);
}
.chat-msg--ai {
  background: var(--lp-wash-subtle);
  margin-left: var(--lp-space-16);
}
/* Câu trả lời dài cần cấp độ: an all-caps line on its own IS a heading in these
   answers, so give it weight instead of leaving a 13px slab. */
.chat-msg--ai .chat-msg__body { color: var(--lp-text-secondary); }
.chat-msg--khach .chat-msg__body { color: var(--lp-text-primary); font-weight: var(--lp-font-medium); }
.chat-msg__head {
  display: flex;
  align-items: center;
  gap: var(--lp-space-8);
  margin-bottom: var(--lp-space-8);
  flex-wrap: wrap;
}
.chat-msg__body {
  font-size: var(--lp-text-base);
  color: var(--lp-text-primary);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  line-height: 1.55;
}
.chat-msg__prompt { margin-top: var(--lp-space-8); }
.chat-msg__prompt summary {
  cursor: pointer;
  font-size: var(--lp-text-xs);
  color: var(--lp-text-tertiary);
}

/* Xem thử nội dung thư. sandbox="" with no allow-* tokens: the preview runs no
   script and can reach nothing, so pasted email HTML cannot act on the panel. */
.html-preview {
  width: 100%;
  min-height: 220px;
  border: var(--lp-border-thin) solid var(--lp-stroke-default);
  border-radius: var(--lp-radius-m);
  background: #fff;
}

/* ------------------------------------------------------------- drill stack
   Depth: a row opens the thing behind it, and that thing opens the next, with
   a way back. base.css lays .modal__header out as a space-between row of two,
   so a third child needs the title to hold the free space or the close button
   drifts off the right edge. */
.modal__header .modal__title { margin-right: auto; }
.modal__back {
  appearance: none; border: none; background: transparent; cursor: pointer;
  font: inherit;
  font-size: var(--lp-text-sm);
  color: var(--lp-text-secondary);
  padding: 0;
  max-width: 40%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.modal__back:hover { color: var(--lp-text-primary); }
.modal__back:focus-visible { outline: none; box-shadow: var(--lp-focus-ring); }

/* A block inside a detail view that carries another list you can walk into.
   Same shape as .affiliate-panel but nested, so it takes the softer ground. */
.drill-block {
  border: var(--lp-border-thin) solid var(--lp-stroke-default);
  border-radius: var(--lp-radius-m);
  padding: var(--lp-space-12);
  background: var(--lp-wash-subtle);
}
.drill-block__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--lp-space-8);
  margin-bottom: var(--lp-space-8);
  flex-wrap: wrap;}

/* ------------------------------------------------- bảng trên màn hẹp
   A 640px table inside a 364px phone hides two columns off-screen with no honest
   sign they exist, and reading one row means dragging sideways while the name
   scrolls away (measured 2026-09-03 at 390x844 by an outside reader: 276px of the
   table was unreachable). Below the table's own width the rows become cards: every
   value stays on screen and carries its column name, so nothing has to be held in
   the head while dragging. */
@media (max-width: 700px) {
  .panel-table { min-width: 0; }
  .panel-table thead { display: none; }
  .panel-table, .panel-table tbody, .panel-table tr, .panel-table td { display: block; width: auto; }
  .panel-table tr {
    border: var(--lp-border-thin) solid var(--lp-stroke-default);
    border-radius: var(--lp-radius-m);
    padding: var(--lp-space-12);
    margin-bottom: var(--lp-space-8);
  }
  .panel-table td {
    display: flex;
    gap: var(--lp-space-8);
    justify-content: space-between;
    align-items: baseline;
    border: none;
    padding: 2px 0;
    text-align: left;
  }
  /* The column name comes back as the label of its own line. */
  .panel-table td[data-label]::before {
    content: attr(data-label);
    flex: none;
    color: var(--lp-text-tertiary);
    font-size: var(--lp-text-xs);
  }
  /* The first cell is the row's subject: no label, full width, and it reads first. */
  .panel-table td:first-child { display: block; }
  .panel-table td:first-child::before { content: none; }
  .panel-table td.num { justify-content: space-between; }
}

/* Chân hội thoại: the line that says the conversation is over. It is a full-width
   marker rather than a note, because the thing it fixes is a reader believing a
   scroll box that simply stopped. */
.thread-end {
  margin-top: var(--lp-space-12);
  padding-top: var(--lp-space-8);
  border-top: var(--lp-border-thin) dashed var(--lp-stroke-default);
  text-align: center;
  font-size: var(--lp-text-xs);
  color: var(--lp-text-tertiary);
}

/* Dấu mở hàng. A whole row being clickable with no arrow, no button and no link
   styling reads as a static table (outside reader, 2026-09-03), so the last cell
   carries the same chevron the drill-in buttons use. */
.row-open { color: var(--lp-text-tertiary); margin-left: var(--lp-space-8); }
.panel-table tbody tr:hover .row-open { color: var(--lp-text-primary); }

/* --- workspace switch (one portal, two backends) ----------------------- */
/* The product panel (app.lazyprompter.com/admin/panel) and the website panel
   (lazyprompter.com/admin/panel) are two apps that must read as one place. The
   switch sits under the brand on both, same order, same labels; the current one
   is the highlighted pill, the other is a plain link (separate login until the
   handoff token lands; see kenz-runtime-preview ARC-LP-ADMIN, 06/09). */
.ws-switch { display: flex; gap: 4px; padding: 0 var(--lp-space-12) var(--lp-space-8); }
.ws-switch__pill {
  flex: 1; text-align: center; padding: 6px 8px; border-radius: var(--lp-radius-full);
  font-size: var(--lp-text-sm); color: var(--lp-text-secondary); text-decoration: none;
  border: 1px solid var(--lp-stroke-default); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ws-switch__pill:hover { color: var(--lp-text-primary); }
.ws-switch__pill--current { background: var(--lp-accent-base); color: var(--lp-text-on-accent); border-color: var(--lp-accent-base); }
.sidebar--collapsed .ws-switch { display: none; }
