/* Broadcast-specific components.
 *
 * brand.css stays portable to the next THC app; anything only Broadcast needs
 * lives here. Same rules apply: plain CSS, tokens from brand.css, no inline
 * styles in templates, computed contrast (nothing here introduces new ink -
 * every colour is a brand.css token).
 */

/* ---------- Panels ----------
   A titled content surface. brand.css's .card is an image thumbnail; this is
   the app's write-surface: forms, summaries, row tables. */

.panel {
  background: var(--brand-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-6);
  margin-bottom: var(--space-4);
  max-width: 32rem;
}

/* Panels holding a table or the composer need the full column width. */
.panel-wide {
  max-width: none;
}

.panel > h2:first-child,
.panel > h3:first-child {
  margin-top: 0;
}

/* ---------- Forms ---------- */

/* Label stacked over its control, the shape every form in the app uses. */
.field {
  display: block;
  margin-bottom: var(--space-4);
}

.field > span {
  display: block;
  font-weight: 500;
  font-size: 0.9rem;
  margin-bottom: var(--space-1);
}

.field input[type="text"],
.field input[type="email"],
.field input[type="search"],
.field input[type="url"],
.field select,
.field textarea {
  width: 100%;
}

/* Narrow numeric inputs (row numbers, counts) should not span the panel. */
.field .narrow {
  width: 6rem;
}

/* A free-standing input outside a .field that still needs room to type in -
   exclusion reasons, recipient search. Capped so it never overflows a panel. */
.input-wide {
  width: 24rem;
  max-width: 100%;
}

/* A fieldset used purely to disable a group of controls at once (read-only
   campaigns), not to draw a box - the UA border/padding would look like a
   panel inside a panel. */
.bare-fieldset {
  border: 0;
  margin: 0;
  padding: 0;
}

/* ---------- Tables ---------- */

/* Wide tables (mapping preview, recipient rows) scroll inside their panel
   rather than widening the page. */
.table-scroll {
  overflow-x: auto;
}

/* ---------- Identity editor ---------- */

/* The signature is edited in the composer island; give it the panel width
   and a working height without the campaign page's density tweaks. */
.identity-edit {
  max-width: 60rem;
}

.identity-edit #signature-mount .thc-composer__body .ProseMirror {
  min-height: 8rem;
}

/* ---------- Compose screen ---------- */

/* Editor column plus the right-hand inspector (UX-01). Collapses to one
   column below the composer's supported width (CMP-21 is desktop-first). */
.compose-layout {
  display: flex;
  gap: var(--space-6);
  align-items: flex-start;
  flex-wrap: wrap;
}

/* Two thirds editor, one third inspector (owner, 2026-08-25). Both columns
   grow from zero so the ratio holds at any width; the min-widths keep the
   composer above its supported width and the preview frame usable before
   the row wraps to one column. */
.compose-main {
  flex: 2 1 0;
  min-width: 40rem;
}

.compose-inspector {
  flex: 1 1 0;
  min-width: 22rem;
}

/* Preview frame: 600px is the shell's content width (RND-03); the narrow
   variant is the mobile check. */
.preview-frame {
  width: 100%;
  height: 30rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--brand-surface);
}

.preview-frame.narrow {
  width: 360px;
}

.preview-frame.tall {
  height: 26rem;
}

/* Sandboxed shell/template previews sit on white regardless of page tint. */
.embed-frame {
  width: 100%;
  height: 15rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--brand-surface);
}

.embed-frame.tall {
  height: 25rem;
}

/* An embedded email preview stops at the shell's own content width (600px
   plus its border) rather than stretching past the message it is showing. */
.embed-frame.email {
  max-width: 620px;
}

/* Source editors (shell HTML, plain-text override) want a monospace face and
   room to work. */
.code-input {
  width: 100%;
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Consolas, monospace;
  font-size: 0.85rem;
}

/* The archived plain-text part, shown exactly as sent: preserve its wrapping
   without letting a long footnoted URL widen the page. */
.archive-text {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  margin: 0;
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Consolas, monospace;
  font-size: 0.85rem;
  color: var(--text-primary);
}

/* ---------- Small helpers ---------- */

/* A status line that updates in place (autosave indicator). */
.status-note {
  font-weight: 400;
  font-size: 0.85rem;
  color: var(--text-muted);
  margin-left: var(--space-2);
}

/* Remove the trailing margin where a panel ends with a paragraph. */
.tight {
  margin-bottom: 0;
}

/* Spaced block between panel sections. */
.section {
  margin: var(--space-4) 0;
}

/* ---------- CRM cohort picker (Zoho definition wizard) ----------
 * A Therapists module exposes 50 picklist fields and 1,000+ options. The
 * handful that choose a cohort are shown in full, every value visible, in a
 * responsive grid; the rest sit behind a search box and only appear when
 * they match (or already carry a selection). */

.field-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-4);
  align-items: start;
}

.field-card {
  background: var(--brand-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3);
}

/* [hidden] must beat the grid item's own display when the search filters. */
.field-card[hidden] {
  display: none;
}

.field-card .field-title {
  font-weight: 600;
  margin: 0 0 var(--space-2);
}

.field-card .field-title .count {
  color: var(--text-muted);
  font-weight: 400;
}

.field-card .option {
  display: flex;
  gap: var(--space-1);
  align-items: baseline;
  margin-bottom: var(--space-1);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* Preview tiles are buttons: clicking a number lists who is behind it. */
.stat-tile-button {
  display: block;
  width: 100%;
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
}

.stat-tile-button:hover,
.stat-tile-button:focus-visible {
  border-color: var(--brand-primary, currentColor);
}

.stat-tile-button.is-active {
  box-shadow: inset 0 0 0 2px var(--brand-primary, currentColor);
}

.stat-tile-button .value {
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
}

/* Modal listing: a native <dialog> in the top layer, so a long table is
 * never clipped by the panel that asked for it. */
.modal {
  width: min(96vw, 1200px);
  max-height: 90vh;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--brand-surface);
  color: inherit;
  display: flex;
  flex-direction: column;
}

.modal:not([open]) {
  display: none;
}

.modal::backdrop {
  background: rgba(0, 0, 0, 0.45);
}

.modal-head {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  column-gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
}

.modal-head h3 {
  margin: 0;
}

.modal-head .meta {
  grid-column: 1;
}

.modal-close {
  grid-column: 2;
  grid-row: 1 / span 2;
}

.modal-body {
  overflow: auto;
  padding: var(--space-2) var(--space-4) var(--space-4);
}

.modal-body table {
  width: 100%;
}

.modal-body thead th {
  position: sticky;
  top: 0;
  background: var(--brand-surface);
}

.table-scroll {
  overflow-x: auto;
}

/* Busy indicator for the live-CRM preview: an indeterminate bar plus an
 * elapsed counter, so a 10-second round trip reads as work, not a freeze. */
.progress {
  margin-top: var(--space-3);
}

.progress-bar {
  position: relative;
  height: 6px;
  border-radius: 3px;
  background: var(--border);
  overflow: hidden;
}

.progress-bar::after {
  content: "";
  position: absolute;
  inset: 0;
  width: 35%;
  border-radius: 3px;
  background: var(--brand-primary, currentColor);
  animation: progress-slide 1.4s ease-in-out infinite;
}

@keyframes progress-slide {
  from { transform: translateX(-100%); }
  to { transform: translateX(300%); }
}

@media (prefers-reduced-motion: reduce) {
  .progress-bar::after { animation: none; width: 100%; opacity: 0.6; }
}

/* Field / operator / value, side by side; stacks on narrow screens. */
.condition-row {
  display: grid;
  grid-template-columns: minmax(150px, 2fr) minmax(130px, 1fr) minmax(150px, 2fr);
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

@media (max-width: 40rem) {
  .condition-row {
    grid-template-columns: 1fr;
  }
}

/* ---------- CRM cohort builder, "Option C" layout trial ----------
 * A second presentation of the same wizard (templates/lists/zoho_import_c.html,
 * /lists/zoho/new/c). Every picklist field is a pill in one bar rather than a
 * card in a grid; the open field's values fill a side pane. Tokens only -
 * nothing here introduces new ink, and purple stays selection chrome. */

.page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-8);
  margin-bottom: var(--space-4);
}

.page-head-main {
  flex: 1 1 32rem;
  min-width: 0;
}

.page-head-main h2 {
  margin: 0;
}

/* The three live counts, compact and horizontal so they sit beside the title
   instead of below the form. Each cell is still the button that lists the rows
   behind its number (the .stat-tile-button contract, in a denser shape). */
.count-strip {
  flex: none;
  display: flex;
  align-items: stretch;
  background: var(--brand-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.count-cell {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-3) var(--space-6);
  margin: 0;
  border: 0;
  border-left: 1px solid var(--border);
  border-radius: 0;
  background: var(--brand-surface);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.count-cell:first-child {
  border-left: 0;
}

.count-cell .label {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--th-text);
}

.count-cell .value {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
  font-feature-settings:
    "lnum" 1,
    "tnum" 1;
}

.count-cell:hover,
.count-cell:focus-visible {
  background: var(--brand-bg);
  filter: none;
}

.count-cell.is-active {
  box-shadow: inset 0 0 0 2px var(--brand-primary);
}

/* Before the first preview the cells are placeholders, not controls: no
   not-allowed cursor, and the underline that promises a row listing comes off
   the "-" so nothing invites a click that would do nothing. */
.count-cell:disabled {
  cursor: default;
  opacity: 1;
}

.count-cell:disabled .value {
  color: var(--text-muted);
  text-decoration: none;
}

/* The trailing "N in module / live at" cell is reference text, not a button. */
.count-cell.count-note {
  justify-content: center;
  background: var(--brand-bg);
  cursor: default;
  font-size: 0.78rem;
  line-height: 1.35;
}

/* The never-send rule, as a single line with its icon rather than a block. */
.warning-inline {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  font-size: 0.9rem;
}

.warning-inline svg {
  flex: none;
  position: relative;
  top: 3px;
}

/* ---------- The field chip bar ---------- */

.chip-bar-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-bottom: var(--space-3);
}

.chip-bar-head h3 {
  margin: 0;
}

.chip-search {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 22rem;
  max-width: 100%;
  margin: 0;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--brand-surface);
  color: var(--text-muted);
}

.chip-search input,
.pane-search input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  padding: 0;
  background: transparent;
}

.chip-search input:focus-visible,
.pane-search input:focus-visible {
  outline: none;
}

.chip-search:focus-within,
.pane-search:focus-within {
  outline: 2px solid var(--brand-primary);
  outline-offset: 2px;
}

.chip-bar {
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--border);
}

.pill[data-field-chip] {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.pill[data-field-chip][hidden] {
  display: none;
}

/* A field nobody has touched sits quieter than the eight that actually choose
   a cohort - the same ranking the grid layout draws with its "search all
   fields" split, without hiding anything. */
.pill.pill-quiet {
  color: var(--text-muted);
}

.pill.pill-quiet[aria-selected="true"] {
  color: var(--on-primary);
}

/* A field carrying a selection keeps its count badge in the bar, so a cohort
   is legible without opening anything. */
.pill[data-has-selection="true"] {
  border-color: var(--info);
}

.pill[data-field-chip] .badge[hidden] {
  display: none;
}

.pill[aria-selected="true"] .badge {
  background: var(--brand-surface);
  color: var(--brand-primary-dark);
}

/* ---------- Builder body: summary + conditions beside the open field ------ */

/* Values on the left, under the chip bar that opens them; the running cohort
   and the specific filters on the right. `align-items: start` keeps the
   right-hand column's heading level with the top of the values pane, so
   "Clear all" reads as sitting directly beside the field list. */
.builder-split {
  display: grid;
  grid-template-columns: 25rem minmax(0, 1fr);
  gap: var(--space-8);
  align-items: start;
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--border);
}

@media (max-width: 60rem) {
  .builder-split {
    grid-template-columns: 1fr;
  }
}

.builder-main h4:first-child {
  margin-top: 0;
}

/* Specific filters, one row at a time. A fourth, narrow column holds each
   extra row's remove button; the first row leaves it empty so all three stay
   aligned. Declared after the base .condition-row rules (equal specificity,
   later wins), including its own narrow-screen collapse. */
.condition-row-managed {
  grid-template-columns: minmax(150px, 2fr) minmax(130px, 1fr) minmax(150px, 2fr) 2rem;
  align-items: center;
}

.condition-row-managed[hidden] {
  display: none;
}

@media (max-width: 40rem) {
  .condition-row-managed {
    grid-template-columns: 1fr;
  }
}

/* A square, quiet control that belongs to its row rather than to the page. */
.row-button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--brand-surface);
  color: var(--text-muted);
  cursor: pointer;
}

.row-button:hover {
  border-color: var(--brand-primary);
  color: var(--text-primary);
  filter: none;
}

.condition-add {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 5px 14px;
  font-size: 0.85rem;
}

.condition-add[hidden] {
  display: none;
}

.cohort-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}

.cohort-head h4 {
  margin: 0;
}

/* Quieter than the panel's real actions — this undoes work, it does not
   advance it, and it only appears once there is something to undo. */
.cohort-clear {
  flex: none;
  padding: 5px 14px;
  font-size: 0.85rem;
}

.cohort-clear[hidden] {
  display: none;
}

.cohort-summary {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-3) 0 var(--space-4);
}

.cohort-summary:empty {
  display: none;
}

/* Each ticked value, removable without opening its field. */
.summary-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 4px 6px 4px 11px;
}

.summary-chip button {
  display: flex;
  align-items: center;
  padding: 2px;
  margin: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: inherit;
  cursor: pointer;
}

.summary-chip button:hover {
  background: var(--brand-surface);
  filter: none;
}

/* ---------- The open field's values ---------- */

.builder-pane {
  position: sticky;
  top: var(--space-4);
}

.field-pane {
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--brand-surface);
  overflow: hidden;
}

.field-pane[hidden] {
  display: none;
}

.field-pane-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-3);
  background: var(--brand-bg);
  border-bottom: 1px solid var(--border);
}

.field-pane-title {
  font-weight: 600;
}

.field-pane-body {
  padding: var(--space-3);
}

.pane-search {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 var(--space-3);
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
}

/* A FIXED height, not a max: with max-height the pane grew and shrank as you
   moved between a 3-value field and a 60-value one, and the whole right-hand
   column jumped. Constant height means a long list scrolls and a short one
   leaves white space, and nothing below the pane ever moves. */
.pane-options {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  height: 22rem;
  overflow-y: auto;
}

.pane-options .option {
  display: flex;
  gap: var(--space-2);
  align-items: baseline;
  margin: 0;
  font-weight: 400;
  font-size: 0.9rem;
}

.pane-options .option[hidden] {
  display: none;
}

.pane-empty {
  margin: 0;
  padding: var(--space-6);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
  text-align: center;
}

.pane-empty[hidden] {
  display: none;
}

/* ---------- Name and actions on one line ---------- */

.builder-foot {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-6);
  padding-top: var(--space-4);
}

.field-inline {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex: 1 1 28rem;
  margin: 0;
}

.field-inline > span {
  white-space: nowrap;
}

.field-inline input {
  flex: 1 1 auto;
  min-width: 0;
}

.builder-actions {
  flex: none;
  margin: 0;
  margin-left: auto;
}

/* ---------- Merge-field picker for plain inputs (compose subject) ----------
 * The body composer's picker lives inside the island; the subject is a plain
 * Django input, so it gets the same affordance in app.js's delegated style.
 * The trigger wears the chip it inserts, explicit fg AND bg (the island
 * learnt that lesson on 2026-08-24). */

.merge-row {
  position: relative;
  display: flex;
  gap: var(--space-2);
  align-items: center;
}

.merge-row input {
  flex: 1 1 auto;
  min-width: 0;
}

.merge-open {
  flex: none;
  background: var(--brand-primary-dark);
  color: var(--on-primary);
  border-radius: var(--radius-pill);
  padding: 3px 10px;
  font-size: 0.8rem;
  line-height: 1.3;
}

.merge-menu {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 50;
  min-width: 14rem;
  max-height: 20rem;
  overflow-y: auto;
  margin-top: 4px;
  padding: 4px;
  background: var(--brand-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 4px 16px rgba(8, 16, 64, 0.14);
}

.merge-menu[hidden] {
  display: none;
}

.merge-menu button {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  padding: 6px 10px;
  font-size: 0.9rem;
}

.merge-menu button:hover,
.merge-menu button:focus-visible {
  background: var(--brand-bg);
  filter: none;
}

/* ---------- Compose density (owner, 2026-08-24) ----------
 * The whole working editor fits a 1920x1080 viewport without vertical
 * scrolling. Occasional-use inspector tools (test send, templates, versions,
 * plain-text override) collapse to a summary line; the preview keeps a
 * working height. (Settings fields were briefly two per row - reverted
 * 2026-08-25, see below.) */

/* Settings fields stack in one column (owner, 2026-08-25: the two-per-row
   grid read as fields scattered to the right); only the vertical margins
   stay tight. */
.compose-main .bare-fieldset .field {
  margin-bottom: var(--space-2);
  min-width: 0;
}

.field-pair > div,
.field-pair > .field {
  min-width: 0;
}

/* A campaign past draft is read-only: the whole editor panel goes off-white
   so it stops looking like a form waiting to be filled in. */
.compose-main .panel-readonly {
  background: var(--brand-bg);
}

.compose-main .panel-readonly input,
.compose-main .panel-readonly select,
.compose-main .panel-readonly textarea {
  background: var(--neutral-bg);
  color: var(--text-muted);
}

/* The composer island paints its own surface; tint it (and the signature
   label that masks the divider) to match the read-only controls. */
.compose-main .panel-readonly .thc-composer,
.compose-main .panel-readonly .thc-composer__signature::before {
  background: var(--neutral-bg);
}

.compose-inspector .panel {
  padding: var(--space-4);
  margin-bottom: var(--space-3);
  max-width: none;  /* fill the column; the generic .panel cap is for forms */
}

.compose-inspector .preview-frame {
  height: 20rem;
}

/* A collapsed tool reads as one line; open, it is the panel it always was. */
details.panel-compact > summary {
  cursor: pointer;
  font-size: 0.95rem;
}

details.panel-compact[open] > summary {
  margin-bottom: var(--space-3);
}

/* Page-scoped shaves so the compose page alone tightens (Chrome/Edge both
   support :has; the selector simply never matches elsewhere). Together with
   the collapses above these bring a draft compose page under a 1080px
   viewport. */
body:has(.compose-layout) nav {
  margin-bottom: var(--space-3);
}

body:has(.compose-layout) h2:first-of-type {
  margin: 0 0 var(--space-2);
  font-size: 1.15rem;
}

body:has(.compose-layout) {
  padding-top: var(--space-3);
  padding-bottom: var(--space-3);
}

.compose-main > .panel {
  padding: var(--space-4);
  margin-bottom: var(--space-2);
}

.compose-main .section {
  margin: var(--space-2) 0;
}

.compose-main .action-row {
  margin: var(--space-2) 0;
}
